add alertContact to REST Schema, patcher and acceptance-test

This commit is contained in:
Michael Hoennig 2024-06-20 16:04:40 +02:00
parent 9c43610e7c
commit cb29730810
6 changed files with 79 additions and 9 deletions

View File

@ -16,7 +16,9 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -26,6 +28,9 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAss
@RestController @RestController
public class HsHostingAssetController implements HsHostingAssetsApi { public class HsHostingAssetController implements HsHostingAssetsApi {
@PersistenceContext
private EntityManager em;
@Autowired @Autowired
private Context context; private Context context;
@ -119,7 +124,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
final var current = assetRepo.findByUuid(assetUuid).orElseThrow(); 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 saved = validated(assetRepo.save(current));
final var mapped = mapper.map(saved, HsHostingAssetResource.class); final var mapped = mapper.map(saved, HsHostingAssetResource.class);

View File

@ -100,7 +100,7 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject {
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "alarmcontactuuid") @JoinColumn(name = "alarmcontactuuid")
private HsHostingContactEntity alarmContactUuid; private HsHostingContactEntity alarmContact;
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY) @OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name="parentassetuuid", referencedColumnName="uuid") @JoinColumn(name="parentassetuuid", referencedColumnName="uuid")

View File

@ -1,17 +1,22 @@
package net.hostsharing.hsadminng.hs.hosting.asset; 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.hosting.generated.api.v1.model.HsHostingAssetPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.KeyValueMap;
import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import jakarta.persistence.EntityManager;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
public class HsHostingAssetEntityPatcher implements EntityPatcher<HsHostingAssetPatchResource> { public class HsHostingAssetEntityPatcher implements EntityPatcher<HsHostingAssetPatchResource> {
private final EntityManager em;
private final HsHostingAssetEntity entity; private final HsHostingAssetEntity entity;
public HsHostingAssetEntityPatcher(final HsHostingAssetEntity entity) { HsHostingAssetEntityPatcher(final EntityManager em, final HsHostingAssetEntity entity) {
this.em = em;
this.entity = entity; this.entity = entity;
} }
@ -21,5 +26,15 @@ public class HsHostingAssetEntityPatcher implements EntityPatcher<HsHostingAsset
.ifPresent(entity::setCaption); .ifPresent(entity::setCaption);
Optional.ofNullable(resource.getConfig()) Optional.ofNullable(resource.getConfig())
.ifPresent(r -> entity.getConfig().patch(KeyValueMap.from(resource.getConfig()))); .ifPresent(r -> 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");
}
} }
} }

View File

@ -32,6 +32,8 @@ components:
type: string type: string
caption: caption:
type: string type: string
alarmContact:
$ref: '../hs-office/hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
config: config:
$ref: '#/components/schemas/HsHostingAssetConfiguration' $ref: '#/components/schemas/HsHostingAssetConfiguration'
required: required:
@ -46,6 +48,10 @@ components:
caption: caption:
type: string type: string
nullable: true nullable: true
alarmContactUuid:
type: string
format: uuid
nullable: true
config: config:
$ref: '#/components/schemas/HsHostingAssetConfiguration' $ref: '#/components/schemas/HsHostingAssetConfiguration'
@ -72,6 +78,10 @@ components:
minLength: 3 minLength: 3
maxLength: 80 maxLength: 80
nullable: false nullable: false
alarmContactUuid:
type: string
format: uuid
nullable: true
config: config:
$ref: '#/components/schemas/HsHostingAssetConfiguration' $ref: '#/components/schemas/HsHostingAssetConfiguration'
required: required:

View File

