From eb98ab99be9c8ab7413e311bfbc8f89b8d0597a5 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 5 Jun 2024 15:07:32 +0200 Subject: [PATCH] hierarchical validation for booking-items up- and downwards --- .../hs/booking/item/HsBookingItemEntity.java | 7 ++ .../HsBookingItemEntityValidators.java | 9 +- .../HsCloudServerBookingItemValidator.java | 8 ++ .../HsManagedServerBookingItemValidator.java | 9 ++ .../HsPrivateCloudBookingItemValidator.java | 37 +++++++ .../asset/HsHostingAssetController.java | 2 +- .../asset/HsHostingAssetPropsController.java | 6 +- ...ava => HsHostingAssetEntityValidator.java} | 31 +++++- .../hs/validation/HsEntityValidator.java | 45 +++++++- .../hs/validation/HsPropertyValidator.java | 5 - ...HsBookingItemEntityValidatorsUnitTest.java | 5 +- ...oudServerBookingItemValidatorUnitTest.java | 56 +++++++++- ...gedServerBookingItemValidatorUnitTest.java | 56 +++++++++- ...dWebspaceBookingItemValidatorUnitTest.java | 6 +- ...sPrivateCloudBookingItemValidatorTest.java | 102 ++++++++++++++++++ ...sHostingAssetControllerAcceptanceTest.java | 1 - ...udServerHostingAssetValidatorUnitTest.java | 2 +- ...sHostingAssetEntityValidatorUnitTest.java} | 4 +- ...edServerHostingAssetValidatorUnitTest.java | 2 +- ...WebspaceHostingAssetValidatorUnitTest.java | 6 +- 20 files changed, 365 insertions(+), 34 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidator.java rename src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/{HsHostingAssetEntityValidators.java => HsHostingAssetEntityValidator.java} (58%) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidatorTest.java rename src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/{HsHostingAssetEntityValidatorsUnitTest.java => HsHostingAssetEntityValidatorUnitTest.java} (93%) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java index 1c5040e7..3da83f8a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java @@ -19,6 +19,7 @@ import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.Type; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -27,12 +28,14 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Transient; import jakarta.persistence.Version; import java.io.IOException; import java.time.LocalDate; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -105,6 +108,10 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatab @Column(columnDefinition = "resources") private Map resources = new HashMap<>(); + @OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true) + @JoinColumn(name="parentitemuuid", referencedColumnName="uuid") + private List subBookingItems; + @Transient private PatchableMapWrapper resourcesWrapper; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidators.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidators.java index 1f4493e2..807a9a5e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidators.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidators.java @@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import jakarta.validation.ValidationException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -14,12 +15,14 @@ import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; @UtilityClass public class HsBookingItemEntityValidators { private static final Map, HsEntityValidator> validators = new HashMap<>(); static { + register(PRIVATE_CLOUD, new HsPrivateCloudBookingItemValidator()); register(CLOUD_SERVER, new HsCloudServerBookingItemValidator()); register(MANAGED_SERVER, new HsManagedServerBookingItemValidator()); register(MANAGED_WEBSPACE, new HsManagedWebspaceBookingItemValidator()); @@ -41,10 +44,14 @@ public class HsBookingItemEntityValidators { } public static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) { - final var violations = HsBookingItemEntityValidators.forType(entityToSave.getType()).validate(entityToSave); + final var violations = validate(entityToSave); if (!violations.isEmpty()) { throw new ValidationException(violations.toString()); } return entityToSave; } + + public static List validate(final HsBookingItemEntity bookingItem) { + return HsBookingItemEntityValidators.forType(bookingItem.getType()).validate(bookingItem); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidator.java index fa09f2c3..2d9b7edc 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidator.java @@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import java.util.List; + import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; @@ -19,4 +21,10 @@ class HsCloudServerBookingItemValidator extends HsEntityValidator validate(final HsBookingItemEntity cloudServerBookingItem) { + final var selfValidation = super.validate(cloudServerBookingItem); + return !selfValidation.isEmpty() ? selfValidation : validateParentEntities(cloudServerBookingItem); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidator.java index 79c41070..949a86d5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidator.java @@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import java.util.List; + import static net.hostsharing.hsadminng.hs.validation.BooleanPropertyValidator.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; @@ -25,4 +27,11 @@ class HsManagedServerBookingItemValidator extends HsEntityValidator validate(final HsBookingItemEntity managedServerBookingItem) { + final var selfValidation = super.validate(managedServerBookingItem); + return !selfValidation.isEmpty() ? selfValidation : validateParentEntities(managedServerBookingItem); + } + } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidator.java new file mode 100644 index 00000000..2d70dd62 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidator.java @@ -0,0 +1,37 @@ +package net.hostsharing.hsadminng.hs.booking.item.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; + +import java.util.List; + +import static java.util.EnumSet.of; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; + +class HsPrivateCloudBookingItemValidator extends HsEntityValidator { + + HsPrivateCloudBookingItemValidator() { + super( + integerProperty("CPUs").min(4).max(128).required(), + integerProperty("RAM").unit("GB").min(4).max(512).required(), + integerProperty("SSD").unit("GB").min(100).max(4000).step(25).required(), + integerProperty("HDD").unit("GB").min(0).max(16000).step(25).optional(), + integerProperty("Traffic").unit("GB").min(1000).max(40000).step(250).required(), + enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional() + ); + } + + @Override + public List validate(final HsBookingItemEntity privateCloudBookingItem) { + final var selfValidation = super.validate(privateCloudBookingItem); + return !selfValidation.isEmpty() ? selfValidation : validateSubEntities(privateCloudBookingItem); + } + + private List validateSubEntities(final HsBookingItemEntity privateCloudBookingItem) { + return validateSubEntities(privateCloudBookingItem, of(CLOUD_SERVER, MANAGED_SERVER)); + } +} 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 a645bb78..291ac5ee 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 @@ -20,7 +20,7 @@ import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; -import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.valid; +import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidator.valid; @RestController public class HsHostingAssetController implements HsHostingAssetsApi { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java index 47852310..97a50cb1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators; +import net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidator; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource; import org.springframework.http.ResponseEntity; @@ -15,7 +15,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi { @Override public ResponseEntity> listAssetTypes() { - final var resource = HsHostingAssetEntityValidators.types().stream() + final var resource = HsHostingAssetEntityValidator.types().stream() .map(Enum::name) .toList(); return ResponseEntity.ok(resource); @@ -25,7 +25,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi { public ResponseEntity> listAssetTypeProps( final HsHostingAssetTypeResource assetType) { - final var propValidators = HsHostingAssetEntityValidators.forType(HsHostingAssetType.of(assetType)); + final var propValidators = HsHostingAssetEntityValidator.forType(HsHostingAssetType.of(assetType)); final List> resource = propValidators.properties(); return ResponseEntity.ok(toListOfObjects(resource)); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidators.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java similarity index 58% rename from src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidators.java rename to src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java index 11df9a84..48aced10 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidators.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java @@ -1,22 +1,25 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import lombok.experimental.UtilityClass; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import org.apache.commons.collections4.ListUtils; import jakarta.validation.ValidationException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import static java.util.Arrays.stream; +import static java.util.Collections.emptyList; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; -@UtilityClass -public class HsHostingAssetEntityValidators { +public class HsHostingAssetEntityValidator extends HsEntityValidator { private static final Map, HsEntityValidator> validators = new HashMap<>(); static { @@ -40,12 +43,30 @@ public class HsHostingAssetEntityValidators { return validators.keySet(); } - public static HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { - final var violations = HsHostingAssetEntityValidators.forType(entityToSave.getType()).validate(entityToSave); + final var violations = HsHostingAssetEntityValidator.forType(entityToSave.getType()).validate(entityToSave); if (!violations.isEmpty()) { throw new ValidationException(violations.toString()); } return entityToSave; } + + @Override + public List validate(final HsHostingAssetEntity assetEntity) { + final var selfValidation = super.validate(assetEntity); + return selfValidation.isEmpty() + // higher levels are only validated with valid sub-entity + ? ListUtils.union( + optionallyValidate(assetEntity.getParentAsset()), + optionallyValidate(assetEntity.getBookingItem())) + : selfValidation; + } + + private static List optionallyValidate(final HsHostingAssetEntity assetEntity) { + return assetEntity != null ? forType(assetEntity.getType()).validate(assetEntity) : emptyList(); + } + + private static List optionallyValidate(final HsBookingItemEntity bookingItem) { + return bookingItem != null ? HsBookingItemEntityValidators.validate(bookingItem) : emptyList(); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index 43be4d10..2fdf506a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -3,13 +3,20 @@ package net.hostsharing.hsadminng.hs.validation; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Objects; import static java.util.Arrays.stream; +import static java.util.Collections.emptyList; +import static java.util.Optional.ofNullable; public class HsEntityValidator, T extends Enum> { @@ -32,7 +39,7 @@ public class HsEntityValidator, T extends Enum> { return result; } - public List> properties() { + public final List> properties() { final var mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); return Arrays.stream(propertyValidators) @@ -46,4 +53,40 @@ public class HsEntityValidator, T extends Enum> { return (Map) map; } + protected List validateSubEntities( + final HsBookingItemEntity assetEntity, + final EnumSet subItemTypes) { + return properties().stream() + .map(prop -> validateMaxTotalValue(assetEntity, subItemTypes, prop)) + .filter(Objects::nonNull) + .toList(); + } + + protected String validateMaxTotalValue( + final HsBookingItemEntity assetEntity, + final EnumSet subTypes, + final Map propDef) { + final var propName = propDef.get("propertyName").toString(); + final var propUnit = ofNullable(propDef.get("unit")).map(u -> " " + u).orElse(""); + final var totalValue = ofNullable(assetEntity.getSubBookingItems()).orElse(emptyList()) + .stream() + .filter(subItem -> subTypes.contains(subItem.getType())) + .map(subItem -> toInteger(subItem.getResources().get(propName))) + .reduce(0, Integer::sum); + final var maxValue = toInteger(assetEntity.getResources().get(propName)); + return totalValue > maxValue + ? "total %s is %d%s exceeds max total %s %d%s".formatted( + propName, totalValue, propUnit, propName, maxValue, propUnit) + : null; + } + + protected List validateParentEntities(final HsBookingItemEntity bookingItem) { + return bookingItem.getParentItem() != null + ? HsBookingItemEntityValidators.validate(bookingItem.getParentItem()) + : emptyList(); + } + + private Integer toInteger(final Object value) { + return value == null ? 0 : (Integer) value; + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java index 891c8a7a..28e4dea7 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java @@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.validation; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -15,10 +14,6 @@ public abstract class HsPropertyValidator { final String propertyName; private Boolean required; - public static Map.Entry defType(K k, V v) { - return new SimpleImmutableEntry<>(k, v); - } - public HsPropertyValidator required() { required = Boolean.TRUE; return this; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorsUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorsUnitTest.java index 741d7c1e..83beac3e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorsUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorsUnitTest.java @@ -5,10 +5,11 @@ import org.junit.jupiter.api.Test; import jakarta.validation.ValidationException; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.valid; -import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -39,6 +40,6 @@ class HsBookingItemEntityValidatorsUnitTest { final var result = HsBookingItemEntityValidators.types(); // then - assertThat(result).containsExactlyInAnyOrder(CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE); + assertThat(result).containsExactlyInAnyOrder(PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidatorUnitTest.java index e15b95d7..df767445 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsCloudServerBookingItemValidatorUnitTest.java @@ -5,8 +5,12 @@ import org.junit.jupiter.api.Test; import java.util.Map; +import static java.util.List.of; import static java.util.Map.entry; +import static java.util.Map.ofEntries; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType; import static org.assertj.core.api.Assertions.assertThat; @@ -15,7 +19,6 @@ class HsCloudServerBookingItemValidatorUnitTest { @Test void validatesProperties() { // given - final var validator = HsBookingItemEntityValidators.forType(CLOUD_SERVER); final var cloudServerBookingItemEntity = HsBookingItemEntity.builder() .type(CLOUD_SERVER) .resources(Map.ofEntries( @@ -28,7 +31,7 @@ class HsCloudServerBookingItemValidatorUnitTest { .build(); // when - final var result = validator.validate(cloudServerBookingItemEntity); + final var result = HsBookingItemEntityValidators.validate(cloudServerBookingItemEntity); // then assertThat(result).containsExactly("'resources.SLA-EMail' is not expected but is set to 'true'"); @@ -48,4 +51,53 @@ class HsCloudServerBookingItemValidatorUnitTest { "{type=integer, propertyName=Traffic, required=true, unit=GB, min=250, max=10000, step=250}", "{type=enumeration, propertyName=SLA-Infrastructure, required=false, values=[BASIC, EXT8H, EXT4H, EXT2H]}"); } + + @Test + void validatesExceedingPropertyTotals() { + // given + final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder() + .type(CLOUD_SERVER) + .resources(ofEntries( + entry("CPUs", 2), + entry("RAM", 10), + entry("SSD", 50), + entry("Traffic", 2500) + )) + .build(); + final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder() + .type(MANAGED_SERVER) + .resources(ofEntries( + entry("CPUs", 3), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 3000) + )) + .build(); + final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() + .type(PRIVATE_CLOUD) + .resources(ofEntries( + entry("CPUs", 4), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 5000) + )) + .subBookingItems(of( + subManagedServerBookingItemEntity, + subCloudServerBookingItemEntity + )) + .build(); + subManagedServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); + subCloudServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); + + // when + final var result = HsBookingItemEntityValidators.validate(subCloudServerBookingItemEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "total CPUs is 5 exceeds max total CPUs 4", + "total RAM is 30 GB exceeds max total RAM 20 GB", + "total SSD is 150 GB exceeds max total SSD 100 GB", + "total Traffic is 5500 GB exceeds max total Traffic 5000 GB" + ); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java index 5f2bdfc3..0adcc4e2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java @@ -5,8 +5,12 @@ import org.junit.jupiter.api.Test; import java.util.Map; +import static java.util.List.of; import static java.util.Map.entry; +import static java.util.Map.ofEntries; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType; import static org.assertj.core.api.Assertions.assertThat; @@ -15,7 +19,6 @@ class HsManagedServerBookingItemValidatorUnitTest { @Test void validatesProperties() { // given - final var validator = HsBookingItemEntityValidators.forType(MANAGED_SERVER); final var mangedServerBookingItemEntity = HsBookingItemEntity.builder() .type(MANAGED_SERVER) .resources(Map.ofEntries( @@ -28,7 +31,7 @@ class HsManagedServerBookingItemValidatorUnitTest { .build(); // when - final var result = validator.validate(mangedServerBookingItemEntity); + final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity); // then assertThat(result).containsExactly("'resources.SLA-EMail' is expected to be false because resources.SLA-Platform=BASIC but is true"); @@ -53,4 +56,53 @@ class HsManagedServerBookingItemValidatorUnitTest { "{type=boolean, propertyName=SLA-Office, required=false, falseIf={SLA-Platform=BASIC}}", "{type=boolean, propertyName=SLA-Web, required=false, falseIf={SLA-Platform=BASIC}}"); } + + @Test + void validatesExceedingPropertyTotals() { + // given + final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder() + .type(CLOUD_SERVER) + .resources(ofEntries( + entry("CPUs", 2), + entry("RAM", 10), + entry("SSD", 50), + entry("Traffic", 2500) + )) + .build(); + final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder() + .type(MANAGED_SERVER) + .resources(ofEntries( + entry("CPUs", 3), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 3000) + )) + .build(); + final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() + .type(PRIVATE_CLOUD) + .resources(ofEntries( + entry("CPUs", 4), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 5000) + )) + .subBookingItems(of( + subManagedServerBookingItemEntity, + subCloudServerBookingItemEntity + )) + .build(); + subManagedServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); + subCloudServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); + + // when + final var result = HsBookingItemEntityValidators.validate(subManagedServerBookingItemEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "total CPUs is 5 exceeds max total CPUs 4", + "total RAM is 30 GB exceeds max total RAM 20 GB", + "total SSD is 150 GB exceeds max total SSD 100 GB", + "total Traffic is 5500 GB exceeds max total Traffic 5000 GB" + ); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidatorUnitTest.java index 8a278850..9d12c996 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidatorUnitTest.java @@ -7,7 +7,6 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; -import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType; import static org.assertj.core.api.Assertions.assertThat; class HsManagedWebspaceBookingItemValidatorUnitTest { @@ -25,10 +24,9 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { entry("SLA-EMail", true) )) .build(); - final var validator = forType(mangedServerBookingItemEntity.getType()); // when - final var result = validator.validate(mangedServerBookingItemEntity); + final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity); // then assertThat(result).containsExactlyInAnyOrder( @@ -40,7 +38,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { @Test void containsAllValidations() { // when - final var validator = forType(MANAGED_WEBSPACE); + final var validator = HsBookingItemEntityValidators.forType(MANAGED_WEBSPACE); // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidatorTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidatorTest.java new file mode 100644 index 00000000..78ab0a66 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsPrivateCloudBookingItemValidatorTest.java @@ -0,0 +1,102 @@ +package net.hostsharing.hsadminng.hs.booking.item.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import org.junit.jupiter.api.Test; + + +import static java.util.List.of; +import static java.util.Map.entry; +import static java.util.Map.ofEntries; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; +import static org.assertj.core.api.Assertions.assertThat; + +class HsPrivateCloudBookingItemValidatorTest { + + @Test + void validatesPropertyTotals() { + // given + final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() + .type(PRIVATE_CLOUD) + .resources(ofEntries( + entry("CPUs", 4), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 5000) + )) + .subBookingItems(of( + HsBookingItemEntity.builder() + .type(MANAGED_SERVER) + .resources(ofEntries( + entry("CPUs", 2), + entry("RAM", 10), + entry("SSD", 50), + entry("Traffic", 2500) + )) + .build(), + HsBookingItemEntity.builder() + .type(CLOUD_SERVER) + .resources(ofEntries( + entry("CPUs", 2), + entry("RAM", 10), + entry("SSD", 50), + entry("Traffic", 2500) + )) + .build() + )) + .build(); + + // when + final var result = HsBookingItemEntityValidators.validate(privateCloudBookingItemEntity); + + // then + assertThat(result).isEmpty(); + } + + @Test + void validatesExceedingPropertyTotals() { + // given + final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() + .type(PRIVATE_CLOUD) + .resources(ofEntries( + entry("CPUs", 4), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 5000) + )) + .subBookingItems(of( + HsBookingItemEntity.builder() + .type(MANAGED_SERVER) + .resources(ofEntries( + entry("CPUs", 3), + entry("RAM", 20), + entry("SSD", 100), + entry("Traffic", 3000) + )) + .build(), + HsBookingItemEntity.builder() + .type(CLOUD_SERVER) + .resources(ofEntries( + entry("CPUs", 2), + entry("RAM", 10), + entry("SSD", 50), + entry("Traffic", 2500) + )) + .build() + )) + .build(); + + // when + final var result = HsBookingItemEntityValidators.validate(privateCloudBookingItemEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "total CPUs is 5 exceeds max total CPUs 4", + "total RAM is 30 GB exceeds max total RAM 20 GB", + "total SSD is 150 GB exceeds max total SSD 100 GB", + "total Traffic is 5500 GB exceeds max total Traffic 5000 GB" + ); + } + +} 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 5204a1ec..f4945cb1 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 @@ -20,7 +20,6 @@ import java.util.Map; import java.util.UUID; import static java.util.Map.entry; -import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java index de679c40..8f97e630 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsCloudServerHostingAssetValidatorUnitTest.java @@ -7,7 +7,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; -import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.forType; +import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidator.forType; import static org.assertj.core.api.Assertions.assertThat; class HsCloudServerHostingAssetValidatorUnitTest { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorsUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorUnitTest.java similarity index 93% rename from src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorsUnitTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorUnitTest.java index 0e07e30c..2e4f01a2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorsUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidatorUnitTest.java @@ -6,11 +6,11 @@ import org.junit.jupiter.api.Test; import jakarta.validation.ValidationException; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; -import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.valid; +import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidator.valid; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -class HsHostingAssetEntityValidatorsUnitTest { +class HsHostingAssetEntityValidatorUnitTest { @Test void validThrowsException() { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java index cb9e066b..6da559c5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidatorUnitTest.java @@ -7,7 +7,7 @@ import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; -import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.forType; +import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidator.forType; import static org.assertj.core.api.Assertions.assertThat; class HsManagedServerHostingAssetValidatorUnitTest { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java index 83634501..682787fb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidatorUnitTest.java @@ -30,7 +30,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { @Test void validatesIdentifier() { // given - final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -47,7 +47,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { @Test void validatesUnknownProperties() { // given - final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -67,7 +67,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { @Test void validatesValidEntity() { // given - final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity)