implements HsOfficePersonController

This commit is contained in:
Michael Hoennig 2022-09-21 09:44:09 +02:00
parent 0bab27d723
commit 00174e4c4a
9 changed files with 895 additions and 17 deletions

View File

@ -0,0 +1,118 @@
package net.hostsharing.hsadminng.hs.office.person;
import net.hostsharing.hsadminng.Mapper;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntityPatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import java.util.List;
import java.util.UUID;
import static net.hostsharing.hsadminng.Mapper.map;
@RestController
public class HsOfficePersonController implements HsOfficePersonsApi {
@Autowired
private Context context;
@Autowired
private HsOfficePersonRepository personRepo;
@Override
@Transactional(readOnly = true)
public ResponseEntity<List<HsOfficePersonResource>> listPersons(
final String currentUser,
final String assumedRoles,
final String label) {
context.define(currentUser, assumedRoles);
final var entities = personRepo.findPersonByOptionalNameLike(label);
final var resources = Mapper.mapList(entities, HsOfficePersonResource.class);
return ResponseEntity.ok(resources);
}
@Override
@Transactional
public ResponseEntity<HsOfficePersonResource> addPerson(
final String currentUser,
final String assumedRoles,
final HsOfficePersonInsertResource body) {
context.define(currentUser, assumedRoles);
final var entityToSave = map(body, HsOfficePersonEntity.class);
entityToSave.setUuid(UUID.randomUUID());
final var saved = personRepo.save(entityToSave);
final var uri =
MvcUriComponentsBuilder.fromController(getClass())
.path("/api/hs/office/persons/{id}")
.buildAndExpand(entityToSave.getUuid())
.toUri();
final var mapped = map(saved, HsOfficePersonResource.class);
return ResponseEntity.created(uri).body(mapped);
}
@Override
@Transactional(readOnly = true)
public ResponseEntity<HsOfficePersonResource> getPersonByUuid(
final String currentUser,
final String assumedRoles,
final UUID personUuid) {
context.define(currentUser, assumedRoles);
final var result = personRepo.findByUuid(personUuid);
if (result.isEmpty()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(map(result.get(), HsOfficePersonResource.class));
}
@Override
@Transactional
public ResponseEntity<Void> deletePersonByUuid(
final String currentUser,
final String assumedRoles,
final UUID personUuid) {
context.define(currentUser, assumedRoles);
final var result = personRepo.deleteByUuid(personUuid);
if (result == 0) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
@Override
@Transactional
public ResponseEntity<HsOfficePersonResource> patchPerson(
final String currentUser,
final String assumedRoles,
final UUID personUuid,
final HsOfficePersonPatchResource body) {
context.define(currentUser, assumedRoles);
final var current = personRepo.findByUuid(personUuid).orElseThrow();
new HsOfficePersonEntityPatch(current).apply(body);
final var saved = personRepo.save(current);
final var mapped = map(saved, HsOfficePersonResource.class);
return ResponseEntity.ok(mapped);
}
}

View File

@ -0,0 +1,26 @@
package net.hostsharing.hsadminng.hs.office.person;
import net.hostsharing.hsadminng.OptionalFromJson;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonTypeResource;
import java.util.Optional;
class HsOfficePersonEntityPatch {
private final HsOfficePersonEntity entity;
HsOfficePersonEntityPatch(final HsOfficePersonEntity entity) {
this.entity = entity;
}
void apply(final HsOfficePersonPatchResource resource) {
Optional.ofNullable(resource.getPersonType())
.map(HsOfficePersonTypeResource::getValue)
.map(HsOfficePersonType::valueOf)
.ifPresent(entity::setPersonType);
OptionalFromJson.of(resource.getTradeName()).ifPresent(entity::setTradeName);
OptionalFromJson.of(resource.getFamilyName()).ifPresent(entity::setFamilyName);
OptionalFromJson.of(resource.getGivenName()).ifPresent(entity::setGivenName);
}
}

View File

@ -16,3 +16,5 @@ map:
null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/contacts/{contactUUID}:
null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/persons/{personUUID}:
null: org.openapitools.jackson.nullable.JsonNullable

View File

@ -3,16 +3,28 @@ components:
schemas:
HsOfficePersonBase:
HsOfficePersonTypeValues:
- NATURAL # a human
- LEGAL # e.g. Corp., Inc., AG, GmbH, eG
- SOLE_REPRESENTATION # e.g. OHG, GbR
- JOINT_REPRESENTATION # e.g. community of heirs
HsOfficePersonType:
type: string
enum:
- NATURAL # a human
- LEGAL # e.g. Corp., Inc., AG, GmbH, eG
- SOLE_REPRESENTATION # e.g. OHG, GbR
- JOINT_REPRESENTATION # e.g. community of heirs
HsOfficePerson:
type: object
properties:
personType:
uuid:
type: string
enum:
- NATURAL # a human
- LEGAL # e.g. Corp., Inc., AG, GmbH, eG
- SOLE_REPRESENTATION # e.g. OHG, GbR
- JOINT_REPRESENTATION # e.g. community of heirs
format: uuid
personType:
$ref: '#/components/schemas/HsOfficePersonType'
tradeName:
type: string
givenName:
@ -20,14 +32,32 @@ components:
familyName:
type: string
HsOfficePerson:
allOf:
- type: object
properties:
uuid:
type: string
format: uuid
- $ref: '#/components/schemas/HsOfficePersonBase'
HsOfficePersonInsert:
type: object
properties:
personType:
$ref: '#/components/schemas/HsOfficePersonType'
tradeName:
type: string
givenName:
type: string
familyName:
type: string
required:
- personType
HsOfficePersonUpdate:
$ref: '#/components/schemas/HsOfficePersonBase'
HsOfficePersonPatch:
type: object
properties:
personType:
nullable: true
$ref: '#/components/schemas/HsOfficePersonType'
tradeName:
type: string
nullable: true
givenName:
type: string
nullable: true
familyName:
type: string
nullable: true

View File

@ -0,0 +1,83 @@
get:
tags:
- hs-office-persons
description: 'Fetch a single business person by its uuid, if visible for the current subject.'
operationId: getPersonByUuid
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: personUUID
in: path
required: true
schema:
type: string
format: uuid
description: UUID of the person to fetch.
responses:
"200":
description: OK
content:
'application/json':
schema:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
patch:
tags:
- hs-office-persons
description: 'Updates a single person by its uuid, if permitted for the current subject.'
operationId: patchPerson
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: personUUID
in: path
required: true
schema:
type: string
format: uuid
requestBody:
content:
'application/json':
schema:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePersonPatch'
responses:
"200":
description: OK
content:
'application/json':
schema:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
delete:
tags:
- hs-office-persons
description: 'Delete a single business person by its uuid, if permitted for the current subject.'
operationId: deletePersonByUuid
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: personUUID
in: path
required: true
schema:
type: string
format: uuid
description: UUID of the person to delete.
responses:
"204":
description: No Content
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
"404":
$ref: './error-responses.yaml#/components/responses/NotFound'

View File

@ -0,0 +1,56 @@
get:
summary: Returns a list of (optionally filtered) persons.
description: Returns the list of (optionally filtered) persons which are visible to the current user or any of it's assumed roles.
tags:
- hs-office-persons
operationId: listPersons
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- name: name
in: query
required: false
schema:
type: string
description: Prefix of label to filter the results.
responses:
"200":
description: OK
content:
'application/json':
schema:
type: array
items:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
post:
summary: Adds a new person.
tags:
- hs-office-persons
operationId: addPerson
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
requestBody:
content:
'application/json':
schema:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePersonInsert'
required: true
responses:
"201":
description: Created
content:
'application/json':
schema:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
"409":
$ref: './error-responses.yaml#/components/responses/Conflict'

View File

@ -25,3 +25,12 @@ paths:
/api/hs/office/contacts/{contactUUID}:
$ref: "./hs-office-contacts-with-uuid.yaml"
# Persons
/api/hs/office/persons:
$ref: "./hs-office-persons.yaml"
/api/hs/office/persons/{personUUID}:
$ref: "./hs-office-persons-with-uuid.yaml"

View File

@ -0,0 +1,401 @@
package net.hostsharing.hsadminng.hs.office.person;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.Accepts;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.test.JpaAttempt;
import org.apache.commons.lang3.RandomStringUtils;
import org.json.JSONException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
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.server.LocalServerPort;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { HsadminNgApplication.class, JpaAttempt.class }
)
@Transactional
class HsOfficePersonControllerAcceptanceTest {
@LocalServerPort
private Integer port;
@Autowired
Context context;
@Autowired
Context contextMock;
@Autowired
HsOfficePersonRepository personRepo;
@Autowired
JpaAttempt jpaAttempt;
Set<UUID> tempPersonUuids = new HashSet<>();
@Nested
@Accepts({ "Person:F(Find)" })
class ListPersons {
@Test
void globalAdmin_withoutAssumedRoles_canViewAllPersons_ifNoCriteriaGiven() throws JSONException {
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/persons")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"personType": "JOINT_REPRESENTATION",
"tradeName": "Erben Bessler",
"givenName": "Bessler",
"familyName": "Mel"
},
{
"personType": "LEGAL",
"tradeName": "First Impressions GmbH",
"givenName": null,
"familyName": null
},
{
"personType": "SOLE_REPRESENTATION",
"tradeName": "Ostfriesische Kuhhandel OHG",
"givenName": null,
"familyName": null
},
{
"personType": "NATURAL",
"tradeName": null,
"givenName": "Smith",
"familyName": "Peter"
},
{
"personType": "LEGAL",
"tradeName": "Rockshop e.K.",
"givenName": "Miller",
"familyName": "Sandra"
}
]
"""
));
// @formatter:on
}
}
@Nested
@Accepts({ "Person:C(Create)" })
class AddPerson {
@Test
void globalAdmin_withoutAssumedRole_canAddPerson() {
context.define("superuser-alex@hostsharing.net");
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"personType": "NATURAL",
"familyName": "Tester",
"givenName": "Testi"
}
""")
.port(port)
.when()
.post("http://localhost/api/hs/office/persons")
.then().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("personType", is("NATURAL"))
.body("familyName", is("Tester"))
.body("givenName", is("Testi"))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
// finally, the new person can be accessed under the generated UUID
final var newUserUuid = toCleanup(UUID.fromString(
location.substring(location.lastIndexOf('/') + 1)));
assertThat(newUserUuid).isNotNull();
}
}
@Nested
@Accepts({ "Person:R(Read)" })
class GetPerson {
@Test
void globalAdmin_withoutAssumedRole_canGetArbitraryPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPersonUuid = personRepo.findPersonByOptionalNameLike("Erben").get(0).getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/persons/" + givenPersonUuid)
.then().log().body().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
{
"tradeName": "Erben Bessler"
}
""")); // @formatter:on
}
@Test
@Accepts({ "Person:X(Access Control)" })
void normalUser_canNotGetUnrelatedPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPersonUuid = personRepo.findPersonByOptionalNameLike("Erben").get(0).getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port)
.when()
.get("http://localhost/api/hs/office/persons/" + givenPersonUuid)
.then().log().body().assertThat()
.statusCode(404); // @formatter:on
}
@Test
@Accepts({ "Person:X(Access Control)" })
void personOwnerUser_canGetRelatedPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPersonUuid = personRepo.findPersonByOptionalNameLike("Erben").get(0).getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "person-ErbenBesslerMelBessler@example.com")
.port(port)
.when()
.get("http://localhost/api/hs/office/persons/" + givenPersonUuid)
.then().log().body().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
{
"personType": "JOINT_REPRESENTATION",
"tradeName": "Erben Bessler",
"givenName": "Bessler",
"familyName": "Mel"
}
""")); // @formatter:on
}
}
@Nested
@Accepts({ "Person:U(Update)" })
class PatchPerson {
@Test
void globalAdmin_withoutAssumedRole_canPatchAllPropertiesOfArbitraryPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = givenSomeTemporaryPersonCreatedBy("selfregistered-test-user@hostsharing.org");
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"personType": "JOINT_REPRESENTATION",
"tradeName": "Patched Trade Name",
"familyName": "Patched Family Name",
"givenName": "Patched Given Name"
}
""")
.port(port)
.when()
.patch("http://localhost/api/hs/office/persons/" + givenPerson.getUuid())
.then().assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("personType", is("JOINT_REPRESENTATION"))
.body("tradeName", is("Patched Trade Name"))
.body("familyName", is("Patched Family Name"))
.body("givenName", is("Patched Given Name"));
// @formatter:on
// finally, the person is actually updated
context.define("superuser-alex@hostsharing.net");
assertThat(personRepo.findByUuid(givenPerson.getUuid())).isPresent().get()
.matches(person -> {
assertThat(person.getPersonType()).isEqualTo(HsOfficePersonType.JOINT_REPRESENTATION);
assertThat(person.getTradeName()).isEqualTo("Patched Trade Name");
assertThat(person.getFamilyName()).isEqualTo("Patched Family Name");
assertThat(person.getGivenName()).isEqualTo("Patched Given Name");
return true;
});
}
@Test
void globalAdmin_withoutAssumedRole_canPatchPartialPropertiesOfArbitraryPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = givenSomeTemporaryPersonCreatedBy("selfregistered-test-user@hostsharing.org");
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"familyName": "Patched Family Name",
"givenName": "Patched Given Name"
}
""")
.port(port)
.when()
.patch("http://localhost/api/hs/office/persons/" + givenPerson.getUuid())
.then().assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("personType", is(givenPerson.getPersonType().toString()))
.body("tradeName", is(givenPerson.getTradeName()))
.body("familyName", is("Patched Family Name"))
.body("givenName", is("Patched Given Name"));
// @formatter:on
// finally, the person is actually updated
assertThat(personRepo.findByUuid(givenPerson.getUuid())).isPresent().get()
.matches(person -> {
assertThat(person.getPersonType()).isEqualTo(givenPerson.getPersonType());
assertThat(person.getTradeName()).isEqualTo(givenPerson.getTradeName());
assertThat(person.getFamilyName()).isEqualTo("Patched Family Name");
assertThat(person.getGivenName()).isEqualTo("Patched Given Name");
return true;
});
}
}
@Nested
@Accepts({ "Person:D(Delete)" })
class DeletePerson {
@Test
void globalAdmin_withoutAssumedRole_canDeleteArbitraryPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = givenSomeTemporaryPersonCreatedBy("selfregistered-test-user@hostsharing.org");
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.delete("http://localhost/api/hs/office/persons/" + givenPerson.getUuid())
.then().log().body().assertThat()
.statusCode(204); // @formatter:on
// then the given person is gone
assertThat(personRepo.findByUuid(givenPerson.getUuid())).isEmpty();
}
@Test
@Accepts({ "Person:X(Access Control)" })
void personOwner_canDeleteRelatedPerson() {
final var givenPerson = givenSomeTemporaryPersonCreatedBy("selfregistered-test-user@hostsharing.org");
RestAssured // @formatter:off
.given()
.header("current-user", "selfregistered-test-user@hostsharing.org")
.port(port)
.when()
.delete("http://localhost/api/hs/office/persons/" + givenPerson.getUuid())
.then().log().body().assertThat()
.statusCode(204); // @formatter:on
// then the given person is still there
assertThat(personRepo.findByUuid(givenPerson.getUuid())).isEmpty();
}
@Test
@Accepts({ "Person:X(Access Control)" })
void normalUser_canNotDeleteUnrelatedPerson() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = givenSomeTemporaryPersonCreatedBy("selfregistered-test-user@hostsharing.org");
RestAssured // @formatter:off
.given()
.header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port)
.when()
.delete("http://localhost/api/hs/office/persons/" + givenPerson.getUuid())
.then().log().body().assertThat()
.statusCode(404); // unrelated user cannot even view the person
// @formatter:on
// then the given person is still there
assertThat(personRepo.findByUuid(givenPerson.getUuid())).isNotEmpty();
}
}
private HsOfficePersonEntity givenSomeTemporaryPersonCreatedBy(final String creatingUser) {
return jpaAttempt.transacted(() -> {
context.define(creatingUser);
final var newPerson = HsOfficePersonEntity.builder()
.uuid(UUID.randomUUID())
.personType(HsOfficePersonType.LEGAL)
.tradeName("Temp " + Context.getCallerMethodNameFromStackFrame(2))
.familyName(RandomStringUtils.randomAlphabetic(10) + "@example.org")
.givenName("Given Name " + RandomStringUtils.randomAlphabetic(10))
.build();
toCleanup(newPerson.getUuid());
return personRepo.save(newPerson);
}).assertSuccessful().returnedValue();
}
private UUID toCleanup(final UUID tempPersonUuid) {
tempPersonUuids.add(tempPersonUuid);
return tempPersonUuid;
}
@BeforeEach
@AfterEach
void cleanup() {
tempPersonUuids.forEach(uuid -> {
jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net", null);
System.out.println("DELETING temporary person: " + uuid);
final var entity = personRepo.findByUuid(uuid);
final var count = personRepo.deleteByUuid(uuid);
System.out.println("DELETED temporary person: " + uuid + (count > 0 ? " successful" : " failed") +
(" (" + entity.map(HsOfficePersonEntity::getDisplayName).orElse("null") + ")"));
}).assertSuccessful();
});
}
}

