From bc1d107c34e6f4e5bd65f88464bae580196b6fc2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 24 Sep 2024 11:56:05 +0200 Subject: [PATCH 01/21] introduce targetUnixUser property for domain-setup booking-item --- .../booking/item/HsBookingItemController.java | 1 + .../HsDomainSetupBookingItemValidator.java | 10 ++++ ...mainSetupBookingItemValidatorUnitTest.java | 55 +++++++++++++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index 6afd5219..acf2fe40 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -71,6 +71,7 @@ public class HsBookingItemController implements HsBookingItemsApi { .buildAndExpand(saved.getUuid()) .toUri(); final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + return ResponseEntity.created(uri).body(mapped); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidator.java index c9fd731a..f42ea4e0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidator.java @@ -1,3 +1,4 @@ + package net.hostsharing.hsadminng.hs.booking.item.validators; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem; @@ -15,6 +16,9 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator { public static final String FQDN_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(? Date: Tue, 24 Sep 2024 12:23:32 +0200 Subject: [PATCH 02/21] add missing targetUnixUser property to other tests --- .../validators/HsDomainSetupHostingAssetValidatorUnitTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java index 8acca8d9..e5a119d5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java @@ -42,7 +42,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest { .project(project) .type(HsBookingItemType.DOMAIN_SETUP) .resources(new HashMap<>(ofEntries( - entry("domainName", domainName) + entry("domainName", domainName), + entry("targetUnixUser", "xyz00") )))); HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem); return HsHostingAssetRbacEntity.builder() -- 2.39.5 From 5014160113749a9f6d50e8cffde9bc417f2306d0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 24 Sep 2024 13:04:39 +0200 Subject: [PATCH 03/21] add targetUnixUser to import --- .../hsadminng/hs/migration/ImportHostingAssets.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java index 9d73ac89..634ba207 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/ImportHostingAssets.java @@ -1680,13 +1680,19 @@ public class ImportHostingAssets extends BaseOfficeDataImport { final var relatedProject = domainSetup.getSubHostingAssets().stream() .map(ha -> ha.getAssignedToAsset() != null ? ha.getAssignedToAsset().getRelatedProject() : null) .findAny().orElseThrow(); + final var targetUnixUser = domainSetup.getSubHostingAssets().stream() + .filter(subAsset -> subAsset.getType() == DOMAIN_HTTP_SETUP) + .map(domainHttpSetup -> domainHttpSetup.getAssignedToAsset().getIdentifier()) + .findAny().orElse(null); final var bookingItem = HsBookingItemRealEntity.builder() .type(HsBookingItemType.DOMAIN_SETUP) .caption("BI " + domainSetup.getIdentifier()) .project((HsBookingProjectRealEntity) relatedProject) //.validity(toPostgresDateRange(created, cancelled)) .resources(Map.ofEntries( - entry("domainName", domainSetup.getIdentifier()))) + entry("domainName", domainSetup.getIdentifier()), + entry("targetUnixUser", targetUnixUser) + )) .build(); domainSetup.setBookingItem(bookingItem); bookingItems.put(nextAvailableBookingItemId(), bookingItem); -- 2.39.5 From fdbe46311a165520e237ea62f4dda7e264a31b89 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 09:48:00 +0200 Subject: [PATCH 04/21] introduce BookingItemEntityProcessor --- .../booking/item/HsBookingItemController.java | 27 ++-- .../BookingItemEntitySaveProcessor.java | 131 ++++++++++++++++++ .../HsBookingItemEntityValidatorRegistry.java | 5 +- .../hs/validation/HsEntityValidator.java | 2 +- .../hostsharing/hsadminng/mapper/Mapper.java | 22 ++- 5 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/BookingItemEntitySaveProcessor.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index acf2fe40..71080a2c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -5,7 +5,9 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsA import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemPatchResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemResource; +import net.hostsharing.hsadminng.hs.booking.item.validators.BookingItemEntitySaveProcessor; import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.Mapper; import org.springframework.beans.factory.annotation.Autowired; @@ -48,7 +50,7 @@ public class HsBookingItemController implements HsBookingItemsApi { final var entities = bookingItemRepo.findAllByProjectUuid(projectUuid); - final var resources = mapper.mapList(entities, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + final var resources = mapper.mapList(entities, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER); return ResponseEntity.ok(resources); } @@ -62,16 +64,20 @@ public class HsBookingItemController implements HsBookingItemsApi { context.define(currentSubject, assumedRoles); final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); - - final var saved = HsBookingItemEntityValidatorRegistry.validated(em, bookingItemRepo.save(entityToSave)); + final var mapped = new BookingItemEntitySaveProcessor(em, entityToSave) + .preprocessEntity() + .validateEntity() + .prepareForSave() + .save() + .validateContext() + .mapUsing(e -> mapper.map(e, HsBookingItemResource.class, ITEM_TO_RESOURCE_POSTMAPPER)) + .revampProperties(); final var uri = MvcUriComponentsBuilder.fromController(getClass()) .path("/api/hs/booking/items/{id}") - .buildAndExpand(saved.getUuid()) + .buildAndExpand(mapped.getUuid()) .toUri(); - final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); - return ResponseEntity.created(uri).body(mapped); } @@ -88,7 +94,7 @@ public class HsBookingItemController implements HsBookingItemsApi { result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading return result .map(bookingItemEntity -> ResponseEntity.ok( - mapper.map(bookingItemEntity, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER))) + mapper.map(bookingItemEntity, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER))) .orElseGet(() -> ResponseEntity.notFound().build()); } @@ -121,18 +127,21 @@ public class HsBookingItemController implements HsBookingItemsApi { new HsBookingItemEntityPatcher(current).apply(body); final var saved = bookingItemRepo.save(HsBookingItemEntityValidatorRegistry.validated(em, current)); - final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + final var mapped = mapper.map(saved, HsBookingItemResource.class, RBAC_ENTITY_TO_RESOURCE_POSTMAPPER); return ResponseEntity.ok(mapped); } - final BiConsumer ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { + final BiConsumer ITEM_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { resource.setValidFrom(entity.getValidity().lower()); if (entity.getValidity().hasUpperBound()) { resource.setValidTo(entity.getValidity().upper().minusDays(1)); } }; + final BiConsumer RBAC_ENTITY_TO_RESOURCE_POSTMAPPER = ITEM_TO_RESOURCE_POSTMAPPER::accept; + final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { + entity.setProject(em.getReference(HsBookingProjectRealEntity.class, resource.getProjectUuid())); entity.setValidity(toPostgresDateRange(LocalDate.now(), resource.getValidTo())); entity.putResources(KeyValueMap.from(resource.getResources())); }; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/BookingItemEntitySaveProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/BookingItemEntitySaveProcessor.java new file mode 100644 index 00000000..77ce40ae --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/BookingItemEntitySaveProcessor.java @@ -0,0 +1,131 @@ +package net.hostsharing.hsadminng.hs.booking.item.validators; + +import net.hostsharing.hsadminng.errors.MultiValidationException; +import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemResource; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; + +import jakarta.persistence.EntityManager; +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.regex.Pattern; + +// TODO.refa: introduce common base class with HsHostingAssetEntitySaveProcessor +/** + * Wraps the steps of the pararation, validation, mapping and revamp around saving of a HsBookingItem into a readable API. + */ +public class BookingItemEntitySaveProcessor { + + private final HsEntityValidator validator; + private String expectedStep = "preprocessEntity"; + private final EntityManager em; + private HsBookingItem entity; + private HsBookingItemResource resource; + + public BookingItemEntitySaveProcessor(final EntityManager em, final HsBookingItem entity) { + this.em = em; + this.entity = entity; + this.validator = HsBookingItemEntityValidatorRegistry.forType(entity.getType()); + } + + /// initial step allowing to set default values before any validations + public BookingItemEntitySaveProcessor preprocessEntity() { + step("preprocessEntity", "validateEntity"); + validator.preprocessEntity(entity); + return this; + } + + /// validates the entity itself including its properties + public BookingItemEntitySaveProcessor validateEntity() { + step("validateEntity", "prepareForSave"); + MultiValidationException.throwIfNotEmpty(validator.validateEntity(entity)); + return this; + } + + // TODO.impl: remove once the migration of legacy data is done + /// validates the entity itself including its properties, but ignoring some error messages for import of legacy data + public BookingItemEntitySaveProcessor validateEntityIgnoring(final String... ignoreRegExp) { + step("validateEntity", "prepareForSave"); + final var ignoreRegExpPatterns = Arrays.stream(ignoreRegExp).map(Pattern::compile).toList(); + MultiValidationException.throwIfNotEmpty( + validator.validateEntity(entity).stream() + .filter(error -> ignoreRegExpPatterns.stream().noneMatch(p -> p.matcher(error).matches() )) + .toList() + ); + return this; + } + + /// hashing passwords etc. + public BookingItemEntitySaveProcessor prepareForSave() { + step("prepareForSave", "save"); + validator.prepareProperties(em, entity); + return this; + } + + /** + * Saves the entity using the given `saveFunction`. + * + *

`validator.postPersist(em, entity)` is NOT called. + * If any postprocessing is necessary, the saveFunction has to implement this.

+ * @param saveFunction + * @return this + */ + public BookingItemEntitySaveProcessor saveUsing(final Function saveFunction) { + step("save", "validateContext"); + entity = saveFunction.apply(entity); + return this; + } + + /** + * Saves the using the `EntityManager`, but does NOT ever merge the entity. + * + *

`validator.postPersist(em, entity)` is called afterwards with the entity guaranteed to be flushed to the database.

+ * @return this + */ + public BookingItemEntitySaveProcessor save() { + return saveUsing(e -> { + if (!em.contains(entity)) { + em.persist(entity); + } + em.flush(); // makes RbacEntity available as RealEntity if needed + validator.postPersist(em, entity); + return entity; + }); + } + + /// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits) + public BookingItemEntitySaveProcessor validateContext() { + step("validateContext", "mapUsing"); + return HsEntityValidator.doWithEntityManager(em, () -> { + MultiValidationException.throwIfNotEmpty(validator.validateContext(entity)); + return this; + }); + } + + /// maps entity to JSON resource representation + public BookingItemEntitySaveProcessor mapUsing( + final Function mapFunction) { + step("mapUsing", "revampProperties"); + resource = mapFunction.apply(entity); + return this; + } + + /// removes write-only-properties and ads computed-properties + @SuppressWarnings("unchecked") + public HsBookingItemResource revampProperties() { + step("revampProperties", null); + final var revampedProps = validator.revampProperties(em, entity, (Map) resource.getResources()); + resource.setResources(revampedProps); + return resource; + } + + // Makes sure that the steps are called in the correct order. + // Could also be implemented using an interface per method, but that seems exaggerated. + private void step(final String current, final String next) { + if (!expectedStep.equals(current)) { + throw new IllegalStateException("expected " + expectedStep + " but got " + current); + } + expectedStep = next; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorRegistry.java index 8bfe12fd..6567ae83 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorRegistry.java @@ -48,10 +48,11 @@ public class HsBookingItemEntityValidatorRegistry { } public static List doValidate(final EntityManager em, final HsBookingItem bookingItem) { + final var bookingItemValidator = HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()); return HsEntityValidator.doWithEntityManager(em, () -> HsEntityValidator.sequentiallyValidate( - () -> HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateEntity(bookingItem), - () -> HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem)) + () -> bookingItemValidator.validateEntity(bookingItem), + () -> bookingItemValidator.validateContext(bookingItem)) ); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index b2fa8a02..68f2779f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -130,7 +130,7 @@ public abstract class HsEntityValidator { } public Map revampProperties(final EntityManager em, final E entity, final Map config) { - final var copy = new HashMap<>(config); + final var copy = config != null ? new HashMap<>(config) : new HashMap(); stream(propertyValidators).forEach(p -> { if (p.isWriteOnly()) { copy.remove(p.propertyName); diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index 9fda5165..a8e25b4b 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.mapper; import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; +import org.modelmapper.spi.MatchingStrategy; import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; @@ -8,11 +10,16 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.PersistenceContext; import jakarta.validation.ValidationException; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.BiConsumer; import java.util.stream.Collectors; +import java.util.stream.Stream; +import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; +import static org.modelmapper.convention.MatchingStrategies.STRICT; /** * A nicer API for ModelMapper. @@ -24,6 +31,8 @@ public class Mapper extends ModelMapper { public Mapper() { getConfiguration().setAmbiguityIgnored(true); +// getConfiguration().setMatchingStrategy(STRICT); +// getConfiguration().setDeepCopyEnabled(true); } public List mapList(final List source, final Class targetClass) { @@ -46,7 +55,7 @@ public class Mapper extends ModelMapper { @Override public D map(final Object source, final Class destinationType) { final var target = super.map(source, destinationType); - for (Field f : destinationType.getDeclaredFields()) { + for (Field f : getDeclaredFieldsIncludingSuperClasses(destinationType)) { if (f.getAnnotation(ManyToOne.class) == null) { continue; } @@ -69,6 +78,17 @@ public class Mapper extends ModelMapper { return target; } + private static Field[] getDeclaredFieldsIncludingSuperClasses(final Class destinationType) { + if (destinationType == null) { + return new Field[0]; + } + + return Stream.concat( + stream(destinationType.getDeclaredFields()), + stream(getDeclaredFieldsIncludingSuperClasses(destinationType.getSuperclass()))) + .toArray(Field[]::new); + } + private Object findEntityById(final Class entityClass, final Object subEntityUuid) { // using getReference would be more efficent, but results in very technical error messages final var entity = em.find(entityClass, subEntityUuid); -- 2.39.5 From 4acd8a7d658bc97bfc737bc6d153cc45f0e3c1e5 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 11:30:54 +0200 Subject: [PATCH 05/21] Mapper.getConfiguration().setMatchingStrategy(STRICT); --- .../java/net/hostsharing/hsadminng/mapper/Mapper.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index a8e25b4b..b16db84b 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -1,8 +1,6 @@ package net.hostsharing.hsadminng.mapper; import org.modelmapper.ModelMapper; -import org.modelmapper.convention.MatchingStrategies; -import org.modelmapper.spi.MatchingStrategy; import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; @@ -10,8 +8,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.PersistenceContext; import jakarta.validation.ValidationException; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -30,9 +26,7 @@ public class Mapper extends ModelMapper { EntityManager em; public Mapper() { - getConfiguration().setAmbiguityIgnored(true); -// getConfiguration().setMatchingStrategy(STRICT); -// getConfiguration().setDeepCopyEnabled(true); + getConfiguration().setMatchingStrategy(STRICT); } public List mapList(final List source, final Class targetClass) { -- 2.39.5 From 303520881820d2b4850ce95bc83f5c467a81151d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 12:13:10 +0200 Subject: [PATCH 06/21] use EntityManagerWrapper in HsBookingItemController to enable mocking --- .../booking/item/HsBookingItemController.java | 7 ++--- .../item/HsBookingItemControllerRestTest.java | 26 ++++++++++++++++--- .../HsHostingAssetControllerRestTest.java | 4 ++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index 71080a2c..88f3a4e9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -10,6 +10,7 @@ import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityV import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -37,8 +38,8 @@ public class HsBookingItemController implements HsBookingItemsApi { @Autowired private HsBookingItemRbacRepository bookingItemRepo; - @PersistenceContext - private EntityManager em; + @Autowired + private EntityManagerWrapper em; @Override @Transactional(readOnly = true) @@ -141,7 +142,7 @@ public class HsBookingItemController implements HsBookingItemsApi { final BiConsumer RBAC_ENTITY_TO_RESOURCE_POSTMAPPER = ITEM_TO_RESOURCE_POSTMAPPER::accept; final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - entity.setProject(em.getReference(HsBookingProjectRealEntity.class, resource.getProjectUuid())); + entity.setProject(em.find(HsBookingProjectRealEntity.class, resource.getProjectUuid())); entity.setValidity(toPostgresDateRange(LocalDate.now(), resource.getValidTo())); entity.putResources(KeyValueMap.from(resource.getResources())); }; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java index e28f4d38..4e21df03 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java @@ -1,17 +1,20 @@ package net.hostsharing.hsadminng.hs.booking.item; +import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository; import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; @@ -28,13 +31,14 @@ import java.util.UUID; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static org.hamcrest.Matchers.matchesRegex; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(HsBookingItemController.class) -@Import(Mapper.class) +@Import({Mapper.class, JsonObjectMapperConfiguration.class}) @RunWith(SpringRunner.class) class HsBookingItemControllerRestTest { @@ -44,8 +48,12 @@ class HsBookingItemControllerRestTest { @MockBean Context contextMock; - @Mock - EntityManager em; + @Autowired + @SuppressWarnings("unused") // not used in test, but in controller class + Mapper mapper; + + @MockBean + EntityManagerWrapper em; @MockBean EntityManagerFactory emf; @@ -56,6 +64,16 @@ class HsBookingItemControllerRestTest { @MockBean HsBookingItemRbacRepository rbacBookingItemRepo; + @TestConfiguration + public static class TestConfig { + + @Bean + public EntityManager entityManager() { + return mock(EntityManager.class); + } + + } + @BeforeEach void init() { when(emf.createEntityManager()).thenReturn(em); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 0d9dd87b..b2726f48 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -63,10 +63,11 @@ public class HsHostingAssetControllerRestTest { Context contextMock; @Autowired + @SuppressWarnings("unused") // not used in test, but in controller class Mapper mapper; @MockBean - private EntityManagerWrapper em; + EntityManagerWrapper em; @MockBean EntityManagerFactory emf; @@ -90,6 +91,7 @@ public class HsHostingAssetControllerRestTest { } } + enum ListTestCases { CLOUD_SERVER( List.of( -- 2.39.5 From 900d3bdc75b4ca7d1a522e385a21a14ca95a860b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 12:56:35 +0200 Subject: [PATCH 07/21] use Mapper.fetchEntity to resolve sub-entities --- .../hsadminng/hs/booking/item/HsBookingItemController.java | 2 -- .../office/sepamandate/HsOfficeSepaMandateController.java | 4 ++++ src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index 88f3a4e9..f28d418d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -17,8 +17,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import java.time.LocalDate; import java.util.List; import java.util.UUID; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java index 9511bdd6..1ccfb457 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.office.sepamandate; import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; +import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMandatesApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource; @@ -135,6 +137,8 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi { }; final BiConsumer SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { + entity.setDebitor(mapper.fetchEntity(HsOfficeDebitorEntity.class, resource.getDebitorUuid())); + entity.setBankAccount(mapper.fetchEntity(HsOfficeBankAccountEntity.class, resource.getBankAccountUuid())); entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo())); }; } diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index b16db84b..91dc725d 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -26,6 +26,7 @@ public class Mapper extends ModelMapper { EntityManager em; public Mapper() { + // make sure that resource.whateverUuid does not get mapped to entity.uuid, if resource.uuid does not exist getConfiguration().setMatchingStrategy(STRICT); } @@ -67,7 +68,7 @@ public class Mapper extends ModelMapper { if (subEntityUuid == null) { continue; } - ReflectionUtils.setField(f, target, findEntityById(f.getType(), subEntityUuid)); + ReflectionUtils.setField(f, target, fetchEntity(f.getType(), subEntityUuid)); } return target; } @@ -83,9 +84,9 @@ public class Mapper extends ModelMapper { .toArray(Field[]::new); } - private Object findEntityById(final Class entityClass, final Object subEntityUuid) { + public E fetchEntity(final Class entityClass, final Object subEntityUuid) { // using getReference would be more efficent, but results in very technical error messages - final var entity = em.find(entityClass, subEntityUuid); + final var entity = em.find(entityClass, subEntityUuid); // FIXME: try getReference if (entity != null) { return entity; } -- 2.39.5 From 6225eb3bf625de206b7e4137b94204e0f5f0e2c0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 13:04:16 +0200 Subject: [PATCH 08/21] more mapper.fetchEntity usage --- .../coopshares/HsOfficeCoopSharesTransactionController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java index 1616568d..c6bd19f9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopShar import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.errors.MultiValidationException; +import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.mapper.Mapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; @@ -131,6 +132,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar } final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { + entity.setMembership(mapper.fetchEntity(HsOfficeMembershipEntity.class, resource.getMembershipUuid())); if ( resource.getAdjustedShareTxUuid() != null ) { entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid()) .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid())))); -- 2.39.5 From 087d59b2383c2cd6a732509183b1834636be8941 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 14:13:03 +0200 Subject: [PATCH 09/21] intoduce Mapper.withMatchingStrategy --- .../hs/booking/item/HsBookingItemController.java | 4 +++- .../net/hostsharing/hsadminng/mapper/Mapper.java | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index f28d418d..e8e01b87 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -23,6 +23,7 @@ import java.util.UUID; import java.util.function.BiConsumer; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; +import static org.modelmapper.convention.MatchingStrategies.STRICT; @RestController public class HsBookingItemController implements HsBookingItemsApi { @@ -62,7 +63,8 @@ public class HsBookingItemController implements HsBookingItemsApi { context.define(currentSubject, assumedRoles); - final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); + final var entityToSave = mapper.withMatchingStrategy(STRICT) + .map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var mapped = new BookingItemEntitySaveProcessor(em, entityToSave) .preprocessEntity() .validateEntity() diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index 91dc725d..a8355aaf 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.mapper; import org.modelmapper.ModelMapper; +import org.modelmapper.spi.MatchingStrategy; import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; @@ -15,7 +16,6 @@ import java.util.stream.Stream; import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; -import static org.modelmapper.convention.MatchingStrategies.STRICT; /** * A nicer API for ModelMapper. @@ -26,8 +26,17 @@ public class Mapper extends ModelMapper { EntityManager em; public Mapper() { - // make sure that resource.whateverUuid does not get mapped to entity.uuid, if resource.uuid does not exist - getConfiguration().setMatchingStrategy(STRICT); + getConfiguration().setAmbiguityIgnored(true); + } + + /** Use STRICT to make sure that resource.whateverUuid does not accidentally get mapped to entity.uuid, + * if resource.uuid does not exist + * + * @return this + */ + public Mapper withMatchingStrategy(final MatchingStrategy matchingStrategy) { + getConfiguration().setMatchingStrategy(matchingStrategy); + return this; } public List mapList(final List source, final Class targetClass) { -- 2.39.5 From 9bb81ff8f704445604d9975207824cd35f65438d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 14:13:46 +0200 Subject: [PATCH 10/21] revert mapping changes in Office --- .../coopshares/HsOfficeCoopSharesTransactionController.java | 2 -- .../hs/office/sepamandate/HsOfficeSepaMandateController.java | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java index c6bd19f9..1616568d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java @@ -6,7 +6,6 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopShar import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.errors.MultiValidationException; -import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.mapper.Mapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; @@ -132,7 +131,6 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar } final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - entity.setMembership(mapper.fetchEntity(HsOfficeMembershipEntity.class, resource.getMembershipUuid())); if ( resource.getAdjustedShareTxUuid() != null ) { entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid()) .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid())))); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java index 1ccfb457..9511bdd6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java @@ -1,8 +1,6 @@ package net.hostsharing.hsadminng.hs.office.sepamandate; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; -import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMandatesApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource; @@ -137,8 +135,6 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi { }; final BiConsumer SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - entity.setDebitor(mapper.fetchEntity(HsOfficeDebitorEntity.class, resource.getDebitorUuid())); - entity.setBankAccount(mapper.fetchEntity(HsOfficeBankAccountEntity.class, resource.getBankAccountUuid())); entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo())); }; } -- 2.39.5 From 8ebbf20b938feef4c22a6cd67290716ff9ae2882 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 16:21:23 +0200 Subject: [PATCH 11/21] split StandardMapper and StrictMapper --- .../booking/item/HsBookingItemController.java | 8 +++----- .../project/HsBookingProjectController.java | 4 ++-- .../asset/HsHostingAssetController.java | 4 ++-- .../HsOfficeBankAccountController.java | 4 ++-- .../contact/HsOfficeContactController.java | 4 ++-- ...OfficeCoopAssetsTransactionController.java | 4 ++-- ...OfficeCoopSharesTransactionController.java | 4 ++-- .../debitor/HsOfficeDebitorController.java | 4 ++-- .../HsOfficeMembershipController.java | 4 ++-- .../HsOfficeMembershipEntityPatcher.java | 6 +++--- .../partner/HsOfficePartnerController.java | 4 ++-- .../person/HsOfficePersonController.java | 4 ++-- .../relation/HsOfficeRelationController.java | 4 ++-- .../HsOfficeSepaMandateController.java | 4 ++-- .../hostsharing/hsadminng/mapper/Mapper.java | 15 ++------------- .../hsadminng/mapper/MapperConfiguration.java | 13 ------------- .../hsadminng/mapper/StandardMapper.java | 14 ++++++++++++++ .../hsadminng/mapper/StrictMapper.java | 19 +++++++++++++++++++ .../rbac/grant/RbacGrantController.java | 4 ++-- .../rbac/role/RbacRoleController.java | 4 ++-- .../rbac/subject/RbacSubjectController.java | 4 ++-- .../test/cust/TestCustomerController.java | 4 ++-- .../rbac/test/pac/TestPackageController.java | 4 ++-- .../item/HsBookingItemControllerRestTest.java | 6 +++--- .../HsHostingAssetControllerRestTest.java | 6 +++--- ...HsOfficeBankAccountControllerRestTest.java | 4 ++-- ...opAssetsTransactionControllerRestTest.java | 4 ++-- ...opSharesTransactionControllerRestTest.java | 4 ++-- .../HsOfficeMembershipControllerRestTest.java | 4 ++-- ...OfficeMembershipEntityPatcherUnitTest.java | 4 ++-- .../HsOfficePartnerControllerRestTest.java | 4 ++-- .../rbac/context/ContextIntegrationTests.java | 4 ++-- .../rbac/role/RbacRoleControllerRestTest.java | 4 ++-- .../RbacSubjectControllerRestTest.java | 4 ++-- .../hsadminng/rbac/test/MapperUnitTest.java | 4 ++-- 35 files changed, 101 insertions(+), 94 deletions(-) delete mode 100644 src/main/java/net/hostsharing/hsadminng/mapper/MapperConfiguration.java create mode 100644 src/main/java/net/hostsharing/hsadminng/mapper/StandardMapper.java create mode 100644 src/main/java/net/hostsharing/hsadminng/mapper/StrictMapper.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index e8e01b87..84f35054 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.hs.booking.item.validators.BookingItemEntitySav import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.mapper.KeyValueMap; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -23,7 +23,6 @@ import java.util.UUID; import java.util.function.BiConsumer; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; -import static org.modelmapper.convention.MatchingStrategies.STRICT; @RestController public class HsBookingItemController implements HsBookingItemsApi { @@ -32,7 +31,7 @@ public class HsBookingItemController implements HsBookingItemsApi { private Context context; @Autowired - private Mapper mapper; + private StrictMapper mapper; @Autowired private HsBookingItemRbacRepository bookingItemRepo; @@ -63,8 +62,7 @@ public class HsBookingItemController implements HsBookingItemsApi { context.define(currentSubject, assumedRoles); - final var entityToSave = mapper.withMatchingStrategy(STRICT) - .map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); + final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var mapped = new BookingItemEntitySaveProcessor(em, entityToSave) .preprocessEntity() .validateEntity() diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectController.java index 55d26b36..f657d45e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectController.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjec import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectInsertResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectPatchResource; import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingProjectResource; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -25,7 +25,7 @@ public class HsBookingProjectController implements HsBookingProjectsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsBookingProjectRbacRepository bookingProjectRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java index 8973d0cc..56b59427 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java @@ -11,7 +11,7 @@ import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAsse import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource; import net.hostsharing.hsadminng.mapper.KeyValueMap; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -35,7 +35,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsHostingAssetRbacRepository rbacAssetRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountController.java index 50183bf7..69295853 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountController.java @@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountResource; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.iban4j.BicUtil; import org.iban4j.IbanUtil; import org.springframework.beans.factory.annotation.Autowired; @@ -24,7 +24,7 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeBankAccountRepository bankAccountRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactController.java index 17d39b7f..0f94e858 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.office.contact; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource; @@ -26,7 +26,7 @@ public class HsOfficeContactController implements HsOfficeContactsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeContactRbacRepository contactRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java index f4cb28a3..9154b8f0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java @@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*; import net.hostsharing.hsadminng.errors.MultiValidationException; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; @@ -29,7 +29,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java index 1616568d..6712fefd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopShar import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopSharesTransactionResource; import net.hostsharing.hsadminng.errors.MultiValidationException; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; @@ -31,7 +31,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java index fd8412c8..a2466aa9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java @@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebito import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.object.BaseEntity; import org.apache.commons.lang3.Validate; import org.hibernate.Hibernate; @@ -34,7 +34,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeDebitorRepository debitorRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index 8c87e5fa..d63f9e6a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembersh import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipResource; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +24,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeMembershipRepository membershipRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java index cbecb800..33bf363b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java @@ -2,18 +2,18 @@ package net.hostsharing.hsadminng.hs.office.membership; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.mapper.EntityPatcher; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.OptionalFromJson; import java.util.Optional; public class HsOfficeMembershipEntityPatcher implements EntityPatcher { - private final Mapper mapper; + private final StandardMapper mapper; private final HsOfficeMembershipEntity entity; public HsOfficeMembershipEntityPatcher( - final Mapper mapper, + final StandardMapper mapper, final HsOfficeMembershipEntity entity) { this.mapper = mapper; this.entity = entity; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java index b4b8bd75..55c280f3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java @@ -12,7 +12,7 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.object.BaseEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -36,7 +36,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficePartnerRepository partnerRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonController.java index 41d9d441..ac746aab 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.office.person; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; 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; @@ -23,7 +23,7 @@ public class HsOfficePersonController implements HsOfficePersonsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficePersonRepository personRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java index f054e563..b93537d9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationsApi; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -28,7 +28,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeRelationRbacRepository relationRbacRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java index 9511bdd6..52ef2aad 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeSepaMand import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateInsertResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandateResource; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; @@ -28,7 +28,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private HsOfficeSepaMandateRepository sepaMandateRepo; diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index a8355aaf..b568a0e6 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -1,7 +1,6 @@ package net.hostsharing.hsadminng.mapper; import org.modelmapper.ModelMapper; -import org.modelmapper.spi.MatchingStrategy; import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; @@ -20,25 +19,15 @@ import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; /** * A nicer API for ModelMapper. */ -public class Mapper extends ModelMapper { +abstract class Mapper extends ModelMapper { @PersistenceContext EntityManager em; - public Mapper() { + Mapper() { getConfiguration().setAmbiguityIgnored(true); } - /** Use STRICT to make sure that resource.whateverUuid does not accidentally get mapped to entity.uuid, - * if resource.uuid does not exist - * - * @return this - */ - public Mapper withMatchingStrategy(final MatchingStrategy matchingStrategy) { - getConfiguration().setMatchingStrategy(matchingStrategy); - return this; - } - public List mapList(final List source, final Class targetClass) { return mapList(source, targetClass, null); } diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/MapperConfiguration.java b/src/main/java/net/hostsharing/hsadminng/mapper/MapperConfiguration.java deleted file mode 100644 index a77b953a..00000000 --- a/src/main/java/net/hostsharing/hsadminng/mapper/MapperConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.hostsharing.hsadminng.mapper; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class MapperConfiguration { - - @Bean - public Mapper modelMapper() { - return new Mapper(); - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/StandardMapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/StandardMapper.java new file mode 100644 index 00000000..42725d3d --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/mapper/StandardMapper.java @@ -0,0 +1,14 @@ +package net.hostsharing.hsadminng.mapper; + +import org.springframework.stereotype.Component; + +/** + * A nicer API for ModelMapper in standard mode. + */ +@Component +public class StandardMapper extends Mapper { + + public StandardMapper() { + getConfiguration().setAmbiguityIgnored(true); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/StrictMapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/StrictMapper.java new file mode 100644 index 00000000..a6d3c3fc --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/mapper/StrictMapper.java @@ -0,0 +1,19 @@ +package net.hostsharing.hsadminng.mapper; + +import org.springframework.stereotype.Component; + +import static org.modelmapper.convention.MatchingStrategies.STRICT; + +/** + * A nicer API for ModelMapper in strict mode. + * + *

This makes sure that resource.whateverUuid does not accidentally get mapped to entity.uuid, + * if resource.uuid does not exist.

+ */ +@Component +public class StrictMapper extends Mapper { + + public StrictMapper() { + getConfiguration().setMatchingStrategy(STRICT); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantController.java b/src/main/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantController.java index 4ca538b9..6af53104 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantController.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.grant; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacGrantResource; import org.springframework.beans.factory.annotation.Autowired; @@ -22,7 +22,7 @@ public class RbacGrantController implements RbacGrantsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private RbacGrantRepository rbacGrantRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/role/RbacRoleController.java b/src/main/java/net/hostsharing/hsadminng/rbac/role/RbacRoleController.java index 5da97292..cffff888 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/role/RbacRoleController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/role/RbacRoleController.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.role; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacRoleResource; import org.springframework.beans.factory.annotation.Autowired; @@ -18,7 +18,7 @@ public class RbacRoleController implements RbacRolesApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private RbacRoleRepository rbacRoleRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectController.java b/src/main/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectController.java index 52c0649b..1676cc7c 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectController.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.subject; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource; @@ -21,7 +21,7 @@ public class RbacSubjectController implements RbacSubjectsApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private RbacSubjectRepository rbacSubjectRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerController.java b/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerController.java index c6bbc115..26628545 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerController.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.test.cust; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi; import net.hostsharing.hsadminng.test.generated.api.v1.model.TestCustomerResource; import org.springframework.beans.factory.annotation.Autowired; @@ -21,7 +21,7 @@ public class TestCustomerController implements TestCustomersApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private TestCustomerRepository testCustomerRepository; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageController.java b/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageController.java index c6ecc7e0..d503bf58 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.rbac.test.pac; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.test.generated.api.v1.api.TestPackagesApi; @@ -21,7 +21,7 @@ public class TestPackageController implements TestPackagesApi { private Context context; @Autowired - private Mapper mapper; + private StandardMapper mapper; @Autowired private TestPackageRepository testPackageRepository; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java index 4e21df03..11ecf5bc 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java @@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(HsBookingItemController.class) -@Import({Mapper.class, JsonObjectMapperConfiguration.class}) +@Import({StrictMapper.class, JsonObjectMapperConfiguration.class}) @RunWith(SpringRunner.class) class HsBookingItemControllerRestTest { @@ -50,7 +50,7 @@ class HsBookingItemControllerRestTest { @Autowired @SuppressWarnings("unused") // not used in test, but in controller class - Mapper mapper; + StrictMapper mapper; @MockBean EntityManagerWrapper em; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index b2726f48..9dc5741b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository; import net.hostsharing.hsadminng.mapper.Array; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,7 +52,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(HsHostingAssetController.class) -@Import({Mapper.class, JsonObjectMapperConfiguration.class}) +@Import({ StandardMapper.class, JsonObjectMapperConfiguration.class}) @RunWith(SpringRunner.class) public class HsHostingAssetControllerRestTest { @@ -64,7 +64,7 @@ public class HsHostingAssetControllerRestTest { @Autowired @SuppressWarnings("unused") // not used in test, but in controller class - Mapper mapper; + StandardMapper mapper; @MockBean EntityManagerWrapper em; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java index 6dcd1cb5..624e9994 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.office.bankaccount; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +25,7 @@ class HsOfficeBankAccountControllerRestTest { Context contextMock; @MockBean - Mapper mapper; + StandardMapper mapper; @MockBean HsOfficeBankAccountRepository bankAccountRepo; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index 0e4716d4..03a1c71b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.office.coopassets; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.test.JsonBuilder; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -30,7 +30,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { Context contextMock; @MockBean - Mapper mapper; + StandardMapper mapper; @MockBean HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java index 4d44c0fb..1e33fde9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.test.JsonBuilder; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -30,7 +30,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest { Context contextMock; @MockBean - Mapper mapper; + StandardMapper mapper; @MockBean HsOfficeCoopSharesTransactionRepository coopSharesTransactionRepo; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 2a5005e6..c26024fd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.membership; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -32,7 +32,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(HsOfficeMembershipController.class) -@Import(Mapper.class) +@Import(StandardMapper.class) public class HsOfficeMembershipControllerRestTest { @Autowired diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java index 2e739e7f..841e7e12 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java @@ -4,7 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInstance; @@ -40,7 +40,7 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase< @Mock private EntityManager em; - private Mapper mapper = new Mapper(); + private StandardMapper mapper = new StandardMapper(); @BeforeEach void initMocks() { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java index a42a4780..2af222dc 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -36,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(HsOfficePartnerController.class) -@Import(Mapper.class) +@Import(StandardMapper.class) class HsOfficePartnerControllerRestTest { static final UUID GIVEN_MANDANTE_UUID = UUID.randomUUID(); diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java index c48f782e..cf5f387e 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.context; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import org.junit.jupiter.api.Test; @@ -17,7 +17,7 @@ import jakarta.servlet.http.HttpServletRequest; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest -@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, Mapper.class }) +@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, StandardMapper.class }) @DirtiesContext class ContextIntegrationTests { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java index 2d3d74c7..7d38b0e9 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.role; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; @@ -30,7 +30,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(RbacRoleController.class) -@Import(Mapper.class) +@Import(StandardMapper.class) @RunWith(SpringRunner.class) class RbacRoleControllerRestTest { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java index d23a8394..2131c7d9 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.rbac.subject; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; @@ -31,7 +31,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(RbacSubjectController.class) -@Import(Mapper.class) +@Import(StandardMapper.class) @RunWith(SpringRunner.class) class RbacSubjectControllerRestTest { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java index 0e01cd05..c2721246 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java @@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.rbac.test; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayAs; -import net.hostsharing.hsadminng.mapper.Mapper; +import net.hostsharing.hsadminng.mapper.StandardMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -27,7 +27,7 @@ class MapperUnitTest { EntityManager em; @InjectMocks - Mapper mapper; + StandardMapper mapper; final UUID GIVEN_UUID = UUID.randomUUID(); -- 2.39.5 From 0701d8a629747873a84799614fdc33cbde68fe37 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 17:13:56 +0200 Subject: [PATCH 12/21] add namePrefix to mapper error message --- .../debitor/HsOfficeDebitorController.java | 2 +- .../hostsharing/hsadminng/mapper/Mapper.java | 23 +++++++++++++++---- ...OfficeDebitorControllerAcceptanceTest.java | 4 ++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java index a2466aa9..a9109c13 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java @@ -84,7 +84,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class); if ( body.getDebitorRel() != null ) { body.getDebitorRel().setType(DEBITOR.name()); - final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationRealEntity.class); + final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class); validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor()); validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder()); validateEntityExists("debitorRel.contactUuid", debitorRel.getContact()); diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index b568a0e6..aabfdfd9 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -47,6 +47,10 @@ abstract class Mapper extends ModelMapper { @Override public D map(final Object source, final Class destinationType) { + return map("", source, destinationType); + } + + public D map(final String namePrefix, final Object source, final Class destinationType) { final var target = super.map(source, destinationType); for (Field f : getDeclaredFieldsIncludingSuperClasses(destinationType)) { if (f.getAnnotation(ManyToOne.class) == null) { @@ -66,7 +70,7 @@ abstract class Mapper extends ModelMapper { if (subEntityUuid == null) { continue; } - ReflectionUtils.setField(f, target, fetchEntity(f.getType(), subEntityUuid)); + ReflectionUtils.setField(f, target, fetchEntity(namePrefix + f.getName() + ".uuid", f.getType(), subEntityUuid)); } return target; } @@ -82,13 +86,15 @@ abstract class Mapper extends ModelMapper { .toArray(Field[]::new); } - public E fetchEntity(final Class entityClass, final Object subEntityUuid) { - // using getReference would be more efficent, but results in very technical error messages + public E fetchEntity(final String propertyName, final Class entityClass, final Object subEntityUuid) { + // using getReference would be more efficient, but results in very technical error messages final var entity = em.find(entityClass, subEntityUuid); // FIXME: try getReference if (entity != null) { return entity; } - throw new ValidationException("Unable to find " + DisplayName.of(entityClass) + " by uuid: " + subEntityUuid); + throw new ValidationException( + "Unable to find " + DisplayName.of(entityClass) + + " by " + propertyName + ": " + subEntityUuid); } public T map(final S source, final Class targetClass, final BiConsumer postMapper) { @@ -99,4 +105,13 @@ abstract class Mapper extends ModelMapper { postMapper.accept(source, target); return target; } + + public T map(final String namePrefix, final S source, final Class targetClass, final BiConsumer postMapper) { + if (source == null) { + return null; + } + final var target = map(source, targetClass); + postMapper.accept(source, target); + return target; + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index 98ba650c..7f3b2281 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -405,7 +405,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu .post("http://localhost/api/hs/office/debitors") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contactUuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contact.uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } @@ -433,7 +433,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu .post("http://localhost/api/hs/office/debitors") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find RealRelation by uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find RealRelation by debitorRel.uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } } -- 2.39.5 From d910b801cf1412ec5f7385ac4cc02721c66bbc7a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 17:23:03 +0200 Subject: [PATCH 13/21] add missing namePrefix to assertion --- .../office/membership/HsOfficeMembershipControllerRestTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index c26024fd..64de089c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -115,7 +115,7 @@ public class HsOfficeMembershipControllerRestTest { .andExpect(status().is4xxClientError()) .andExpect(jsonPath("statusCode", is(400))) .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by uuid: " + givenPartnerUuid))); + .andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid))); } @ParameterizedTest -- 2.39.5 From 35d7fca8f9b75eac05386fed97944588bbadec1c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 17:24:03 +0200 Subject: [PATCH 14/21] add missing namePrefix to assertion --- .../net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java index c2721246..fa8823b3 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java @@ -88,7 +88,7 @@ class MapperUnitTest { ); assertThat(exception).isInstanceOf(ValidationException.class) - .hasMessage("Unable to find SubTargetBean1 by uuid: " + GIVEN_UUID); + .hasMessage("Unable to find SubTargetBean1 by s1.uuid: " + GIVEN_UUID); } @Test @@ -101,7 +101,7 @@ class MapperUnitTest { ); assertThat(exception).isInstanceOf(ValidationException.class) - .hasMessage("Unable to find SomeDisplayName by uuid: " + GIVEN_UUID); + .hasMessage("Unable to find SomeDisplayName by s2.uuid: " + GIVEN_UUID); } @Test -- 2.39.5 From 08bce3d911f44dd754d76b7e13fd77d6f74598e9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 25 Sep 2024 17:41:52 +0200 Subject: [PATCH 15/21] add missing namePrefix to assertion --- .../HsOfficeSepaMandateControllerAcceptanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 026ce95c..b3adfa41 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -195,7 +195,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find BankAccount by uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find BankAccount by bankAccount.uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } @@ -225,7 +225,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find Debitor by uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find Debitor by debitor.uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } } -- 2.39.5 From 7c46fdb36d7de35640c8f694595e09ab34d21e48 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 06:31:57 +0200 Subject: [PATCH 16/21] try getReference instead of find in Mapper --- src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java | 3 +-- .../net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java index aabfdfd9..21779a5c 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Mapper.java @@ -87,8 +87,7 @@ abstract class Mapper extends ModelMapper { } public E fetchEntity(final String propertyName, final Class entityClass, final Object subEntityUuid) { - // using getReference would be more efficient, but results in very technical error messages - final var entity = em.find(entityClass, subEntityUuid); // FIXME: try getReference + final var entity = em.getReference(entityClass, subEntityUuid); if (entity != null) { return entity; } diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java index fa8823b3..b90c7cb1 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/MapperUnitTest.java @@ -50,7 +50,7 @@ class MapperUnitTest { @Test void mapsBeanWithExistingSubEntity() { final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build(); - when(em.find(SubTargetBean1.class, GIVEN_UUID)).thenReturn(new SubTargetBean1(GIVEN_UUID, "xxx")); + when(em.getReference(SubTargetBean1.class, GIVEN_UUID)).thenReturn(new SubTargetBean1(GIVEN_UUID, "xxx")); final var result = mapper.map(givenSource, TargetBean.class); assertThat(result).usingRecursiveComparison().isEqualTo( @@ -81,7 +81,7 @@ class MapperUnitTest { @Test void mapsBeanWithSubEntityNotFound() { final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s1(new SubSourceBean1(GIVEN_UUID)).build(); - when(em.find(SubTargetBean1.class, GIVEN_UUID)).thenReturn(null); + when(em.getReference(SubTargetBean1.class, GIVEN_UUID)).thenReturn(null); final var exception = catchThrowable(() -> mapper.map(givenSource, TargetBean.class) @@ -94,7 +94,7 @@ class MapperUnitTest { @Test void mapsBeanWithSubEntityNotFoundAndDisplayName() { final SourceBean givenSource = SourceBean.builder().a("1234").b("Text").s2(new SubSourceBean2(GIVEN_UUID)).build(); - when(em.find(SubTargetBean2.class, GIVEN_UUID)).thenReturn(null); + when(em.getReference(SubTargetBean2.class, GIVEN_UUID)).thenReturn(null); final var exception = catchThrowable(() -> mapper.map(givenSource, TargetBean.class) -- 2.39.5 From 9d5cf7dc39e0d84d902f4d360f0bfde607544119 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 07:52:54 +0200 Subject: [PATCH 17/21] start chapter Biggest Flaws in our Architecture in our README.md --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 37777227..e1d1515b 100644 --- a/README.md +++ b/README.md @@ -550,12 +550,37 @@ Dependency versions can be automatically upgraded to the latest available versio gw useLatestVersions ``` -Afterwards, `gw check` is automatically started. +Afterward, `gw check` is automatically started. Please only commit+push to master if the check run shows no errors. More infos, e.g. on blacklists see on the [project's website](https://github.com/patrikerdes/gradle-use-latest-versions-plugin). +## Biggest Flaws in our Architecture + +### The RBAC System is too Complicated + +Now, where we have a better experience with what we really need from the RBAC system, we have learned +that and creates too many (grant- and role-) rows and too even tables which could be avoided completely. + +The basic idea is always to always have a fixed set of ordered role-types which apply for all DB-tables under RBAC, +e.g. OWNER>ADMIN>AGENT\[>PROXY?\]>TENENT>REFERRER. +Grants between these for the same DB-row would be implicit by order comparision. +This way we would get rid of all explicit grants within the same DB-row +and would not need the `rbac.role` table anymore. +We would also reduce the depth of the expensive recursive CTE-query. + +This has to be explored further. +For now, we just keep it in mind and + +### The Mapper is Error-Prone + +Where `org.modelmapper.ModelMapper` reduces bloat-code a lot and has some nice features about recursive data-structure mappings, +it often causes strange errors which are hard to fix. +E.g. the uuid of the target main object is often taken from an uuid of a sub-subject. +(For now, use `StrictMapper` to avoid this, for the case it happens.) + + ## How To ... ### How to Configure .pgpass for the Default PostgreSQL Database? -- 2.39.5 From dc4cd818b42c0511ddc4be4d264290bd4ee805fc Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 08:49:53 +0200 Subject: [PATCH 18/21] improved error messages if entity not found --- .../hsadminng/errors/DisplayAs.java | 7 +++- .../debitor/HsOfficeDebitorController.java | 23 ++++------- .../persistence/EntityExistsValidator.java | 41 +++++++++++++++++++ ...OfficeDebitorControllerAcceptanceTest.java | 4 +- 4 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java diff --git a/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java b/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java index 020d006a..8eba1c13 100644 --- a/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java +++ b/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java @@ -11,13 +11,18 @@ import java.lang.annotation.Target; public @interface DisplayAs { class DisplayName { public static String of(final Class clazz) { - final var displayNameAnnot = clazz.getAnnotation(DisplayAs.class); + final var displayNameAnnot = getDisplayNameAnnotation(clazz); return displayNameAnnot != null ? displayNameAnnot.value() : clazz.getSimpleName(); } public static String of(@NotNull final Object instance) { return of(instance.getClass()); } + + private static DisplayAs getDisplayNameAnnotation(final Class clazz) { + final var annot = clazz.getAnnotation(DisplayAs.class); + return annot != null ? annot : getDisplayNameAnnotation(clazz.getSuperclass()); + } } String value() default ""; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java index a9109c13..0770aa35 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java @@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebito import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.mapper.StandardMapper; -import net.hostsharing.hsadminng.rbac.object.BaseEntity; +import net.hostsharing.hsadminng.persistence.EntityExistsValidator; import org.apache.commons.lang3.Validate; import org.hibernate.Hibernate; import org.springframework.beans.factory.annotation.Autowired; @@ -23,7 +23,6 @@ import jakarta.validation.ValidationException; import java.util.List; import java.util.UUID; -import static net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; @RestController @@ -42,6 +41,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { @Autowired private HsOfficeRelationRealRepository relrealRepo; + @Autowired + private EntityExistsValidator entityValidator; + @PersistenceContext private EntityManager em; @@ -85,9 +87,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { if ( body.getDebitorRel() != null ) { body.getDebitorRel().setType(DEBITOR.name()); final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class); - validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor()); - validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder()); - validateEntityExists("debitorRel.contactUuid", debitorRel.getContact()); + entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor()); + entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder()); + entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact()); entityToSave.setDebitorRel(relrealRepo.save(debitorRel)); } else { final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid()); @@ -160,15 +162,4 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { final var mapped = mapper.map(saved, HsOfficeDebitorResource.class); return ResponseEntity.ok(mapped); } - - // TODO.impl: extract this to some generally usable class? - private > T validateEntityExists(final String property, final T entitySkeleton) { - final var foundEntity = em.find(entitySkeleton.getClass(), entitySkeleton.getUuid()); - if ( foundEntity == null) { - throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid()); - } - - //noinspection unchecked - return (T) foundEntity; - } } diff --git a/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java b/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java new file mode 100644 index 00000000..df0e8637 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java @@ -0,0 +1,41 @@ +package net.hostsharing.hsadminng.persistence; + +import lombok.experimental.UtilityClass; +import net.hostsharing.hsadminng.errors.DisplayAs; +import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; +import net.hostsharing.hsadminng.rbac.object.BaseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.persistence.Entity; +import jakarta.validation.ValidationException; + +@Service +public class EntityExistsValidator { + + @Autowired + private EntityManagerWrapper em; + + public > void validateEntityExists(final String property, final T entitySkeleton) { + final var foundEntity = em.find(entityClass(entitySkeleton), entitySkeleton.getUuid()); + if ( foundEntity == null) { + throw new ValidationException("Unable to find " + DisplayName.of(entitySkeleton) + " by " + property + ": " + entitySkeleton.getUuid()); + } + } + + private static > Class entityClass(final T entityOrProxy) { + final var entityClass = entityClass(entityOrProxy.getClass()); + if (entityClass == null) { + throw new IllegalArgumentException("@Entity not found in superclass hierarchy of " + entityOrProxy.getClass()); + } + return entityClass; + } + + private static Class entityClass(final Class entityOrProxyClass) { + return entityOrProxyClass.isAnnotationPresent(Entity.class) + ? entityOrProxyClass + : entityOrProxyClass.getSuperclass() == null + ? null + : entityClass(entityOrProxyClass.getSuperclass()); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index 7f3b2281..d0954b6a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -405,7 +405,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu .post("http://localhost/api/hs/office/debitors") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contact.uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find RealContact by debitorRel.contactUuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } @@ -433,7 +433,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu .post("http://localhost/api/hs/office/debitors") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find RealRelation by debitorRel.uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find RealRelation by debitorRelUuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } } -- 2.39.5 From b1be470cadc505649304d9640b79efab0ab1d4ee Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 09:12:48 +0200 Subject: [PATCH 19/21] fix error message asserts --- .../HsOfficeSepaMandateControllerAcceptanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index b3adfa41..026ce95c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -195,7 +195,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find BankAccount by bankAccount.uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find BankAccount by uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } @@ -225,7 +225,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find Debitor by debitor.uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find Debitor by uuid: 00000000-0000-0000-0000-000000000000")); // @formatter:on } } -- 2.39.5 From b571a911f706990ef7910fd3a05c95b5c3189e2a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 09:23:17 +0200 Subject: [PATCH 20/21] fix error message asserts --- .../HsOfficeSepaMandateControllerAcceptanceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 026ce95c..89b25f35 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -195,7 +195,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find BankAccount by uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find BankAccount with uuid 00000000-0000-0000-0000-000000000000")); // @formatter:on } @@ -225,7 +225,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl .post("http://localhost/api/hs/office/sepamandates") .then().log().all().assertThat() .statusCode(400) - .body("message", is("ERROR: [400] Unable to find Debitor by uuid: 00000000-0000-0000-0000-000000000000")); + .body("message", is("ERROR: [400] Unable to find Debitor with uuid 00000000-0000-0000-0000-000000000000")); // @formatter:on } } -- 2.39.5 From 0f729503369e43eb8dffb383a74c560675cc277b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 09:48:12 +0200 Subject: [PATCH 21/21] fix NPE in DisplayAs --- .../java/net/hostsharing/hsadminng/errors/DisplayAs.java | 9 +++++++-- .../hsadminng/persistence/EntityExistsValidator.java | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java b/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java index 8eba1c13..20723330 100644 --- a/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java +++ b/src/main/java/net/hostsharing/hsadminng/errors/DisplayAs.java @@ -9,7 +9,9 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DisplayAs { + class DisplayName { + public static String of(final Class clazz) { final var displayNameAnnot = getDisplayNameAnnotation(clazz); return displayNameAnnot != null ? displayNameAnnot.value() : clazz.getSimpleName(); @@ -20,8 +22,11 @@ public @interface DisplayAs { } private static DisplayAs getDisplayNameAnnotation(final Class clazz) { - final var annot = clazz.getAnnotation(DisplayAs.class); - return annot != null ? annot : getDisplayNameAnnotation(clazz.getSuperclass()); + if (clazz == null) { + return null; + } + final var annot = clazz.getAnnotation(DisplayAs.class); + return annot != null ? annot : getDisplayNameAnnotation(clazz.getSuperclass()); } } diff --git a/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java b/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java index df0e8637..fac98d33 100644 --- a/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/persistence/EntityExistsValidator.java @@ -1,7 +1,5 @@ package net.hostsharing.hsadminng.persistence; -import lombok.experimental.UtilityClass; -import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs.DisplayName; import net.hostsharing.hsadminng.rbac.object.BaseEntity; import org.springframework.beans.factory.annotation.Autowired; -- 2.39.5