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.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);

View File

@ -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")

View File

@ -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<HsHostingAssetPatchResource> {
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<HsHostingAsset
.ifPresent(entity::setCaption);
Optional.ofNullable(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
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:

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.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 {

View File

@ -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<String, Object> 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();
}
}