From 8fc81d8e2b3207c22aba4613692714e8dc721b0a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 29 Oct 2024 09:17:21 +0100 Subject: [PATCH] fixes HTTP GET with multiple values in result array --- .../debitor/HsOfficeDebitorController.java | 71 ++++++++++++------- .../reflection/AnnotationFinder.java | 25 +++---- .../2023-rbactest-package-rbac.sql | 6 +- .../2033-rbactest-domain-rbac.sql | 6 +- .../5033-hs-office-relation-rbac.sql | 6 +- .../5043-hs-office-partner-rbac.sql | 12 ++-- .../5063-hs-office-debitor-rbac.sql | 4 +- .../5073-hs-office-sepamandate-rbac.sql | 4 +- .../5103-hs-office-membership-rbac.sql | 2 +- .../5113-hs-office-coopshares-rbac.sql | 2 +- .../5123-hs-office-coopassets-rbac.sql | 2 +- .../6203-hs-booking-project-rbac.sql | 4 +- .../hs/office/scenarios/ScenarioTest.java | 25 +++++-- .../hs/office/scenarios/UUIDAppender.java | 34 +++++++++ .../hs/office/scenarios/UseCase.java | 23 +++++- .../debitor/CreateSelfDebitorForPartner.java | 6 +- .../scenarios/debitor/DeleteDebitor.java | 2 +- 17 files changed, 161 insertions(+), 73 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UUIDAppender.java 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 0770aa35..3a592907 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,31 +84,54 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi { "ERROR: [400] debitorRel.mark must be null"); final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class); - if ( body.getDebitorRel() != null ) { - body.getDebitorRel().setType(DEBITOR.name()); - final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class); - 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()); - debitorRelOptional.ifPresentOrElse( - debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));}, - () -> { throw new ValidationException("Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());}); + try { + if (body.getDebitorRel() != null) { + body.getDebitorRel().setType(DEBITOR.name()); + final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class); + 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()); + debitorRelOptional.ifPresentOrElse( + debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));}, + () -> { + throw new ValidationException( + "Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid()); + }); + } + + final var partnerRel = em.createNativeQuery(""" + SELECT partnerRel.* + FROM hs_office.relation AS partnerRel + JOIN hs_office.relation AS debitorRel + ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid + WHERE partnerRel.type = 'PARTNER' + AND :NEW_DebitorRelUuid = debitorRel.uuid + """).setParameter("NEW_DebitorRelUuid", entityToSave.getDebitorRel().getUuid()).getResultList(); + + + final var debitorRel = em.createNativeQuery(""" + SELECT debitorRel.* + FROM hs_office.relation AS debitorRel + WHERE :NEW_DebitorRelUuid = debitorRel.uuid + """).setParameter("NEW_DebitorRelUuid", entityToSave.getDebitorRel().getUuid()).getResultList(); + + final var savedEntity = debitorRepo.save(entityToSave); + em.flush(); + em.refresh(savedEntity); + + final var uri = + MvcUriComponentsBuilder.fromController(getClass()) + .path("/api/hs/office/debitors/{id}") + .buildAndExpand(savedEntity.getUuid()) + .toUri(); + final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class); + return ResponseEntity.created(uri).body(mapped); + } catch (final RuntimeException exc) { + throw exc; } - - final var savedEntity = debitorRepo.save(entityToSave); - em.flush(); - em.refresh(savedEntity); - - final var uri = - MvcUriComponentsBuilder.fromController(getClass()) - .path("/api/hs/office/debitors/{id}") - .buildAndExpand(savedEntity.getUuid()) - .toUri(); - final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class); - return ResponseEntity.created(uri).body(mapped); } @Override diff --git a/src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java b/src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java index 705e8326..9a626e43 100644 --- a/src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java +++ b/src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.reflection; +import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import java.lang.annotation.Annotation; @@ -11,32 +12,28 @@ import static java.util.Optional.empty; @UtilityClass public class AnnotationFinder { + @SneakyThrows public static Optional findCallerAnnotation( final Class annotationClassToFind, final Class annotationClassToStopLookup ) { for (var element : Thread.currentThread().getStackTrace()) { - try { - final var clazz = Class.forName(element.getClassName()); - final var method = getMethodFromStackElement(clazz, element); + final var clazz = Class.forName(element.getClassName()); + final var method = getMethodFromStackElement(clazz, element); - // Check if the method is annotated with the desired annotation - if (method != null) { - if (method.isAnnotationPresent(annotationClassToFind)) { - return Optional.of(method.getAnnotation(annotationClassToFind)); - } else if (method.isAnnotationPresent(annotationClassToStopLookup)) { - return empty(); - } + // Check if the method is annotated with the desired annotation + if (method != null) { + if (method.isAnnotationPresent(annotationClassToFind)) { + return Optional.of(method.getAnnotation(annotationClassToFind)); + } else if (method.isAnnotationPresent(annotationClassToStopLookup)) { + return empty(); } - } catch (final ClassNotFoundException | NoSuchMethodException e) { - throw new RuntimeException(e); // FIXME: when does this happen? } } return empty(); } - private static Method getMethodFromStackElement(Class clazz, StackTraceElement element) - throws NoSuchMethodException { + private static Method getMethodFromStackElement(Class clazz, StackTraceElement element) { for (var method : clazz.getDeclaredMethods()) { if (method.getName().equals(element.getMethodName())) { return method; diff --git a/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql b/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql index 2d2e9804..ac5604eb 100644 --- a/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql +++ b/src/main/resources/db/changelog/2-rbactest/202-rbactest-package/2023-rbactest-package-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer; - assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid); + assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid); perform rbac.defineRoleWithGrants( @@ -102,10 +102,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.customer WHERE uuid = OLD.customerUuid INTO oldCustomer; - assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid); + assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s of package', OLD.customerUuid); SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer; - assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid); + assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid); if NEW.customerUuid <> OLD.customerUuid then diff --git a/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql b/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql index f2195485..2fc0d2a5 100644 --- a/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql +++ b/src/main/resources/db/changelog/2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage; - assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid); perform rbac.defineRoleWithGrants( @@ -98,10 +98,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM rbactest.package WHERE uuid = OLD.packageUuid INTO oldPackage; - assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid); + assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s of domain', OLD.packageUuid); SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage; - assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid); if NEW.packageUuid <> OLD.packageUuid then diff --git a/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql index 5c100b33..08a395e0 100644 --- a/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql @@ -38,13 +38,13 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.person WHERE uuid = NEW.holderUuid INTO newHolderPerson; - assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); + assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s of relation', NEW.holderUuid); SELECT * FROM hs_office.person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; - assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); + assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s of relation', NEW.anchorUuid); SELECT * FROM hs_office.contact WHERE uuid = NEW.contactUuid INTO newContact; - assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); + assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s of relation', NEW.contactUuid); perform rbac.defineRoleWithGrants( diff --git a/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql index 765c0f10..bfe295fe 100644 --- a/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql @@ -37,10 +37,10 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid); SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; - assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'DELETE'), hs_office.relation_OWNER(newPartnerRel)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.relation_TENANT(newPartnerRel)); @@ -96,16 +96,16 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel; - assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid); + assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s of partner', OLD.partnerRelUuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid); SELECT * FROM hs_office.partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails; - assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid); + assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s of partner', OLD.detailsUuid); SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; - assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid); if NEW.partnerRelUuid <> OLD.partnerRelUuid then diff --git a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql index 746dd38f..6a65dd39 100644 --- a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql @@ -44,10 +44,10 @@ begin WHERE partnerRel.type = 'PARTNER' AND NEW.debitorRelUuid = debitorRel.uuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid); SELECT * FROM hs_office.relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid); SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount; diff --git a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql index 15e7c589..f22a826b 100644 --- a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql @@ -37,14 +37,14 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount; - assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s', NEW.bankAccountUuid); + assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s of sepamandate', NEW.bankAccountUuid); SELECT debitorRel.* FROM hs_office.relation debitorRel JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = NEW.debitorUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of sepamandate', NEW.debitorUuid); perform rbac.defineRoleWithGrants( diff --git a/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql index 41587e36..306dbced 100644 --- a/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql @@ -40,7 +40,7 @@ begin JOIN hs_office.relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid WHERE partner.uuid = NEW.partnerUuid INTO newPartnerRel; - assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid); + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s of membership', NEW.partnerUuid); perform rbac.defineRoleWithGrants( diff --git a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql index 911faa94..e7cc8811 100644 --- a/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership; - assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); + assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopshares', NEW.membershipUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership)); diff --git a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql index 1800b842..f5647823 100644 --- a/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql +++ b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql @@ -36,7 +36,7 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership; - assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); + assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopasset', NEW.membershipUuid); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership)); call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership)); diff --git a/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql b/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql index 88a83fbe..ade16515 100644 --- a/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql +++ b/src/main/resources/db/changelog/6-hs-booking/620-booking-project/6203-hs-booking-project-rbac.sql @@ -37,14 +37,14 @@ begin call rbac.enterTriggerForObjectUuid(NEW.uuid); SELECT * FROM hs_office.debitor WHERE uuid = NEW.debitorUuid INTO newDebitor; - assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s', NEW.debitorUuid); + assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s of project', NEW.debitorUuid); SELECT debitorRel.* FROM hs_office.relation debitorRel JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = NEW.debitorUuid INTO newDebitorRel; - assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid); + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s or project', NEW.debitorUuid); perform rbac.defineRoleWithGrants( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java index 48c0b208..1e7b11d0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java @@ -39,6 +39,8 @@ public abstract class ScenarioTest extends ContextBasedTest { @Getter private PrintWriter markdownFile; + private StringBuilder debugLog = new StringBuilder(); + record Alias>(Class useCase, UUID uuid) {} private final static Map> aliases = new HashMap<>(); @@ -57,22 +59,37 @@ public abstract class ScenarioTest extends ContextBasedTest { @BeforeEach void init(final TestInfo testInfo) { createHostsharingPerson(); - testInfo.getTestMethod().ifPresent(this::callRequiredProducers); - createTestLogMarkdownFile(testInfo); + try { + testInfo.getTestMethod().ifPresent(this::callRequiredProducers); + createTestLogMarkdownFile(testInfo); + } catch (Exception exc) { + throw exc; + } } @AfterEach void cleanup() { properties.clear(); - markdownFile.close(); + if (markdownFile != null) { + markdownFile.close(); + } else { + toString(); + } + debugLog = new StringBuilder(); } @SneakyThrows void print(final String output) { + + final var outputWithCommentsForUuids = UUIDAppender.appendUUIDKey(aliases, output.replace("+", "\\+")); + // for tests executed due to @Requires/@Produces there is no markdownFile yet if (markdownFile != null) { - markdownFile.print(output); + markdownFile.print(outputWithCommentsForUuids); } + + // but the debugLog should contain all output + debugLog.append(outputWithCommentsForUuids); } void printLine(final String output) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UUIDAppender.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UUIDAppender.java new file mode 100644 index 00000000..187d2857 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UUIDAppender.java @@ -0,0 +1,34 @@ +package net.hostsharing.hsadminng.hs.office.scenarios; + +import java.util.Map; + +public class UUIDAppender { + public static String appendUUIDKey(Map> uuidMap, String multilineText) { + // Split the multiline text into lines + String[] lines = multilineText.split("\\r?\\n"); + + // StringBuilder to build the resulting multiline text + StringBuilder result = new StringBuilder(); + + // Iterate over each line + for (String line : lines) { + + // Iterate over the map to find if the line contains a UUID + for (Map.Entry> entry : uuidMap.entrySet()) { + String uuidString = entry.getValue().uuid().toString(); + + // If the line contains the UUID, append the key at the end + if (line.contains(uuidString)) { + line = line + " // " + entry.getKey(); + break; // Exit once we've matched one UUID + } + } + + // Append the (possibly modified) line to the result + result.append(line).append("\n"); + } + + // Return the final modified text + return result.toString(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 189f4ee9..12d75732 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.office.scenarios; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.restassured.http.ContentType; @@ -17,10 +18,10 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; -import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Function; @@ -67,7 +68,11 @@ public abstract class UseCase> { |------|-------|"""); givenProperties.forEach((key, value) -> printLine("| " + key + " | " + value.toString().replace("\n", "
") + " |")); printLine(""); - requirements.forEach((alias, factory) -> factory.apply(alias).run().keep()); + requirements.forEach((alias, factory) -> { + if (!ScenarioTest.containsAlias(alias)) { + factory.apply(alias).run().keep(); + } + }); return run(); } protected abstract HttpResponse run(); @@ -144,7 +149,7 @@ public abstract class UseCase> { .timeout(Duration.ofSeconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); - return new HttpResponse(HttpMethod.PATCH, uriPath, null, response); + return new HttpResponse(HttpMethod.DELETE, uriPath, null, response); } public final UUID uuid(final String alias) { @@ -240,8 +245,20 @@ public abstract class UseCase> { new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid)); } + @SneakyThrows + public HttpResponse expectArrayElements(final int expectedElementCount) { + final var rootNode = objectMapper.readTree(response.body()); + assertThat(rootNode.isArray()).as("array expected, but got: " + response.body()).isTrue(); + + final var root = (List) objectMapper.readValue(response.body(), new TypeReference>() { + }); + assertThat(root.size()).as("unexpected number of array elements").isEqualTo(expectedElementCount); + return this; + } + @SneakyThrows public String getFromBody(final String path) { + // FIXME: use JsonPath: https://www.baeldung.com/guide-to-jayway-jsonpath final var rootNode = objectMapper.readTree(response.body()); return getPropertyFromJson(rootNode, path); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java index 2fa18d7c..bbeb43e4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java @@ -16,9 +16,9 @@ public class CreateSelfDebitorForPartner extends UseCase - httpGet("/api/hs/office/relations?personData=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerPersonTradeName}")) .expecting(OK).expecting(JSON), - response -> response.getFromBody("[0].holder.uuid") + response -> response.expectArrayElements(1).getFromBody("[0].holder.uuid") ); printPara("From that output above, we're taking the UUID of the holder of the first result element."); printPara("**HINT**: With production data, you might get multiple results and have to decide which is the right one."); @@ -49,7 +49,7 @@ public class CreateSelfDebitorForPartner extends UseCase { super(testSuite); requires("Debitor: Test AG - delete debitor", alias -> new CreateSelfDebitorForPartner(testSuite, alias) - .given("partnerPersonUuid", "%{Person: Test AG}") + .given("partnerPersonTradeName", "Test AG") .given("billingContactCaption", "Test AG - billing department") .given("billingContactEmailAddress", "billing@test-ag.example.org") .given("debitorNumberSuffix", "%{debitorSuffix}")