create partner now taking existing contact+person uuids instead of complete (new) objects

This commit is contained in:
Michael Hoennig 2022-09-14 12:16:44 +02:00
parent 3fa02d4a10
commit 9cd4525e2b
7 changed files with 143 additions and 101 deletions

View File

@ -13,6 +13,7 @@ import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.LocalDateTime;
import java.util.NoSuchElementException;
import java.util.Optional;
@ControllerAdvice
@ -34,6 +35,13 @@ public class RestResponseEntityExceptionHandler
return errorResponse(request, httpStatus(message).orElse(HttpStatus.INTERNAL_SERVER_ERROR), message);
}
@ExceptionHandler(NoSuchElementException.class)
protected ResponseEntity<CustomErrorResponse> handleNoSuchElementException(
final RuntimeException exc, final WebRequest request) {
final var message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage());
return errorResponse(request, HttpStatus.NOT_FOUND, message);
}
@ExceptionHandler(Throwable.class)
protected ResponseEntity<CustomErrorResponse> handleOtherExceptions(
final Throwable exc, final WebRequest request) {

View File

@ -2,14 +2,9 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.Mapper;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartnersApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerUpdateResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePersonResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@ -18,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.function.BiConsumer;
@ -59,34 +55,25 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
public ResponseEntity<HsOfficePartnerResource> addPartner(
final String currentUser,
final String assumedRoles,
final HsOfficePartnerResource body) {
final HsOfficePartnerInsertResource body) {
context.define(currentUser, assumedRoles);
if (body.getUuid() == null) {
body.setUuid(UUID.randomUUID());
}
final var entityToSave = mapToHsOfficePartnerEntity(body);
entityToSave.setUuid(UUID.randomUUID());
entityToSave.setContact(contactRepo.findByUuid(body.getContactUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find contact uuid " + body.getContactUuid())
));
entityToSave.setPerson(personRepo.findByUuid(body.getPersonUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find person uuid " + body.getPersonUuid())
));
final var entityToSave = map(body, HsOfficePartnerEntity.class);
if (entityToSave.getContact().getUuid() != null) {
contactRepo.findByUuid(entityToSave.getContact().getUuid()).ifPresent(entityToSave::setContact);
} else {
entityToSave.getContact().setUuid(UUID.randomUUID());
entityToSave.setContact(contactRepo.save(entityToSave.getContact()));
}
if (entityToSave.getPerson().getUuid() != null) {
personRepo.findByUuid(entityToSave.getPerson().getUuid()).ifPresent(entityToSave::setPerson);
} else {
entityToSave.getPerson().setUuid(UUID.randomUUID());
entityToSave.setPerson(personRepo.save(entityToSave.getPerson()));
}
final var saved = partnerRepo.save(entityToSave);
final var uri =
MvcUriComponentsBuilder.fromController(getClass())
.path("/api/hs/office/partners/{id}")
.buildAndExpand(body.getUuid())
.buildAndExpand(entityToSave.getUuid())
.toUri();
final var mapped = map(saved, HsOfficePartnerResource.class,
PARTNER_ENTITY_TO_RESOURCE_POSTMAPPER);
@ -121,18 +108,22 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
final String currentUser,
final String assumedRoles,
final UUID partnerUuid,
final HsOfficePartnerUpdateResource body) {
final HsOfficePartnerPatchResource body) {
return null;
}
private final BiConsumer<HsOfficePartnerResource, HsOfficePartnerEntity> PARTNER_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
entity.setPerson(map(resource.getPerson(), HsOfficePersonEntity.class));
entity.setContact(map(resource.getContact(), HsOfficeContactEntity.class));
};
private final BiConsumer<HsOfficePartnerEntity, HsOfficePartnerResource> PARTNER_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
final BiConsumer<HsOfficePartnerEntity, HsOfficePartnerResource> PARTNER_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setPerson(map(entity.getPerson(), HsOfficePersonResource.class));
resource.setContact(map(entity.getContact(), HsOfficeContactResource.class));
};
private HsOfficePartnerEntity mapToHsOfficePartnerEntity(final HsOfficePartnerInsertResource resource) {
final var entity = new HsOfficePartnerEntity();
entity.setBirthday(resource.getBirthday());
entity.setBirthName(resource.getBirthName());
entity.setDateOfDeath(resource.getDateOfDeath());
entity.setRegistrationNumber(resource.getRegistrationNumber());
entity.setRegistrationOffice(resource.getRegistrationOffice());
return entity;
}
}

View File

@ -32,7 +32,7 @@ components:
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
- $ref: '#/components/schemas/HsOfficePartnerBase'
HsOfficePartnerUpdate:
HsOfficePartnerPatch:
allOf:
- type: object
properties:
@ -43,3 +43,18 @@ components:
type: string
format: uuid
- $ref: '#/components/schemas/HsOfficePartnerBase'
HsOfficePartnerInsert:
allOf:
- type: object
properties:
personUuid:
type: string
format: uuid
contactUuid:
type: string
format: uuid
- required:
- personUuid
- contactUuid
- $ref: '#/components/schemas/HsOfficePartnerBase'

View File

@ -42,7 +42,7 @@ patch:
content:
'application/json':
schema:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartnerUpdate'
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartnerPatch'
responses:
"200":
description: OK

View File

@ -39,7 +39,7 @@ post:
content:
'application/json':
schema:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartnerInsert'
required: true
responses:
"201":

View File