@ -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.HsBookingItemRepository;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRepository; 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.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
@ -54,6 +56,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
@Autowired @Autowired
HsOfficeDebitorRepository debitorRepo; HsOfficeDebitorRepository debitorRepo;
@Autowired
HsOfficeContactRepository contactRepo;
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@ -425,6 +430,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
final var givenAsset = givenSomeTemporaryHostingAsset("2001", MANAGED_SERVER, 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)); 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 RestAssured // @formatter:off
.given() .given()
@ -432,13 +438,14 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"alarmContactUuid": "%s",
"config": { "config": {
"monit_max_ssd_usage": 85, "monit_max_ssd_usage": 85,
"monit_max_hdd_usage": null, "monit_max_hdd_usage": null,
"monit_min_free_ssd": 5 "monit_min_free_ssd": 5
} }
} }
""") """.formatted(alarmContactUuid))
.port(port) .port(port)
.when() .when()
.patch("http://localhost/api/hs/hosting/assets/" + givenAsset.getUuid()) .patch("http://localhost/api/hs/hosting/assets/" + givenAsset.getUuid())
@ -450,6 +457,11 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
"type": "MANAGED_SERVER", "type": "MANAGED_SERVER",
"identifier": "vm2001", "identifier": "vm2001",
"caption": "some test-asset", "caption": "some test-asset",
"alarmContact": {
"uuid": "%s",
"caption": "second contact",
"emailAddresses": { "main": "contact-admin@secondcontact.example.com" }
},
"config": { "config": {
"monit_max_cpu_usage": 90, "monit_max_cpu_usage": 90,
"monit_max_ram_usage": 70, "monit_max_ram_usage": 70,
@ -457,12 +469,15 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
"monit_min_free_ssd": 5 "monit_min_free_ssd": 5
} }
} }
""")); // @formatter:on """.formatted(alarmContactUuid)));
// @formatter:on
// finally, the asset is actually updated // finally, the asset is actually updated
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(assetRepo.findByUuid(givenAsset.getUuid())).isPresent().get() assertThat(assetRepo.findByUuid(givenAsset.getUuid())).isPresent().get()
.matches(asset -> { .matches(asset -> {
assertThat(asset.getAlarmContact().toString()).isEqualTo(
"contact(caption='second contact', emailAddresses='{main=contact-admin@secondcontact.example.com}')");
assertThat(asset.getConfig().toString()).isEqualTo( 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 }"); "{ monit_max_cpu_usage: 90, monit_max_ram_usage: 70, monit_max_ssd_usage: 85, monit_min_free_ssd: 5 }");
return true; 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 @Nested
@Order(5) @Order(5)
class DeleteAsset { class DeleteAsset {

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset; 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.hosting.generated.api.v1.model.HsHostingAssetPatchResource;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.mapper.KeyValueMap; import net.hostsharing.hsadminng.mapper.KeyValueMap;
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase; import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach; 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 INITIAL_BOOKING_ITEM_UUID = UUID.randomUUID();
private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
private static final Map<String, Object> INITIAL_CONFIG = patchMap( private static final Map<String, Object> INITIAL_CONFIG = patchMap(
entry("CPU", 1), entry("CPU", 1),
@ -47,6 +48,9 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
entry("SSD", 256), entry("SSD", 256),
entry("MEM", 64) entry("MEM", 64)
); );
final HsHostingContactEntity givenInitialContact = HsHostingContactEntity.builder()
.uuid(UUID.randomUUID())
.build();
private static final String INITIAL_CAPTION = "initial caption"; private static final String INITIAL_CAPTION = "initial caption";
private static final String PATCHED_CAPTION = "patched caption"; private static final String PATCHED_CAPTION = "patched caption";
@ -56,10 +60,12 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
@BeforeEach @BeforeEach
void initMocks() { void initMocks() {
lenient().when(em.getReference(eq(HsOfficeDebitorEntity.class), any())).thenAnswer(invocation -> // lenient().when(em.getReference(eq(HsOfficeDebitorEntity.class), any())).thenAnswer(invocation ->
HsOfficeDebitorEntity.builder().uuid(invocation.getArgument(1)).build()); // HsOfficeDebitorEntity.builder().uuid(invocation.getArgument(1)).build());
lenient().when(em.getReference(eq(HsHostingAssetEntity.class), any())).thenAnswer(invocation -> lenient().when(em.getReference(eq(HsHostingAssetEntity.class), any())).thenAnswer(invocation ->
HsHostingAssetEntity.builder().uuid(invocation.getArgument(1)).build()); 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 @Override
@ -69,6 +75,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
entity.setBookingItem(TEST_BOOKING_ITEM); entity.setBookingItem(TEST_BOOKING_ITEM);
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG)); entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
entity.setCaption(INITIAL_CAPTION); entity.setCaption(INITIAL_CAPTION);
entity.setAlarmContact(givenInitialContact);
return entity; return entity;
} }
@ -79,7 +86,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
@Override @Override
protected HsHostingAssetEntityPatcher createPatcher(final HsHostingAssetEntity server) { protected HsHostingAssetEntityPatcher createPatcher(final HsHostingAssetEntity server) {
return new HsHostingAssetEntityPatcher(server); return new HsHostingAssetEntityPatcher(em, server);
} }
@Override @Override
@ -96,7 +103,18 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
PATCH_CONFIG, PATCH_CONFIG,
HsHostingAssetEntity::putConfig, HsHostingAssetEntity::putConfig,
PATCHED_CONFIG) PATCHED_CONFIG)
.notNullable(),
new JsonNullableProperty<>(
"alarmContact",
HsHostingAssetPatchResource::setAlarmContactUuid,
PATCHED_CONTACT_UUID,
HsHostingAssetEntity::setAlarmContact,
newContact(PATCHED_CONTACT_UUID))
.notNullable() .notNullable()
); );
} }
static HsHostingContactEntity newContact(final UUID uuid) {
return HsHostingContactEntity.builder().uuid(uuid).build();
}
} }