add skeleton and boundaries

This commit is contained in:
2025-10-23 19:15:40 +02:00
parent fb1eaa99ed
commit 3d20e876c3
13 changed files with 219 additions and 15 deletions

10
pom.xml
View File

@@ -25,6 +25,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -37,7 +45,7 @@
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-common</artifactId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.13</version>
</dependency>
<dependency>

View File

@@ -2,7 +2,9 @@ package com.spijkerman.ivo.threekidfamily;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
@SpringBootApplication
public class ThreeKidFamilyApplication {

View File

@@ -0,0 +1,4 @@
package com.spijkerman.ivo.threekidfamily.domain.person;
public record Person() {
}

View File

@@ -0,0 +1,38 @@
package com.spijkerman.ivo.threekidfamily.domain.person;
import com.spijkerman.ivo.threekidfamily.domain.person.dto.PersonUpsertRequest;
import com.spijkerman.ivo.threekidfamily.domain.person.dto.PersonUpsertResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/people")
@RequiredArgsConstructor
public class PersonController {
private final PersonService personService;
@PostMapping
public ResponseEntity<List<PersonUpsertResponse>> upsertPerson(
@RequestBody PersonUpsertRequest request
) {
throw new UnsupportedOperationException();
}
@GetMapping("/{id}")
public ResponseEntity<?> getPerson(
@PathVariable int id
) {
throw new UnsupportedOperationException();
}
@DeleteMapping
public ResponseEntity<?> deletePeople(
@RequestBody List<Integer> ids
) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,45 @@
package com.spijkerman.ivo.threekidfamily.domain.person;
import jakarta.annotation.Nullable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import lombok.*;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PersonEntity {
@Id
@NonNull
private Integer id;
@Nullable
private String name;
@Nullable
private LocalDate birthDate;
// Store only relation IDs instead of JPA mappings to reduce JPA/Hibernate tomfoolery
@Nullable
private Integer partnerId;
@NonNull
@Singular
@ElementCollection(fetch = FetchType.EAGER)
private Set<Integer> childIds = new HashSet<>();
@NonNull
@Singular
@ElementCollection(fetch = FetchType.EAGER)
private Set<Integer> parentIds = new HashSet<>();
}

View File

@@ -0,0 +1,8 @@
package com.spijkerman.ivo.threekidfamily.domain.person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PersonRepository extends JpaRepository<PersonEntity, Integer> {
}

View File

@@ -0,0 +1,44 @@
package com.spijkerman.ivo.threekidfamily.domain.person;
import lombok.Locked;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class PersonService {
private final PersonRepository personRepository;
/**
* Upserts a person to storage, overwriting any existing or conflicting data.
*
* @param person The Person data to be persisted
* @return The IDs of the Persons that have been modified because of this operation.
*/
public @NonNull Set<Integer> upsertPerson(Person person) {
throw new UnsupportedOperationException();
}
/**
* Returns all known data related to that specific user.
*
* @param id The ID of the person to be retrieved.
* @return A Person object, may contain all information, or just the ID.
*/
public Person getPersonById(int id) {
throw new UnsupportedOperationException();
}
/**
* Deletes all data related to the specified user, and blacklists that ID from later use.
* @param id The ID of the person to be deleted and blacklisted.
*/
public void deletePersonById(int id) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,4 @@
package com.spijkerman.ivo.threekidfamily.domain.person.dto;
public record PersonUpsertRequest() {
}

View File

@@ -0,0 +1,4 @@
package com.spijkerman.ivo.threekidfamily.domain.person.dto;
public record PersonUpsertResponse() {
}

View File

@@ -1 +1,5 @@
spring.application.name: threekidfamily
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password: password

View File

@@ -1,13 +0,0 @@
package com.spijkerman.ivo.threekidfamily;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ThreeKidFamilyApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -0,0 +1,53 @@
package com.spijkerman.ivo.threekidfamily;
import com.spijkerman.ivo.threekidfamily.domain.person.Person;
import com.spijkerman.ivo.threekidfamily.domain.person.PersonEntity;
import com.spijkerman.ivo.threekidfamily.domain.person.PersonRepository;
import com.spijkerman.ivo.threekidfamily.domain.person.PersonService;
import lombok.val;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ThreeKidFamilyIT {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private PersonRepository personRepository;
@Test
void contextLoads() {
}
@Test
void gibberishNotFound() {
val response = restTemplate.exchange("/gibberish", HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Test
void swaggerUiLoads() {
val response = restTemplate.exchange("/swagger-ui.html", HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void testDbRoundTrip() {
val given = PersonEntity.builder().id(12).build();
val saved = personRepository.save(given);
assertThat(saved).isEqualTo(given);
val retrieved = personRepository.findById(12);
assertThat(retrieved).isPresent().hasValue(given);
}
}

View File

@@ -0,0 +1,3 @@
spring:
jpa:
show-sql: true