@ -156,7 +156,7 @@ grant all privileges on hs_office_contact_rv to restricted;
/**
Instead of insert trigger function for hs_office_contact_rv.
*/
create or replace function inserthsOfficeContact()
create or replace function insertHsOfficeContact()
returns trigger
language plpgsql as $$
declare
@ -173,11 +173,11 @@ $$;
/*
Creates an instead of insert trigger for the hs_office_contact_rv view.
*/
create trigger inserthsOfficeContact_Trigger
create trigger insertHsOfficeContact_Trigger
instead of insert
on hs_office_contact_rv
for each row
execute function inserthsOfficeContact();
execute function insertHsOfficeContact();
--//
-- ============================================================================
@ -189,7 +189,7 @@ execute function inserthsOfficeContact();
Checks if the current subject (user / assumed role) has the permission to delete the row.
*/
create or replace function deletehsOfficeContact()
create or replace function deleteHsOfficeContact()
returns trigger
language plpgsql as $$
begin
@ -204,11 +204,11 @@ end; $$;
/*
Creates an instead of delete trigger for the hs_office_contact_rv view.
*/
create trigger deletehsOfficeContact_Trigger
create trigger deleteHsOfficeContact_Trigger
instead of delete
on hs_office_contact_rv
for each row
execute function deletehsOfficeContact();
execute function deleteHsOfficeContact();
--/
-- ============================================================================

View File

@ -5,6 +5,8 @@ import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.Accepts;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.test.JpaAttempt;
import org.json.JSONException;
import org.junit.jupiter.api.AfterEach;
@ -20,7 +22,6 @@ import java.util.Set;
import java.util.UUID;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonBuilder.jsonObject;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.is;
@ -45,6 +46,12 @@ class HsOfficePartnerControllerAcceptanceTest {
@Autowired
HsOfficePartnerRepository partnerRepo;
@Autowired
HsOfficePersonRepository personRepo;
@Autowired
HsOfficeContactRepository contactRepo;
@Autowired
JpaAttempt jpaAttempt;
@ -90,70 +97,28 @@ class HsOfficePartnerControllerAcceptanceTest {
@Accepts({ "Partner:C(Create)" })
class AddPartner {
private final static String NEW_PARTNER_JSON_WITHOUT_UUID =
"""
{
"person": {
"personType": "LEGAL",
"tradeName": "Test Corp.",
"givenName": null,
"familyName": null
},
"contact": {
"label": "Test Corp.",
"postalAddress": "Test Corp.\\nTestweg 50\\n20001 Hamburg",
"emailAddresses": "office@example.com",
"phoneNumbers": "040 12345"
},
@Test
void globalAdmin_withoutAssumedRole_canAddPartner_withGeneratedUuid() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Ostfriesische").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0);
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"contactUuid": "%s",
"personUuid": "%s",
"registrationOffice": "Registergericht Hamburg",
"registrationNumber": "123456",
"birthName": null,
"birthday": null,
"dateOfDeath": null
}
""";
@Test
void globalAdmin_withoutAssumedRole_canAddPartner_withExplicitUuid() {
final var givenUUID = toCleanup(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6"));
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body(jsonObject(NEW_PARTNER_JSON_WITHOUT_UUID)
.with("uuid", givenUUID.toString()).toString())
.port(port)
.when()
.post("http://localhost/api/hs/office/partners")
.then().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", is("3fa85f64-5717-4562-b3fc-2c963f66afa6"))
.body("registrationNumber", is("123456"))
.body("person.tradeName", is("Test Corp."))
.body("contact.label", is("Test Corp."))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
// finally, the new partner can be accessed under the given UUID
final var newUserUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
assertThat(newUserUuid).isEqualTo(givenUUID);
context.define("superuser-alex@hostsharing.net");
assertThat(partnerRepo.findByUuid(newUserUuid))
.hasValueSatisfying(c -> assertThat(c.getPerson().getTradeName()).isEqualTo("Test Corp."));
}
@Test
void globalAdmin_withoutAssumedRole_canAddPartner_withGeneratedUuid() {
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body(NEW_PARTNER_JSON_WITHOUT_UUID)
""".formatted(givenContact.getUuid(), givenPerson.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/partners")
@ -162,7 +127,8 @@ class HsOfficePartnerControllerAcceptanceTest {
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("registrationNumber", is("123456"))
.body("person.tradeName", is("Test Corp."))
.body("contact.label", is(givenContact.getLabel()))
.body("person.tradeName", is(givenPerson.getTradeName()))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
@ -171,6 +137,68 @@ class HsOfficePartnerControllerAcceptanceTest {
location.substring(location.lastIndexOf('/') + 1)));
assertThat(newUserUuid).isNotNull();
}
@Test
void globalAdmin_canNotAddPartner_ifContactDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Ostfriesische").get(0);
final var givenContactUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6");
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"contactUuid": "%s",
"personUuid": "%s",
"registrationOffice": "Registergericht Hamburg",
"registrationNumber": "123456",
"birthName": null,
"birthday": null,
"dateOfDeath": null
}
""".formatted(givenContactUuid, givenPerson.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/partners")
.then().log().all().assertThat()
.statusCode(404)
.body("message", is("cannot find contact uuid 3fa85f64-5717-4562-b3fc-2c963f66afa6"));
// @formatter:on
}
@Test
void globalAdmin_canNotAddPartner_ifPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
final var givenPersonUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6");
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0);
final var location = RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"contactUuid": "%s",
"personUuid": "%s",
"registrationOffice": "Registergericht Hamburg",
"registrationNumber": "123456",
"birthName": null,
"birthday": null,
"dateOfDeath": null
}
""".formatted(givenContact.getUuid(), givenPersonUuid))
.port(port)
.when()
.post("http://localhost/api/hs/office/partners")
.then().log().all().assertThat()
.statusCode(404)
.body("message", is("cannot find person uuid 3fa85f64-5717-4562-b3fc-2c963f66afa6"));
// @formatter:on
}
}
@Nested