From cb29730810012d9120f37cd7fda9f677fe1761e8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 20 Jun 2024 16:04:40 +0200 Subject: [PATCH] add alertContact to REST Schema, patcher and acceptance-test --- .../asset/HsHostingAssetController.java | 7 ++++- .../hosting/asset/HsHostingAssetEntity.java | 2 +- .../asset/HsHostingAssetEntityPatcher.java | 17 +++++++++++- .../hs-hosting/hs-hosting-asset-schemas.yaml | 10 +++++++ ...sHostingAssetControllerAcceptanceTest.java | 26 +++++++++++++++++-- .../HsHostingAssetEntityPatcherUnitTest.java | 26 ++++++++++++++++--- 6 files changed, 79 insertions(+), 9 deletions(-) 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 d3578833..b7982328 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 @@ -16,7 +16,9 @@ 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.EntityNotFoundException; +import jakarta.persistence.PersistenceContext; import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; @@ -26,6 +28,9 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAss @RestController public class HsHostingAssetController implements HsHostingAssetsApi { + @PersistenceContext + private EntityManager em; + @Autowired private Context context; @@ -119,7 +124,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { final var current = assetRepo.findByUuid(assetUuid).orElseThrow(); - new HsHostingAssetEntityPatcher(current).apply(body); + new HsHostingAssetEntityPatcher(em, current).apply(body); final var saved = validated(assetRepo.save(current)); final var mapped = mapper.map(saved, HsHostingAssetResource.class); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index a99b4f2a..dfd1d363 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -100,7 +100,7 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "alarmcontactuuid") - private HsHostingContactEntity alarmContactUuid; + private HsHostingContactEntity alarmContact; @OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY) @JoinColumn(name="parentassetuuid", referencedColumnName="uuid") diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcher.java index a555be19..adbcdcaf 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcher.java @@ -1,17 +1,22 @@ package net.hostsharing.hsadminng.hs.hosting.asset; +import net.hostsharing.hsadminng.hs.hosting.contact.HsHostingContactEntity; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetPatchResource; import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.OptionalFromJson; +import jakarta.persistence.EntityManager; import java.util.Optional; +import java.util.UUID; public class HsHostingAssetEntityPatcher implements EntityPatcher { + private final EntityManager em; private final HsHostingAssetEntity entity; - public HsHostingAssetEntityPatcher(final HsHostingAssetEntity entity) { + HsHostingAssetEntityPatcher(final EntityManager em, final HsHostingAssetEntity entity) { + this.em = em; this.entity = entity; } @@ -21,5 +26,15 @@ public class HsHostingAssetEntityPatcher implements EntityPatcher entity.getConfig().patch(KeyValueMap.from(resource.getConfig()))); + OptionalFromJson.of(resource.getAlarmContactUuid()).ifPresent(newValue -> { + verifyNotNull(newValue, "alarmContact"); + entity.setAlarmContact(em.getReference(HsHostingContactEntity.class, newValue)); + }); + } + + private void verifyNotNull(final UUID newValue, final String propertyName) { + if (newValue == null) { + throw new IllegalArgumentException("property '" + propertyName + "' must not be null"); + } } } diff --git a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml index 8e9dbe02..934c9647 100644 --- a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml +++ b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml @@ -32,6 +32,8 @@ components: type: string caption: type: string + alarmContact: + $ref: '../hs-office/hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' config: $ref: '#/components/schemas/HsHostingAssetConfiguration' required: @@ -46,6 +48,10 @@ components: caption: type: string nullable: true + alarmContactUuid: + type: string + format: uuid + nullable: true config: $ref: '#/components/schemas/HsHostingAssetConfiguration' @@ -72,6 +78,10 @@ components: minLength: 3 maxLength: 80 nullable: false + alarmContactUuid: + type: string + format: uuid + nullable: true config: $ref: '#/components/schemas/HsHostingAssetConfiguration' required: diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java index 84fe1627..d2885cfd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java @@ -7,6 +7,8 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRepository; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRepository; +import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; @@ -54,6 +56,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup @Autowired HsOfficeDebitorRepository debitorRepo; + @Autowired + HsOfficeContactRepository contactRepo; + @Autowired JpaAttempt jpaAttempt; @@ -425,6 +430,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup final var givenAsset = givenSomeTemporaryHostingAsset("2001", MANAGED_SERVER, config("monit_max_ssd_usage", 80), config("monit_max_hdd_usage", 90), config("monit_max_cpu_usage", 90), config("monit_max_ram_usage", 70)); + final var alarmContactUuid = givenContact().getUuid(); RestAssured // @formatter:off .given() @@ -432,13 +438,14 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup .contentType(ContentType.JSON) .body(""" { + "alarmContactUuid": "%s", "config": { "monit_max_ssd_usage": 85, "monit_max_hdd_usage": null, "monit_min_free_ssd": 5 } } - """) + """.formatted(alarmContactUuid)) .port(port) .when() .patch("http://localhost/api/hs/hosting/assets/" + givenAsset.getUuid()) @@ -450,6 +457,11 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup "type": "MANAGED_SERVER", "identifier": "vm2001", "caption": "some test-asset", + "alarmContact": { + "uuid": "%s", + "caption": "second contact", + "emailAddresses": { "main": "contact-admin@secondcontact.example.com" } + }, "config": { "monit_max_cpu_usage": 90, "monit_max_ram_usage": 70, @@ -457,12 +469,15 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup "monit_min_free_ssd": 5 } } - """)); // @formatter:on + """.formatted(alarmContactUuid))); + // @formatter:on // finally, the asset is actually updated context.define("superuser-alex@hostsharing.net"); assertThat(assetRepo.findByUuid(givenAsset.getUuid())).isPresent().get() .matches(asset -> { + assertThat(asset.getAlarmContact().toString()).isEqualTo( + "contact(caption='second contact', emailAddresses='{main=contact-admin@secondcontact.example.com}')"); assertThat(asset.getConfig().toString()).isEqualTo( "{ monit_max_cpu_usage: 90, monit_max_ram_usage: 70, monit_max_ssd_usage: 85, monit_min_free_ssd: 5 }"); return true; @@ -470,6 +485,13 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup } } + private HsOfficeContactEntity givenContact() { + return jpaAttempt.transacted(() -> { + context.define("superuser-alex@hostsharing.net"); + return contactRepo.findContactByOptionalCaptionLike("second").stream().findFirst().orElseThrow(); + }).returnedValue(); + } + @Nested @Order(5) class DeleteAsset { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcherUnitTest.java index 2530f5fa..e36755c8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityPatcherUnitTest.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset; +import net.hostsharing.hsadminng.hs.hosting.contact.HsHostingContactEntity; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetPatchResource; -import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase; import org.junit.jupiter.api.BeforeEach; @@ -31,6 +31,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< > { private static final UUID INITIAL_BOOKING_ITEM_UUID = UUID.randomUUID(); + private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID(); private static final Map INITIAL_CONFIG = patchMap( entry("CPU", 1), @@ -47,6 +48,9 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< entry("SSD", 256), entry("MEM", 64) ); + final HsHostingContactEntity givenInitialContact = HsHostingContactEntity.builder() + .uuid(UUID.randomUUID()) + .build(); private static final String INITIAL_CAPTION = "initial caption"; private static final String PATCHED_CAPTION = "patched caption"; @@ -56,10 +60,12 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< @BeforeEach void initMocks() { - lenient().when(em.getReference(eq(HsOfficeDebitorEntity.class), any())).thenAnswer(invocation -> - HsOfficeDebitorEntity.builder().uuid(invocation.getArgument(1)).build()); +// lenient().when(em.getReference(eq(HsOfficeDebitorEntity.class), any())).thenAnswer(invocation -> +// HsOfficeDebitorEntity.builder().uuid(invocation.getArgument(1)).build()); lenient().when(em.getReference(eq(HsHostingAssetEntity.class), any())).thenAnswer(invocation -> HsHostingAssetEntity.builder().uuid(invocation.getArgument(1)).build()); + lenient().when(em.getReference(eq(HsHostingContactEntity.class), any())).thenAnswer(invocation -> + HsHostingContactEntity.builder().uuid(invocation.getArgument(1)).build()); } @Override @@ -69,6 +75,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< entity.setBookingItem(TEST_BOOKING_ITEM); entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG)); entity.setCaption(INITIAL_CAPTION); + entity.setAlarmContact(givenInitialContact); return entity; } @@ -79,7 +86,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< @Override protected HsHostingAssetEntityPatcher createPatcher(final HsHostingAssetEntity server) { - return new HsHostingAssetEntityPatcher(server); + return new HsHostingAssetEntityPatcher(em, server); } @Override @@ -96,7 +103,18 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase< PATCH_CONFIG, HsHostingAssetEntity::putConfig, PATCHED_CONFIG) + .notNullable(), + new JsonNullableProperty<>( + "alarmContact", + HsHostingAssetPatchResource::setAlarmContactUuid, + PATCHED_CONTACT_UUID, + HsHostingAssetEntity::setAlarmContact, + newContact(PATCHED_CONTACT_UUID)) .notNullable() ); } + + static HsHostingContactEntity newContact(final UUID uuid) { + return HsHostingContactEntity.builder().uuid(uuid).build(); + } }