HsOfficeContactFromResourceConverter
This commit is contained in:
parent
4712d69012
commit
fa05234c89
@ -13,12 +13,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.errors.Validate.validate;
|
import static net.hostsharing.hsadminng.errors.Validate.validate;
|
||||||
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
|
||||||
@ -30,9 +29,18 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StrictMapper mapper;
|
private StrictMapper mapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HsOfficeContactFromResourceConverter<HsOfficeContactRbacEntity> contactFromResourceConverter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficeContactRbacRepository contactRepo;
|
private HsOfficeContactRbacRepository contactRepo;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// HOWTO: add a ModelMapper converter for a generic entity class to a ModelMapper to be used in a certain context
|
||||||
|
mapper.addConverter(contactFromResourceConverter, HsOfficeContactInsertResource.class, HsOfficeContactRbacEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@Timed("app.office.contacts.api.getListOfContacts")
|
@Timed("app.office.contacts.api.getListOfContacts")
|
||||||
@ -62,7 +70,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
|||||||
|
|
||||||
context.define(currentSubject, assumedRoles);
|
context.define(currentSubject, assumedRoles);
|
||||||
|
|
||||||
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class);
|
||||||
|
|
||||||
final var saved = contactRepo.save(entityToSave);
|
final var saved = contactRepo.save(entityToSave);
|
||||||
|
|
||||||
@ -128,11 +136,4 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
|||||||
final var mapped = mapper.map(saved, HsOfficeContactResource.class);
|
final var mapped = mapper.map(saved, HsOfficeContactResource.class);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final BiConsumer<HsOfficeContactInsertResource, HsOfficeContactRbacEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
|
||||||
entity.putPostalAddress(from(resource.getPostalAddress()));
|
|
||||||
entity.putEmailAddresses(from(resource.getEmailAddresses()));
|
|
||||||
entity.putPhoneNumbers(from(resource.getPhoneNumbers()));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.contact;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
||||||
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
|
import org.modelmapper.Converter;
|
||||||
|
import org.modelmapper.spi.MappingContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
||||||
|
|
||||||
|
// HOWTO: implement a ModelMapper converter which converts from a (JSON) resource instance to a generic entity instance (RBAC vs. REAL)
|
||||||
|
@Component
|
||||||
|
public class HsOfficeContactFromResourceConverter<E extends HsOfficeContact>
|
||||||
|
implements Converter<HsOfficeContactInsertResource, E> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StrictMapper mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public E convert(final MappingContext<HsOfficeContactInsertResource, E> context) {
|
||||||
|
final var resource = context.getSource();
|
||||||
|
final var entity = context.getDestinationType().getDeclaredConstructor().newInstance();
|
||||||
|
entity.setCaption(resource.getCaption());
|
||||||
|
entity.putPostalAddress(from(resource.getPostalAddress()));
|
||||||
|
entity.putEmailAddresses(from(resource.getEmailAddresses()));
|
||||||
|
entity.putPhoneNumbers(from(resource.getPhoneNumbers()));
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,10 @@ package net.hostsharing.hsadminng.hs.office.partner;
|
|||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
|
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactFromResourceConverter;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartnersApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartnersApi;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
||||||
@ -22,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.PersistenceContext;
|
import jakarta.persistence.PersistenceContext;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -41,6 +44,9 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StrictMapper mapper;
|
private StrictMapper mapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HsOfficeContactFromResourceConverter<HsOfficeContactRealEntity> contactFromResourceConverter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HsOfficePartnerRbacRepository rbacPartnerRepo;
|
private HsOfficePartnerRbacRepository rbacPartnerRepo;
|
||||||
|
|
||||||
@ -50,6 +56,11 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
private EntityManager em;
|
private EntityManager em;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
mapper.addConverter(contactFromResourceConverter, HsOfficeContactInsertResource.class, HsOfficeContactRealEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@Timed("app.office.partners.api.getListOfPartners")
|
@Timed("app.office.partners.api.getListOfPartners")
|
||||||
@ -185,7 +196,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
private void optionallyUpdateRelatedRelations(final HsOfficePartnerRbacEntity saved, final HsOfficePersonRealEntity previousPartnerPerson) {
|
private void optionallyUpdateRelatedRelations(final HsOfficePartnerRbacEntity saved, final HsOfficePersonRealEntity previousPartnerPerson) {
|
||||||
final var partnerPersonHasChanged = !saved.getPartnerRel().getHolder().getUuid().equals(previousPartnerPerson.getUuid());
|
final var partnerPersonHasChanged = !saved.getPartnerRel().getHolder().getUuid().equals(previousPartnerPerson.getUuid());
|
||||||
if (partnerPersonHasChanged) {
|
if (partnerPersonHasChanged) {
|
||||||
final var count = em.createNativeQuery("""
|
em.createNativeQuery("""
|
||||||
UPDATE hs_office.relation
|
UPDATE hs_office.relation
|
||||||
SET holderUuid = :newPartnerPersonUuid
|
SET holderUuid = :newPartnerPersonUuid
|
||||||
WHERE type = 'DEBITOR' AND holderUuid = :oldPartnerPersonUuid AND anchorUuid = :oldPartnerPersonUuid
|
WHERE type = 'DEBITOR' AND holderUuid = :oldPartnerPersonUuid AND anchorUuid = :oldPartnerPersonUuid
|
||||||
@ -193,7 +204,6 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
.setParameter("oldPartnerPersonUuid", previousPartnerPerson.getUuid())
|
.setParameter("oldPartnerPersonUuid", previousPartnerPerson.getUuid())
|
||||||
.setParameter("newPartnerPersonUuid", saved.getPartnerRel().getHolder().getUuid())
|
.setParameter("newPartnerPersonUuid", saved.getPartnerRel().getHolder().getUuid())
|
||||||
.executeUpdate();
|
.executeUpdate();
|
||||||
System.out.println(count); // FIXME: remove
|
|
||||||
em.createNativeQuery("""
|
em.createNativeQuery("""
|
||||||
UPDATE hs_office.relation
|
UPDATE hs_office.relation
|
||||||
SET anchorUuid = :newPartnerPersonUuid
|
SET anchorUuid = :newPartnerPersonUuid
|
||||||
|
@ -66,6 +66,9 @@ class HsOfficeRelationEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
|
|
||||||
{
|
{
|
||||||
setCaption("Patched-Contact-Caption");
|
setCaption("Patched-Contact-Caption");
|
||||||
|
setEmailAddresses(Map.ofEntries(
|
||||||
|
Map.entry("main", "patched@exampl.org")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private static HsOfficeContactRealEntity PATCHED_CONTACT = HsOfficeContactRealEntity.builder()
|
private static HsOfficeContactRealEntity PATCHED_CONTACT = HsOfficeContactRealEntity.builder()
|
||||||
|
@ -101,7 +101,10 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase<ReplaceD
|
|||||||
() -> httpGet("/api/hs/office/partners/%{partnerNumber}")
|
() -> httpGet("/api/hs/office/partners/%{partnerNumber}")
|
||||||
.expecting(OK).expecting(JSON).expectObject(),
|
.expecting(OK).expecting(JSON).expectObject(),
|
||||||
path("partnerRel.holder.tradeName").contains(
|
path("partnerRel.holder.tradeName").contains(
|
||||||
"Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}")
|
"Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}"),
|
||||||
|
path("partnerRel.contact.postalAddress").lenientlyContainsJson("§{communityOfHeirsPostalAddress}"),
|
||||||
|
path("partnerRel.contact.phoneNumbers.office").contains("%{communityOfHeirsOfficePhoneNumber}"),
|
||||||
|
path("partnerRel.contact.emailAddresses.main").contains("%{communityOfHeirsEmailAddress}")
|
||||||
);
|
);
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
@ -116,14 +119,16 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase<ReplaceD
|
|||||||
verify(
|
verify(
|
||||||
"Verify the Representative-Relation",
|
"Verify the Representative-Relation",
|
||||||
() -> httpGet(
|
() -> httpGet(
|
||||||
"/api/hs/office/relations?relationType=REPRESENTATIVE&personUuid=%{Person: %{representativeGivenName} %{representativeFamilyName}}")
|
"/api/hs/office/relations?relationType=REPRESENTATIVE&personUuid=%{Person: Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}}")
|
||||||
.expecting(OK).expecting(JSON).expectArrayElements(1),
|
.expecting(OK).expecting(JSON).expectArrayElements(1),
|
||||||
path("[0].anchor.tradeName").contains(
|
path("[0].anchor.tradeName").contains(
|
||||||
"Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}"),
|
"Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}"),
|
||||||
path("[0].holder.familyName").contains("%{representativeFamilyName}")
|
path("[0].holder.familyName").contains("%{representativeFamilyName}"),
|
||||||
|
path("[0].contact.postalAddress").lenientlyContainsJson("§{communityOfHeirsPostalAddress}"),
|
||||||
|
path("[0].contact.phoneNumbers.office").contains("%{communityOfHeirsOfficePhoneNumber}"),
|
||||||
|
path("[0].contact.emailAddresses.main").contains("%{communityOfHeirsEmailAddress}")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
"Verify the Debitor-Relation",
|
"Verify the Debitor-Relation",
|
||||||
() -> httpGet(
|
() -> httpGet(
|
||||||
|
@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase.HttpResponse;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||||
|
import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
public class PathAssertion {
|
public class PathAssertion {
|
||||||
@ -27,6 +28,18 @@ public class PathAssertion {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public Consumer<UseCase.HttpResponse> lenientlyContainsJson(final String resolvableValue) {
|
||||||
|
return response -> {
|
||||||
|
try {
|
||||||
|
lenientlyEquals(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS)).matches(response.getFromBody(path)) ;
|
||||||
|
} catch (final AssertionError e) {
|
||||||
|
// without this, the error message is often lacking important context
|
||||||
|
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public Consumer<HttpResponse> doesNotExist() {
|
public Consumer<HttpResponse> doesNotExist() {
|
||||||
return response -> {
|
return response -> {
|
||||||
try {
|
try {
|
||||||
|
@ -32,6 +32,12 @@ public class TemplateResolver {
|
|||||||
return jsonQuoted(value);
|
return jsonQuoted(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
JSON_OBJECT('§'){
|
||||||
|
@Override
|
||||||
|
String convert(final Object value, final Resolver resolver) {
|
||||||
|
return jsonObject(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
URI_ENCODED('&'){
|
URI_ENCODED('&'){
|
||||||
@Override
|
@Override
|
||||||
String convert(final Object value, final Resolver resolver) {
|
String convert(final Object value, final Resolver resolver) {
|
||||||
@ -213,4 +219,12 @@ public class TemplateResolver {
|
|||||||
default -> "\"" + value + "\"";
|
default -> "\"" + value + "\"";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String jsonObject(final Object value) {
|
||||||
|
return switch (value) {
|
||||||
|
case null -> null;
|
||||||
|
case String string -> "{" + string.replace("\n", " ") + "}";
|
||||||
|
default -> throw new IllegalArgumentException("can not format " + value.getClass() + " (" + value + ") as JSON object");
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user