From d189d2670cf2de31d7999f0a65689855bef752ea Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 26 Sep 2024 19:42:29 +0200 Subject: [PATCH 1/3] add application event for newly created booking items and create domain setup hosting asset (WIP) --- .../booking/item/BookingItemCreatedEvent.java | 16 +++++ .../booking/item/HsBookingItemController.java | 9 ++- .../BookingItemEntitySaveProcessor.java | 5 ++ .../HsDomainSetupBookingItemValidator.java | 5 ++ .../asset/HsBookingItemCreatedListener.java | 51 ++++++++++++++++ .../hs-booking/hs-booking-item-schemas.yaml | 1 + ...HsBookingItemControllerAcceptanceTest.java | 59 +++++++++++++++++++ ...mainSetupBookingItemValidatorUnitTest.java | 14 +++-- 8 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/booking/item/BookingItemCreatedEvent.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/BookingItemCreatedEvent.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/BookingItemCreatedEvent.java new file mode 100644 index 00000000..bea6c9ae --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/BookingItemCreatedEvent.java @@ -0,0 +1,16 @@ +package net.hostsharing.hsadminng.hs.booking.item; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import jakarta.validation.constraints.NotNull; + +@Getter +public class BookingItemCreatedEvent extends ApplicationEvent { + private final @NotNull HsBookingItem newBookingItem; + + public BookingItemCreatedEvent(@NotNull HsBookingItemController source, @NotNull final HsBookingItem newBookingItem) { + super(source); + this.newBookingItem = newBookingItem; + } +} 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 84f35054..b3e3250e 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 @@ -12,6 +12,7 @@ import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RestController; @@ -33,6 +34,9 @@ public class HsBookingItemController implements HsBookingItemsApi { @Autowired private StrictMapper mapper; + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + @Autowired private HsBookingItemRbacRepository bookingItemRepo; @@ -63,7 +67,8 @@ public class HsBookingItemController implements HsBookingItemsApi { context.define(currentSubject, assumedRoles); final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); - final var mapped = new BookingItemEntitySaveProcessor(em, entityToSave) + final var saveProcessor = new BookingItemEntitySaveProcessor(em, entityToSave); + final var mapped = saveProcessor .preprocessEntity() .validateEntity() .prepareForSave() @@ -72,6 +77,8 @@ public class HsBookingItemController implements HsBookingItemsApi { .mapUsing(e -> mapper.map(e, HsBookingItemResource.class, ITEM_TO_RESOURCE_POSTMAPPER)) .revampProperties(); + applicationEventPublisher.publishEvent(new BookingItemCreatedEvent(this, saveProcessor.getEntity())); + final var uri = MvcUriComponentsBuilder.fromController(getClass()) .path("/api/hs/booking/items/{id}") 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 index 77ce40ae..1e712ad3 100644 --- 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 @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; +import lombok.Getter; 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; @@ -20,7 +21,11 @@ public class BookingItemEntitySaveProcessor { private final HsEntityValidator validator; private String expectedStep = "preprocessEntity"; private final EntityManager em; + + @Getter private HsBookingItem entity; + + @Getter private HsBookingItemResource resource; public BookingItemEntitySaveProcessor(final EntityManager em, final HsBookingItem entity) { 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 f42ea4e0..266ff641 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 @@ -55,6 +55,11 @@ class HsDomainSetupBookingItemValidator extends HsBookingItemEntityValidator { } private static String generateVerificationCode(final EntityManager em, final PropertiesProvider propertiesProvider) { + final var userDefinedVerificationCode = propertiesProvider.getDirectValue(VERIFICATION_CODE_PROPERTY_NAME, String.class); + if (userDefinedVerificationCode != null) { + return userDefinedVerificationCode; + } + final var alphaNumeric = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; final var secureRandom = new SecureRandom(); final var sb = new StringBuilder(); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java new file mode 100644 index 00000000..4dcc1f63 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java @@ -0,0 +1,51 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedEvent; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; +import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class HsBookingItemCreatedListener implements ApplicationListener { + + @Autowired + private EntityManagerWrapper emw; + + @Override + public void onApplicationEvent(final BookingItemCreatedEvent event) { + System.out.println("Received newly created booking item: " + event.getNewBookingItem()); + final var newBookingItemRealEntity = + emw.getReference(HsBookingItemRealEntity.class, event.getNewBookingItem().getUuid()); + final var newHostingAsset = switch (newBookingItemRealEntity.getType()) { + case PRIVATE_CLOUD -> null; + case CLOUD_SERVER -> null; + case MANAGED_SERVER -> null; + case MANAGED_WEBSPACE -> null; + case DOMAIN_SETUP -> createDomainSetupHostingAsset(newBookingItemRealEntity); + }; + if (newHostingAsset != null) { + new HostingAssetEntitySaveProcessor(emw, newHostingAsset) + .preprocessEntity() + .validateEntity() + .prepareForSave() + .save() + .validateContext(); + } + } + + private HsHostingAsset createDomainSetupHostingAsset(final HsBookingItemRealEntity fromBookingItem) { + return HsHostingAssetRbacEntity.builder() + .bookingItem(fromBookingItem) + .type(HsHostingAssetType.DOMAIN_SETUP) + .identifier(fromBookingItem.getDirectValue("domainName", String.class)) + .subHostingAssets(List.of( + // TARGET_UNIX_USER_PROPERTY_NAME + )) + .build(); + } +} diff --git a/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml b/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml index b18c7356..92875b90 100644 --- a/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml +++ b/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml @@ -10,6 +10,7 @@ components: - CLOUD_SERVER - MANAGED_SERVER - MANAGED_WEBSPACE + - DOMAIN_SETUP HsBookingItem: type: object diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 92f35895..4649b611 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -5,6 +5,7 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository; +import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -180,6 +181,64 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup location.substring(location.lastIndexOf('/') + 1)); assertThat(newSubjectUuid).isNotNull(); } + + @Test + void globalAdmin_canAddBookingItemWithHostingAsset() { + + context.define("superuser-alex@hostsharing.net"); + final var givenProject = debitorRepo.findDebitorByDebitorNumber(1000111).stream() + .map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid())) + .flatMap(List::stream) + .findFirst() + .orElseThrow(); + + Dns.fakeResultForDomain("example.org", + Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code")); + + final var location = RestAssured // @formatter:off + .given() + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(ContentType.JSON) + .body(""" + { + "projectUuid": "{projectUuid}", + "type": "DOMAIN_SETUP", + "caption": "some new domain-setup booking", + "resources": { + "domainName": "example.org", + "targetUnixUser": "fir01-web", + "verificationCode": "just-a-fake-verification-code" + } + } + """ + .replace("{projectUuid}", givenProject.getUuid().toString()) + ) + .port(port) + .when() + .post("http://localhost/api/hs/booking/items") + .then().log().all().assertThat() + .statusCode(201) + .contentType(ContentType.JSON) + .body("", lenientlyEquals(""" + { + "type": "DOMAIN_SETUP", + "caption": "some new domain-setup booking", + "validFrom": "{today}", + "validTo": null, + "resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" } + } + """ + .replace("{today}", LocalDate.now().toString()) + .replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString())) + ) + .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*")) + .extract().header("Location"); // @formatter:on + + // finally, the new bookingItem can be accessed under the generated UUID + final var newSubjectUuid = UUID.fromString( + location.substring(location.lastIndexOf('/') + 1)); + assertThat(newSubjectUuid).isNotNull(); + } } @Nested diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java index 52a63509..607a8e07 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -14,6 +15,7 @@ import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.DOMAIN_SETUP; import static org.apache.commons.lang3.StringUtils.right; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; class HsDomainSetupBookingItemValidatorUnitTest { @@ -41,10 +43,12 @@ class HsDomainSetupBookingItemValidatorUnitTest { .build(); // when - final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity); + final var thrown = catchThrowable(() -> { + new BookingItemEntitySaveProcessor(em, domainSetupBookingItemEntity).preprocessEntity().validateEntity(); + }); // then - assertThat(result).isEmpty(); + assertThat(thrown).isNull(); } @Test @@ -62,10 +66,12 @@ class HsDomainSetupBookingItemValidatorUnitTest { .build(); // when - final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity); + final var thrown = catchThrowable(() -> { + new BookingItemEntitySaveProcessor(em, domainSetupBookingItemEntity).preprocessEntity().validateEntity(); + }); // then - assertThat(result).isEmpty(); + assertThat(thrown).isNull(); } @Test -- 2.39.5 From 4c0b4a2c1e35b8f33e3b853601a239253f8274f1 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 27 Sep 2024 06:38:01 +0200 Subject: [PATCH 2/3] handle+test failure creating HostingAsset from BookingItem (here: verificationCode missing) --- .../asset/HsBookingItemCreatedListener.java | 17 ++- ...HsBookingItemControllerAcceptanceTest.java | 106 ++++++++++++++++-- ...mainSetupBookingItemValidatorUnitTest.java | 1 - 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java index 4dcc1f63..c625076a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsBookingItemCreatedListener.java @@ -29,12 +29,17 @@ public class HsBookingItemCreatedListener implements ApplicationListener createDomainSetupHostingAsset(newBookingItemRealEntity); }; if (newHostingAsset != null) { - new HostingAssetEntitySaveProcessor(emw, newHostingAsset) - .preprocessEntity() - .validateEntity() - .prepareForSave() - .save() - .validateContext(); + try { + new HostingAssetEntitySaveProcessor(emw, newHostingAsset) + .preprocessEntity() + .validateEntity() + .prepareForSave() + .save() + .validateContext(); + } catch (final Exception e) { + // TODO.impl: store status in a separate field, maybe enum+message + newBookingItemRealEntity.getResources().put("status", e.getMessage()); + } } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 4649b611..91ee81a9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -4,9 +4,11 @@ import io.hypersistence.utils.hibernate.type.range.Range; import io.restassured.RestAssured; import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; +import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealRepository; import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns; -import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import org.junit.jupiter.api.ClassOrderer; @@ -51,7 +53,10 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup HsBookingProjectRealRepository realProjectRepo; @Autowired - HsOfficeDebitorRepository debitorRepo; + HsBookingDebitorRepository debitorRepo; + + @Autowired + HsHostingAssetRealRepository realHostingAssetRepo; @Autowired JpaAttempt jpaAttempt; @@ -65,7 +70,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup // given context("superuser-alex@hostsharing.net"); - final var givenProject = debitorRepo.findDebitorByDebitorNumber(1000111).stream() + final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream() .map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid())) .flatMap(List::stream) .findFirst() @@ -133,7 +138,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup void globalAdmin_canAddBookingItem() { context.define("superuser-alex@hostsharing.net"); - final var givenProject = debitorRepo.findDebitorByDebitorNumber(1000111).stream() + final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream() .map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid())) .flatMap(List::stream) .findFirst() @@ -183,10 +188,10 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup } @Test - void globalAdmin_canAddBookingItemWithHostingAsset() { + void projectAgent_canAddBookingItemWithHostingAsset() { - context.define("superuser-alex@hostsharing.net"); - final var givenProject = debitorRepo.findDebitorByDebitorNumber(1000111).stream() + context.define("superuser-alex@hostsharing.net", "hs_booking.project#D-1000111-D-1000111defaultproject:AGENT"); + final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream() .map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid())) .flatMap(List::stream) .findFirst() @@ -234,10 +239,80 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*")) .extract().header("Location"); // @formatter:on - // finally, the new bookingItem can be accessed under the generated UUID - final var newSubjectUuid = UUID.fromString( - location.substring(location.lastIndexOf('/') + 1)); - assertThat(newSubjectUuid).isNotNull(); + // then, the new BookingItem can be accessed under the generated UUID + final var newBookingItem = fetchRealBookingItemFromURI(location); + assertThat(newBookingItem) + .extracting(bi -> bi.getDirectValue("domainName", String.class)) + .isEqualTo("example.org"); + + // and the related HostingAsset also got created + assertThat(realHostingAssetRepo.findByIdentifier("example.org")).isNotEmpty() + .map(HsHostingAsset::getBookingItem) + .contains(newBookingItem); + } + + @Test + void projectAgent_canAddBookingItemEvenIfHostingAssetCreationFails() { + + context.define("superuser-alex@hostsharing.net", "hs_booking.project#D-1000111-D-1000111defaultproject:AGENT"); + final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream() + .map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid())) + .flatMap(List::stream) + .findFirst() + .orElseThrow(); + + Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords()); // without valid verificationCode + + final var location = RestAssured // @formatter:off + .given() + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(ContentType.JSON) + .body(""" + { + "projectUuid": "{projectUuid}", + "type": "DOMAIN_SETUP", + "caption": "some new domain-setup booking", + "resources": { + "domainName": "example.org", + "targetUnixUser": "fir01-web", + "verificationCode": "just-a-fake-verification-code" + } + } + """ + .replace("{projectUuid}", givenProject.getUuid().toString()) + ) + .port(port) + .when() + .post("http://localhost/api/hs/booking/items") + .then().log().all().assertThat() + .statusCode(201) + .contentType(ContentType.JSON) + .body("", lenientlyEquals(""" + { + "type": "DOMAIN_SETUP", + "caption": "some new domain-setup booking", + "validFrom": "{today}", + "validTo": null, + "resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" } + } + """ + .replace("{today}", LocalDate.now().toString()) + .replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString())) + ) + .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*")) + .extract().header("Location"); // @formatter:on + + // then, the new BookingItem can be accessed under the generated UUID + final var newBookingItem = fetchRealBookingItemFromURI(location); + assertThat(newBookingItem) + .extracting(bi -> bi.getDirectValue("domainName", String.class)) + .isEqualTo("example.org"); + assertThat(newBookingItem) + .extracting(bi -> bi.getDirectValue("status", String.class)) + .isEqualTo("[[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=just-a-fake-verification-code' found for domain name 'example.org' (nor in its super-domain)]"); + + // but the related HostingAsset did not get created + assertThat(realHostingAssetRepo.findByIdentifier("example.org")).isEmpty(); } } @@ -464,4 +539,13 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup private Map.Entry resource(final String key, final Object value) { return entry(key, value); } + + private HsBookingItemRealEntity fetchRealBookingItemFromURI(final String location) { + final var newBookingItemUuid = UUID.fromString( + location.substring(location.lastIndexOf('/') + 1)); + assertThat(newBookingItemUuid).isNotNull(); + final var optional = realBookingItemRepo.findByUuid(newBookingItemUuid); + assertThat(optional).isNotEmpty(); + return optional.get(); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java index 607a8e07..66d77899 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsDomainSetupBookingItemValidatorUnitTest.java @@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -- 2.39.5 From 8dc388fa5a7ca313c7900e526f8a420a94c0a085 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 27 Sep 2024 11:00:33 +0200 Subject: [PATCH 3/3] use fetchRealBookingItemFromURI wherever sensible and improve formatting --- ...HsBookingItemControllerAcceptanceTest.java | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 91ee81a9..cf43f8cb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -182,9 +182,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup .extract().header("Location"); // @formatter:on // finally, the new bookingItem can be accessed under the generated UUID - final var newSubjectUuid = UUID.fromString( - location.substring(location.lastIndexOf('/') + 1)); - assertThat(newSubjectUuid).isNotNull(); + assertThat(fetchRealBookingItemFromURI(location)).isNotNull(); } @Test @@ -220,24 +218,24 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup ) .port(port) .when() - .post("http://localhost/api/hs/booking/items") + .post("http://localhost/api/hs/booking/items") .then().log().all().assertThat() - .statusCode(201) - .contentType(ContentType.JSON) - .body("", lenientlyEquals(""" - { - "type": "DOMAIN_SETUP", - "caption": "some new domain-setup booking", - "validFrom": "{today}", - "validTo": null, - "resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" } - } - """ - .replace("{today}", LocalDate.now().toString()) - .replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString())) - ) - .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*")) - .extract().header("Location"); // @formatter:on + .statusCode(201) + .contentType(ContentType.JSON) + .body("", lenientlyEquals(""" + { + "type": "DOMAIN_SETUP", + "caption": "some new domain-setup booking", + "validFrom": "{today}", + "validTo": null, + "resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" } + } + """ + .replace("{today}", LocalDate.now().toString()) + .replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString())) + ) + .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*")) + .extract().header("Location"); // @formatter:on // then, the new BookingItem can be accessed under the generated UUID final var newBookingItem = fetchRealBookingItemFromURI(location); -- 2.39.5