View File

@ -0,0 +1,153 @@
package net.hostsharing.hsadminng.hs.office.person;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonTypeResource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openapitools.jackson.nullable.JsonNullable;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
// TODO: there must be an easier way to test such patch classes
class HsOfficePersonEntityPatchUnitTest {
private static final UUID INITIAL_PERSON_UUID = UUID.randomUUID();
final HsOfficePersonEntity givenPerson = new HsOfficePersonEntity();
final HsOfficePersonPatchResource patchResource = new HsOfficePersonPatchResource();
private final HsOfficePersonEntityPatch hsOfficePersonEntityPatch =
new HsOfficePersonEntityPatch(givenPerson);
{
givenPerson.setUuid(INITIAL_PERSON_UUID);
givenPerson.setPersonType(HsOfficePersonType.LEGAL);
givenPerson.setTradeName("initial@example.org");
givenPerson.setFamilyName("initial postal address");
givenPerson.setGivenName("+01 100 123456789");
}
@Test
void willPatchAllProperties() {
// given
patchResource.setPersonType(HsOfficePersonTypeResource.NATURAL);
patchResource.setTradeName(JsonNullable.of("patched@example.org"));
patchResource.setFamilyName(JsonNullable.of("patched postal address"));
patchResource.setGivenName(JsonNullable.of("+01 200 987654321"));
// when
hsOfficePersonEntityPatch.apply(patchResource);
// then
new HsOfficePersonEntityMatcher()
.withPatchedPersonType(HsOfficePersonType.NATURAL)
.withPatchedTradeName("patched@example.org")
.withPatchedFamilyName("patched postal address")
.withPatchedGivenName("+01 200 987654321")
.matches(givenPerson);
}
@ParameterizedTest
@EnumSource(HsOfficePersonTypeResource.class)
void willPatchOnlyPersonTypeProperty(final HsOfficePersonTypeResource patchedValue) {
// given
patchResource.setPersonType(patchedValue);
// when
hsOfficePersonEntityPatch.apply(patchResource);
// then
new HsOfficePersonEntityMatcher()
.withPatchedPersonType(HsOfficePersonType.valueOf(patchedValue.getValue()))
.matches(givenPerson);
}
@ParameterizedTest
@ValueSource(strings = { "patched@example.org" })
@NullSource
void willPatchOnlyTradeNameProperty(final String patchedValue) {
// given
patchResource.setTradeName(JsonNullable.of(patchedValue));
// when
hsOfficePersonEntityPatch.apply(patchResource);
// then
new HsOfficePersonEntityMatcher()
.withPatchedTradeName(patchedValue)
.matches(givenPerson);
}
@ParameterizedTest
@ValueSource(strings = { "patched postal address" })
@NullSource
void willPatchOnlyFamilyNameProperty(final String patchedValue) {
// given
patchResource.setFamilyName(JsonNullable.of(patchedValue));
// when
hsOfficePersonEntityPatch.apply(patchResource);
// then
new HsOfficePersonEntityMatcher()
.withPatchedFamilyName(patchedValue)
.matches(givenPerson);
}
@ParameterizedTest
@ValueSource(strings = { "+01 200 987654321" })
@NullSource
void willPatchOnlyGivenNameProperty(final String patchedValue) {
// given
patchResource.setGivenName(JsonNullable.of(patchedValue));
// when
hsOfficePersonEntityPatch.apply(patchResource);
// then
new HsOfficePersonEntityMatcher()
.withPatchedGivenName(patchedValue)
.matches(givenPerson);
}
private static class HsOfficePersonEntityMatcher {
private HsOfficePersonType expectedPersonType = HsOfficePersonType.LEGAL;
private String expectedTradeName = "initial@example.org";
private String expectedFamilyName = "initial postal address";
private String expectedGivenName = "+01 100 123456789";
HsOfficePersonEntityMatcher withPatchedPersonType(final HsOfficePersonType patchedPersonType) {
expectedPersonType = patchedPersonType;
return this;
}
HsOfficePersonEntityMatcher withPatchedTradeName(final String patchedTradeName) {
expectedTradeName = patchedTradeName;
return this;
}
HsOfficePersonEntityMatcher withPatchedFamilyName(final String patchedFamilyName) {
expectedFamilyName = patchedFamilyName;
return this;
}
HsOfficePersonEntityMatcher withPatchedGivenName(final String patchedGivenName) {
expectedGivenName = patchedGivenName;
return this;
}
void matches(final HsOfficePersonEntity givenPerson) {
assertThat(givenPerson.getPersonType()).isEqualTo(expectedPersonType);
assertThat(givenPerson.getTradeName()).isEqualTo(expectedTradeName);
assertThat(givenPerson.getFamilyName()).isEqualTo(expectedFamilyName);
assertThat(givenPerson.getGivenName()).isEqualTo(expectedGivenName);
}
}
}