Compare commits

..

No commits in common. "cb29730810012d9120f37cd7fda9f677fe1761e8" and "d7a57fd112394d31458a11d5a3060fad5ac3a6a2" have entirely different histories.

7 changed files with 10 additions and 82 deletions

View File

@ -16,9 +16,7 @@ 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;
@ -28,9 +26,6 @@ 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;
@ -124,7 +119,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
final var current = assetRepo.findByUuid(assetUuid).orElseThrow(); final var current = assetRepo.findByUuid(assetUuid).orElseThrow();
new HsHostingAssetEntityPatcher(em, current).apply(body); new HsHostingAssetEntityPatcher(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 alarmContact; private HsHostingContactEntity alarmContactUuid;
@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,22 +1,17 @@
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;
HsHostingAssetEntityPatcher(final EntityManager em, final HsHostingAssetEntity entity) { public HsHostingAssetEntityPatcher(final HsHostingAssetEntity entity) {
this.em = em;
this.entity = entity; this.entity = entity;
} }
@ -26,15 +21,5 @@ 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,8 +32,6 @@ 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:
@ -48,10 +46,6 @@ 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'
@ -78,10 +72,6 @@ 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

@ -58,7 +58,6 @@ public class ArchitectureTest {
"..hs.booking.project", "..hs.booking.project",
"..hs.booking.item", "..hs.booking.item",
"..hs.booking.item.validators", "..hs.booking.item.validators",
"..hs.hosting.contact",
"..hs.hosting.asset", "..hs.hosting.asset",
"..hs.hosting.asset.validators", "..hs.hosting.asset.validators",
"..errors", "..errors",
@ -151,8 +150,7 @@ public class ArchitectureTest {
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage( .resideInAnyPackage(
"..hs.booking.(*)..", "..hs.booking.(*)..",
"..hs.hosting.(*)..", "..hs.hosting.(*).."
"..hs.validation" // TODO.impl: Some Validators need to be refactored to booking package.
); );
@ArchTest @ArchTest

View File

@ -7,8 +7,6 @@ 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;
@ -56,9 +54,6 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
@Autowired @Autowired
HsOfficeDebitorRepository debitorRepo; HsOfficeDebitorRepository debitorRepo;
@Autowired
HsOfficeContactRepository contactRepo;
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@ -430,7 +425,6 @@ 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()
@ -438,14 +432,13 @@ 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())
@ -457,11 +450,6 @@ 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,
@ -469,15 +457,12 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
"monit_min_free_ssd": 5 "monit_min_free_ssd": 5
} }
} }
""".formatted(alarmContactUuid))); """)); // @formatter:on
// @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;
@ -485,13 +470,6 @@ 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,7 +31,6 @@ 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),
@ -48,9 +47,6 @@ 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";
@ -60,12 +56,10 @@ 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
@ -75,7 +69,6 @@ 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;
} }
@ -86,7 +79,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
@Override @Override
protected HsHostingAssetEntityPatcher createPatcher(final HsHostingAssetEntity server) { protected HsHostingAssetEntityPatcher createPatcher(final HsHostingAssetEntity server) {
return new HsHostingAssetEntityPatcher(em, server); return new HsHostingAssetEntityPatcher(server);
} }
@Override @Override
@ -103,18 +96,7 @@ 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();
}
} }