diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java index 2ada5e0c..174ba076 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemController.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; -import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.valid; +import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.valid; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; @RestController 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 3da83f8a..e739d344 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 @@ -10,7 +10,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; -import net.hostsharing.hsadminng.hs.validation.Validatable; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; @@ -65,7 +64,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Setter @NoArgsConstructor @AllArgsConstructor -public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatable { +public class HsBookingItemEntity implements Stringifyable, RbacObject { private static Stringify stringify = stringify(HsBookingItemEntity.class) .withProp(HsBookingItemEntity::getProject) @@ -157,16 +156,6 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatab return parentItem == null ? null : parentItem.relatedProject(); } - @Override - public String getPropertiesName() { - return "resources"; - } - - @Override - public Map getProperties() { - return resources; - } - public HsBookingProjectEntity getRelatedProject() { return project != null ? project : parentItem.getRelatedProject(); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java new file mode 100644 index 00000000..113210db --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java @@ -0,0 +1,120 @@ +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 net.hostsharing.hsadminng.hs.validation.ValidatableProperty; + +import jakarta.validation.ValidationException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; + +import static java.util.Arrays.stream; +import static java.util.Collections.emptyList; +import static java.util.Optional.ofNullable; +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; + +public class HsBookingItemEntityValidator extends HsEntityValidator { + + 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()); + } + + private static void register(final Enum type, final HsEntityValidator validator) { + stream(validator.propertyValidators).forEach( entry -> { + entry.verifyConsistency(Map.entry(type, validator)); + }); + validators.put(type, validator); + } + + public static HsEntityValidator forType(final Enum type) { + if ( validators.containsKey(type)) { + return validators.get(type); + } + throw new IllegalArgumentException("no validator found for type " + type); + } + + public static Set> types() { + return validators.keySet(); + } + + public static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) { + final var violations = doValidate(entityToSave); + if (!violations.isEmpty()) { + throw new ValidationException(violations.toString()); + } + return entityToSave; + } + + public static List doValidate(final HsBookingItemEntity bookingItem) { + return HsBookingItemEntityValidator.forType(bookingItem.getType()).validate(bookingItem); + } + + public HsBookingItemEntityValidator(final ValidatableProperty... properties) { + super(properties); + } + + public List validate(final HsBookingItemEntity bookingItem) { + return sequentiallyValidate( + () -> enrich(prefix(bookingItem.toShortString(), "resources"), validateProperties(bookingItem.getResources())), + () -> enrich(prefix(bookingItem.toShortString(), "parentItem"), optionallyValidate(bookingItem.getParentItem())), + () -> validateSubEntities(bookingItem) + ); + } + + private static List optionallyValidate(final HsBookingItemEntity bookingItem) { + return bookingItem != null ? HsBookingItemEntityValidator.doValidate(bookingItem) : emptyList(); + } + + protected List validateSubEntities(final HsBookingItemEntity bookingItem) { + return stream(propertyValidators) + .filter(propDef -> propDef.isTotalsValidator()) + .map(prop -> validateMaxTotalValue(bookingItem, prop)) + .filter(Objects::nonNull) + .toList(); + } + + @SafeVarargs + private List sequentiallyValidate(final Supplier>... validators) { + return stream(validators) + .map(Supplier::get) + .filter(violations -> !violations.isEmpty()) + .findFirst() + .orElse(emptyList()); + } + + private String validateMaxTotalValue( + final HsBookingItemEntity bookingItem, + final ValidatableProperty propDef) { + final var propName = propDef.propertyName(); + final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse(""); + final var totalValue = ofNullable(bookingItem.getSubBookingItems()).orElse(emptyList()) + .stream() + .map(subItem -> propDef.getValue(subItem.getResources())) + .map(HsBookingItemEntityValidator::toInteger) + .reduce(0, Integer::sum); + final var maxValue = toInteger(propDef.getValue(bookingItem.getResources())); + return totalValue > maxValue + ? "total %s is %d%s exceeds max total %s %d%s".formatted( + propName, totalValue, propUnit, propName, maxValue, propUnit) + : null; + } + + private static Integer toInteger(final Object value) { + if (value instanceof Integer) { + return (Integer) value; + } + throw new IllegalArgumentException("Integer value expected, but got " + value); + } +} 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 deleted file mode 100644 index 807a9a5e..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidators.java +++ /dev/null @@ -1,57 +0,0 @@ -package net.hostsharing.hsadminng.hs.booking.item.validators; - -import lombok.experimental.UtilityClass; -import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; -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; - -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()); - } - - private static void register(final Enum type, final HsEntityValidator validator) { - stream(validator.propertyValidators).forEach( entry -> { - entry.verifyConsistency(Map.entry(type, validator)); - }); - validators.put(type, validator); - } - - public static HsEntityValidator forType(final Enum type) { - return validators.get(type); - } - - public static Set> types() { - return validators.keySet(); - } - - public static HsBookingItemEntity valid(final HsBookingItemEntity 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 2d9b7edc..a0d6f09d 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 @@ -1,15 +1,13 @@ 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 net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; -import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; +import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; -class HsCloudServerBookingItemValidator extends HsEntityValidator { +class HsCloudServerBookingItemValidator extends HsBookingItemEntityValidator { HsCloudServerBookingItemValidator() { super( 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 949a86d5..8cf4b0ac 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 @@ -1,16 +1,14 @@ 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 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; +import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; +import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; -class HsManagedServerBookingItemValidator extends HsEntityValidator { +class HsManagedServerBookingItemValidator extends HsBookingItemEntityValidator { HsManagedServerBookingItemValidator() { super( diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java index 482d0900..9b8330bb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java @@ -1,24 +1,47 @@ 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.Collections.emptyList; +import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; +import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; -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; - -class HsManagedWebspaceBookingItemValidator extends HsEntityValidator { +class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator { public HsManagedWebspaceBookingItemValidator() { super( integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(), integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(), integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(), - enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional(), + integerProperty("MultiOptions").min(1).max(100).step(1).required() + .asTotalLimitFor( 25, HsManagedWebspaceBookingItemValidator::totalUnixUsers) + .asTotalLimitFor( 5, HsManagedWebspaceBookingItemValidator::totalDatabaseUsers) + .asTotalLimitFor( 5, HsManagedWebspaceBookingItemValidator::totalDatabases) + .asTotalLimitFor(250, HsManagedWebspaceBookingItemValidator::totalEMailAddresses), integerProperty("Daemons").min(0).max(10).optional(), - booleanProperty("Online Office Server").optional() + booleanProperty("Online Office Server").optional(), + enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional() ); } + + private static List totalUnixUsers() { + // FIXME: implement + return emptyList(); + } + + private static List totalDatabaseUsers() { + // FIXME: implement + return emptyList(); + } + + private static List totalDatabases() { + // FIXME: implement + return emptyList(); + } + + private static List totalEMailAddresses() { + // FIXME: implement + return emptyList(); + } } 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 index 2d70dd62..317f2f0c 100644 --- 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 @@ -1,37 +1,18 @@ 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 static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; -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 { +class HsPrivateCloudBookingItemValidator extends HsBookingItemEntityValidator { 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() + integerProperty("CPUs").min(4).max(128).required().asTotalLimit(), + integerProperty("RAM").unit("GB").min(4).max(512).required().asTotalLimit(), + integerProperty("SSD").unit("GB").min(100).max(4000).step(25).required().asTotalLimit(), + integerProperty("HDD").unit("GB").min(0).max(16000).step(25).withDefault(0).asTotalLimit(), + integerProperty("Traffic").unit("GB").min(1000).max(40000).step(250).required().asTotalLimit(), + enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").withDefault("BASIC") ); } - - @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/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index 8d573c48..6c4dc1b6 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 @@ -8,7 +8,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.validation.Validatable; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; @@ -56,7 +55,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Setter @NoArgsConstructor @AllArgsConstructor -public class HsHostingAssetEntity implements Stringifyable, RbacObject, Validatable { +public class HsHostingAssetEntity implements Stringifyable, RbacObject { private static Stringify stringify = stringify(HsHostingAssetEntity.class) .withProp(HsHostingAssetEntity::getType) @@ -114,16 +113,6 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Validata PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg); } - @Override - public String getPropertiesName() { - return "config"; - } - - @Override - public Map getProperties() { - return config; - } - @Override public String toString() { return stringify.apply(this); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java index 48aced10..2c66b766 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityValidator.java @@ -1,10 +1,11 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators; +import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import net.hostsharing.hsadminng.hs.validation.ValidatableProperty; import org.apache.commons.collections4.ListUtils; import jakarta.validation.ValidationException; @@ -19,23 +20,23 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOU import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; -public class HsHostingAssetEntityValidator extends HsEntityValidator { +public class HsHostingAssetEntityValidator extends HsEntityValidator { - private static final Map, HsEntityValidator> validators = new HashMap<>(); + private static final Map, HsEntityValidator> validators = new HashMap<>(); static { - register(CLOUD_SERVER, new HsEntityValidator<>()); + register(CLOUD_SERVER, new HsHostingAssetEntityValidator()); register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator()); register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator()); } - private static void register(final Enum type, final HsEntityValidator validator) { + private static void register(final Enum type, final HsEntityValidator validator) { stream(validator.propertyValidators).forEach( entry -> { entry.verifyConsistency(Map.entry(type, validator)); }); validators.put(type, validator); } - public static HsEntityValidator forType(final Enum type) { + public static HsEntityValidator forType(final Enum type) { return validators.get(type); } @@ -51,9 +52,15 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator... properties) { + super(properties); + } + + @Override public List validate(final HsHostingAssetEntity assetEntity) { - final var selfValidation = super.validate(assetEntity); + final var selfValidation = enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig())); return selfValidation.isEmpty() // higher levels are only validated with valid sub-entity ? ListUtils.union( @@ -67,6 +74,6 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator optionallyValidate(final HsBookingItemEntity bookingItem) { - return bookingItem != null ? HsBookingItemEntityValidators.validate(bookingItem) : emptyList(); + return bookingItem != null ? HsBookingItemEntityValidator.doValidate(bookingItem) : emptyList(); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java index 35f3b81d..b2107866 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedServerHostingAssetValidator.java @@ -1,12 +1,8 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; -import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; -import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; - -class HsManagedServerHostingAssetValidator extends HsEntityValidator { +class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator { public HsManagedServerHostingAssetValidator() { super( diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java index ffef39d7..4de66cc4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsManagedWebspaceHostingAssetValidator.java @@ -1,13 +1,11 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; -import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import java.util.List; -class HsManagedWebspaceHostingAssetValidator extends HsEntityValidator { +class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator { public HsManagedWebspaceHostingAssetValidator() { } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanProperty.java similarity index 54% rename from src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java rename to src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanProperty.java index 2838e0f5..38e82c36 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanProperty.java @@ -8,29 +8,29 @@ import java.util.Map; import java.util.Objects; @Setter -public class BooleanPropertyValidator extends HsPropertyValidator { +public class BooleanProperty extends ValidatableProperty { private Map.Entry falseIf; - private BooleanPropertyValidator(final String propertyName) { + private BooleanProperty(final String propertyName) { super(Boolean.class, propertyName); } - public static BooleanPropertyValidator booleanProperty(final String propertyName) { - return new BooleanPropertyValidator(propertyName); + public static BooleanProperty booleanProperty(final String propertyName) { + return new BooleanProperty(propertyName); } - public HsPropertyValidator falseIf(final String refPropertyName, final String refPropertyValue) { + public ValidatableProperty falseIf(final String refPropertyName, final String refPropertyValue) { this.falseIf = new AbstractMap.SimpleImmutableEntry<>(refPropertyName, refPropertyValue); return this; } @Override - protected void validate(final ArrayList result, final String propertiesName, final Boolean propValue, final Map props) { + protected void validate(final ArrayList result, final Boolean propValue, final Map props) { if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) { if (propValue) { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be false because " + - propertiesName+"." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue); + result.add(propertyName + " is expected to be false because " + + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue); } } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationProperty.java new file mode 100644 index 00000000..851a8d87 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationProperty.java @@ -0,0 +1,38 @@ +package net.hostsharing.hsadminng.hs.validation; + +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; + +@Setter +public class EnumerationProperty extends ValidatableProperty { + + private String[] values; + + private EnumerationProperty(final String propertyName) { + super(String.class, propertyName); + } + + public static EnumerationProperty enumerationProperty(final String propertyName) { + return new EnumerationProperty(propertyName); + } + + public ValidatableProperty values(final String... values) { + this.values = values; + return this; + } + + @Override + protected void validate(final ArrayList result, final String propValue, final Map props) { + if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) { + result.add(propertyName + " is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'"); + } + } + + @Override + protected String simpleTypeName() { + return "enumeration"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java deleted file mode 100644 index 329feb74..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.hostsharing.hsadminng.hs.validation; - -import lombok.Setter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Map; - -@Setter -public class EnumerationPropertyValidator extends HsPropertyValidator { - - private String[] values; - - private EnumerationPropertyValidator(final String propertyName) { - super(String.class, propertyName); - } - - public static EnumerationPropertyValidator enumerationProperty(final String propertyName) { - return new EnumerationPropertyValidator(propertyName); - } - - public HsPropertyValidator values(final String... values) { - this.values = values; - return this; - } - - @Override - protected void validate(final ArrayList result, final String propertiesName, final String propValue, final Map props) { - if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'"); - } - } - - @Override - protected String simpleTypeName() { - return "enumeration"; - } -} 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 2fdf506a..5acced97 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -4,41 +4,36 @@ 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 net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator; 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> { +public abstract class HsEntityValidator { - public final HsPropertyValidator[] propertyValidators; + public final ValidatableProperty[] propertyValidators; - public HsEntityValidator(final HsPropertyValidator... validators) { + public HsEntityValidator(final ValidatableProperty... validators) { propertyValidators = validators; } - public List validate(final E assetEntity) { - final var result = new ArrayList(); - assetEntity.getProperties().keySet().forEach( givenPropName -> { - if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) { - result.add("'"+assetEntity.getPropertiesName()+"." + givenPropName + "' is not expected but is set to '" +assetEntity.getProperties().get(givenPropName) + "'"); - } - }); - stream(propertyValidators).forEach(pv -> { - result.addAll(pv.validate(assetEntity.getPropertiesName(), assetEntity.getProperties())); - }); - return result; + protected static List enrich(final String prefix, final List messages) { + return messages.stream() + .map(message -> prefix + "." + message) + .toList(); } + protected static String prefix(final String... parts) { + return String.join(".", parts); + } + + public abstract List validate(final E entity); + public final List> properties() { final var mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); @@ -48,45 +43,27 @@ public class HsEntityValidator, T extends Enum> { .toList(); } + protected ArrayList validateProperties(final Map properties) { + final var result = new ArrayList(); + properties.keySet().forEach( givenPropName -> { + if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) { + result.add(givenPropName + " is not expected but is set to '" + properties.get(givenPropName) + "'"); + } + }); + stream(propertyValidators).forEach(pv -> { + result.addAll(pv.validate(properties)); + }); + return result; + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static Map asKeyValueMap(final Map map) { 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()) + ? HsBookingItemEntityValidator.doValidate(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 deleted file mode 100644 index 28e4dea7..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java +++ /dev/null @@ -1,62 +0,0 @@ -package net.hostsharing.hsadminng.hs.validation; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@RequiredArgsConstructor -public abstract class HsPropertyValidator { - - final Class type; - final String propertyName; - private Boolean required; - - public HsPropertyValidator required() { - required = Boolean.TRUE; - return this; - } - - public HsPropertyValidator optional() { - required = Boolean.FALSE; - return this; - } - - public final List validate(final String propertiesName, final Map props) { - final var result = new ArrayList(); - final var propValue = props.get(propertyName); - if (propValue == null) { - if (required) { - result.add("'"+propertiesName+"." + propertyName + "' is required but missing"); - } - } - if (propValue != null){ - if ( type.isInstance(propValue)) { - //noinspection unchecked - validate(result, propertiesName, (T) propValue, props); - } else { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be of type " + type + ", " + - "but is of type '" + propValue.getClass().getSimpleName() + "'"); - } - } - return result; - } - - protected abstract void validate(final ArrayList result, final String propertiesName, final T propValue, final Map props); - - public void verifyConsistency(final Map.Entry, ?> typeDef) { - if (required == null ) { - throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); - } - } - - public Map toMap(final ObjectMapper mapper) { - final Map map = mapper.convertValue(this, Map.class); - map.put("type", simpleTypeName()); - return map; - } - - protected abstract String simpleTypeName(); -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java new file mode 100644 index 00000000..313c398a --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerProperty.java @@ -0,0 +1,46 @@ +package net.hostsharing.hsadminng.hs.validation; + +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Map; + +@Setter +public class IntegerProperty extends ValidatableProperty { + + private String unit; + private Integer min; + private Integer max; + private Integer step; + + public static IntegerProperty integerProperty(final String propertyName) { + return new IntegerProperty(propertyName); + } + + private IntegerProperty(final String propertyName) { + super(Integer.class, propertyName); + } + + @Override + public String unit() { + return unit; + } + + @Override + protected void validate(final ArrayList result, final Integer propValue, final Map props) { + if (min != null && propValue < min) { + result.add(propertyName + " is expected to be >= " + min + " but is " + propValue); + } + if (max != null && propValue > max) { + result.add(propertyName + " is expected to be <= " + max + " but is " + propValue); + } + if (step != null && propValue % step != 0) { + result.add(propertyName + " is expected to be multiple of " + step + " but is " + propValue); + } + } + + @Override + protected String simpleTypeName() { + return "integer"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java deleted file mode 100644 index d6fb85f5..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.hostsharing.hsadminng.hs.validation; - -import lombok.Setter; - -import java.util.ArrayList; -import java.util.Map; - -@Setter -public class IntegerPropertyValidator extends HsPropertyValidator { - - private String unit; - private Integer min; - private Integer max; - private Integer step; - - public static IntegerPropertyValidator integerProperty(final String propertyName) { - return new IntegerPropertyValidator(propertyName); - } - - private IntegerPropertyValidator(final String propertyName) { - super(Integer.class, propertyName); - } - - - @Override - protected void validate(final ArrayList result, final String propertiesName, final Integer propValue, final Map props) { - if (min != null && propValue < min) { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be >= " + min + " but is " + propValue); - } - if (max != null && propValue > max) { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be <= " + max + " but is " + propValue); - } - if (step != null && propValue % step != 0) { - result.add("'"+propertiesName+"." + propertyName + "' is expected to be multiple of " + step + " but is " + propValue); - } - } - - @Override - protected String simpleTypeName() { - return "integer"; - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java deleted file mode 100644 index 6f214b04..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.hostsharing.hsadminng.hs.validation; - - -import java.util.Map; - -public interface Validatable> { - - - Enum getType(); - - String getPropertiesName(); - Map getProperties(); -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java new file mode 100644 index 00000000..292a23af --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java @@ -0,0 +1,109 @@ +package net.hostsharing.hsadminng.hs.validation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static java.util.Collections.emptyList; + +@RequiredArgsConstructor +public abstract class ValidatableProperty { + + final Class type; + final String propertyName; + private Boolean required; + private T defaultValue; + private Supplier> asTotalLimitValidator; + + public String unit() { + return null; + } + + public ValidatableProperty required() { + required = TRUE; + return this; + } + + public ValidatableProperty optional() { + required = FALSE; + return this; + } + + public ValidatableProperty withDefault(final T value) { + defaultValue = value; + required = FALSE; + return this; + } + + public ValidatableProperty asTotalLimit() { + asTotalLimitValidator = ValidatableProperty::directTotalLimitValidator; + return this; + } + + public String propertyName() { + return propertyName; + } + + public boolean isTotalsValidator() { + return asTotalLimitValidator != null; + } + + public ValidatableProperty asTotalLimitFor(final T factor, final Supplier> validator) { + // FIXME: implement i + asTotalLimitValidator = validator; // FIXME: implement multiple + return this; + } + + public final List validate(final Map props) { + final var result = new ArrayList(); + final var propValue = props.get(propertyName); + if (propValue == null) { + if (required) { + result.add(propertyName + " is required but missing"); + } + } + if (propValue != null){ + if ( type.isInstance(propValue)) { + //noinspection unchecked + validate(result, (T) propValue, props); + } else { + result.add(propertyName + " is expected to be of type " + type + ", " + + "but is of type '" + propValue.getClass().getSimpleName() + "'"); + } + } + return result; + } + + protected abstract void validate(final ArrayList result, final T propValue, final Map props); + + public void verifyConsistency(final Map.Entry, ?> typeDef) { + if (required == null ) { + throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); + } + } + + + private static List directTotalLimitValidator() { + return emptyList(); // FIXME: implement + } + + public Map toMap(final ObjectMapper mapper) { + final Map map = mapper.convertValue(this, Map.class); + map.put("type", simpleTypeName()); + return map; + } + + @SuppressWarnings("unchecked") + public T getValue(final Map propValues) { + return (T) Optional.ofNullable(propValues.get(propertyName)).orElse(defaultValue); + } + + protected abstract String simpleTypeName(); +} 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/HsBookingItemEntityValidatorUnitTest.java similarity index 56% rename from src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorsUnitTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidatorUnitTest.java index 83beac3e..d6d355ac 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/HsBookingItemEntityValidatorUnitTest.java @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; +import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import org.junit.jupiter.api.Test; import jakarta.validation.ValidationException; @@ -9,17 +11,26 @@ import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVAT 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.validators.HsBookingItemEntityValidator.valid; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -class HsBookingItemEntityValidatorsUnitTest { +class HsBookingItemEntityValidatorUnitTest { + final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder() + .debitorNumber(12345) + .build(); + final HsBookingProjectEntity project = HsBookingProjectEntity.builder() + .debitor(debitor) + .caption("test project") + .build(); @Test void validThrowsException() { // given final var cloudServerBookingItemEntity = HsBookingItemEntity.builder() .type(CLOUD_SERVER) + .project(project) + .caption("Test-Server") .build(); // when @@ -28,16 +39,16 @@ class HsBookingItemEntityValidatorsUnitTest { // then assertThat(result).isInstanceOf(ValidationException.class) .hasMessageContaining( - "'resources.CPUs' is required but missing", - "'resources.RAM' is required but missing", - "'resources.SSD' is required but missing", - "'resources.Traffic' is required but missing"); + "D-12345:test project:Test-Server.resources.CPUs is required but missing", + "D-12345:test project:Test-Server.resources.RAM is required but missing", + "D-12345:test project:Test-Server.resources.SSD is required but missing", + "D-12345:test project:Test-Server.resources.Traffic is required but missing"); } @Test void listsTypes() { // when - final var result = HsBookingItemEntityValidators.types(); + final var result = HsBookingItemEntityValidator.types(); // then 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 df767445..ccb40b37 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 @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; +import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import org.junit.jupiter.api.Test; import java.util.Map; @@ -11,16 +13,26 @@ 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 net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.forType; import static org.assertj.core.api.Assertions.assertThat; class HsCloudServerBookingItemValidatorUnitTest { + final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder() + .debitorNumber(12345) + .build(); + final HsBookingProjectEntity project = HsBookingProjectEntity.builder() + .debitor(debitor) + .caption("Test-Project") + .build(); + @Test void validatesProperties() { // given final var cloudServerBookingItemEntity = HsBookingItemEntity.builder() .type(CLOUD_SERVER) + .project(project) + .caption("Test-Server") .resources(Map.ofEntries( entry("CPUs", 2), entry("RAM", 25), @@ -31,10 +43,10 @@ class HsCloudServerBookingItemValidatorUnitTest { .build(); // when - final var result = HsBookingItemEntityValidators.validate(cloudServerBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(cloudServerBookingItemEntity); // then - assertThat(result).containsExactly("'resources.SLA-EMail' is not expected but is set to 'true'"); + assertThat(result).containsExactly("D-12345:Test-Project:Test-Server.resources.SLA-EMail is not expected but is set to 'true'"); } @Test @@ -44,12 +56,12 @@ class HsCloudServerBookingItemValidatorUnitTest { // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{type=integer, propertyName=CPUs, required=true, unit=null, min=1, max=32, step=null}", - "{type=integer, propertyName=RAM, required=true, unit=GB, min=1, max=128, step=null}", - "{type=integer, propertyName=SSD, required=true, unit=GB, min=25, max=1000, step=25}", - "{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=4000, step=250}", - "{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]}"); + "{type=integer, propertyName=CPUs, required=true, defaultValue=null, asTotalLimitValidator=null, unit=null, min=1, max=32, step=null, totalsValidator=false}", + "{type=integer, propertyName=RAM, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=1, max=128, step=null, totalsValidator=false}", + "{type=integer, propertyName=SSD, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=25, max=1000, step=25, totalsValidator=false}", + "{type=integer, propertyName=HDD, required=false, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=0, max=4000, step=250, totalsValidator=false}", + "{type=integer, propertyName=Traffic, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=250, max=10000, step=250, totalsValidator=false}", + "{type=enumeration, propertyName=SLA-Infrastructure, required=false, defaultValue=null, asTotalLimitValidator=null, values=[BASIC, EXT8H, EXT4H, EXT2H], totalsValidator=false}"); } @Test @@ -57,6 +69,7 @@ class HsCloudServerBookingItemValidatorUnitTest { // given final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder() .type(CLOUD_SERVER) + .caption("Test Cloud-Server") .resources(ofEntries( entry("CPUs", 2), entry("RAM", 10), @@ -66,6 +79,7 @@ class HsCloudServerBookingItemValidatorUnitTest { .build(); final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder() .type(MANAGED_SERVER) + .caption("Test Managed-Server") .resources(ofEntries( entry("CPUs", 3), entry("RAM", 20), @@ -75,6 +89,8 @@ class HsCloudServerBookingItemValidatorUnitTest { .build(); final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() .type(PRIVATE_CLOUD) + .project(project) + .caption("Test Cloud") .resources(ofEntries( entry("CPUs", 4), entry("RAM", 20), @@ -90,14 +106,14 @@ class HsCloudServerBookingItemValidatorUnitTest { subCloudServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); // when - final var result = HsBookingItemEntityValidators.validate(subCloudServerBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(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" + "D-12345:Test-Project:Test Cloud-Server.parentItem.total CPUs is 5 exceeds max total CPUs 4", + "D-12345:Test-Project:Test Cloud-Server.parentItem.total RAM is 30 GB exceeds max total RAM 20 GB", + "D-12345:Test-Project:Test Cloud-Server.parentItem.total SSD is 150 GB exceeds max total SSD 100 GB", + "D-12345:Test-Project:Test Cloud-Server.parentItem.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 0adcc4e2..ee46f79f 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 @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; +import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import org.junit.jupiter.api.Test; import java.util.Map; @@ -11,16 +13,25 @@ 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 net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.forType; import static org.assertj.core.api.Assertions.assertThat; class HsManagedServerBookingItemValidatorUnitTest { + final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder() + .debitorNumber(12345) + .build(); + final HsBookingProjectEntity project = HsBookingProjectEntity.builder() + .debitor(debitor) + .caption("Test-Project") + .build(); + @Test void validatesProperties() { // given final var mangedServerBookingItemEntity = HsBookingItemEntity.builder() .type(MANAGED_SERVER) + .project(project) .resources(Map.ofEntries( entry("CPUs", 2), entry("RAM", 25), @@ -31,10 +42,10 @@ class HsManagedServerBookingItemValidatorUnitTest { .build(); // when - final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(mangedServerBookingItemEntity); // then - assertThat(result).containsExactly("'resources.SLA-EMail' is expected to be false because resources.SLA-Platform=BASIC but is true"); + assertThat(result).containsExactly("D-12345:Test-Project:null.resources.SLA-EMail is expected to be false because SLA-Platform=BASIC but is true"); } @Test @@ -44,17 +55,17 @@ class HsManagedServerBookingItemValidatorUnitTest { // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{type=integer, propertyName=CPUs, required=true, unit=null, min=1, max=32, step=null}", - "{type=integer, propertyName=RAM, required=true, unit=GB, min=1, max=128, step=null}", - "{type=integer, propertyName=SSD, required=true, unit=GB, min=25, max=1000, step=25}", - "{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=4000, step=250}", - "{type=integer, propertyName=Traffic, required=true, unit=GB, min=250, max=10000, step=250}", - "{type=enumeration, propertyName=SLA-Platform, required=false, values=[BASIC, EXT8H, EXT4H, EXT2H]}", - "{type=boolean, propertyName=SLA-EMail, required=false, falseIf={SLA-Platform=BASIC}}", - "{type=boolean, propertyName=SLA-Maria, required=false, falseIf={SLA-Platform=BASIC}}", - "{type=boolean, propertyName=SLA-PgSQL, required=false, falseIf={SLA-Platform=BASIC}}", - "{type=boolean, propertyName=SLA-Office, required=false, falseIf={SLA-Platform=BASIC}}", - "{type=boolean, propertyName=SLA-Web, required=false, falseIf={SLA-Platform=BASIC}}"); + "{type=integer, propertyName=CPUs, required=true, defaultValue=null, asTotalLimitValidator=null, unit=null, min=1, max=32, step=null, totalsValidator=false}", + "{type=integer, propertyName=RAM, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=1, max=128, step=null, totalsValidator=false}", + "{type=integer, propertyName=SSD, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=25, max=1000, step=25, totalsValidator=false}", + "{type=integer, propertyName=HDD, required=false, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=0, max=4000, step=250, totalsValidator=false}", + "{type=integer, propertyName=Traffic, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=250, max=10000, step=250, totalsValidator=false}", + "{type=enumeration, propertyName=SLA-Platform, required=false, defaultValue=null, asTotalLimitValidator=null, values=[BASIC, EXT8H, EXT4H, EXT2H], totalsValidator=false}", + "{type=boolean, propertyName=SLA-EMail, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf={SLA-Platform=BASIC}, totalsValidator=false}", + "{type=boolean, propertyName=SLA-Maria, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf={SLA-Platform=BASIC}, totalsValidator=false}", + "{type=boolean, propertyName=SLA-PgSQL, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf={SLA-Platform=BASIC}, totalsValidator=false}", + "{type=boolean, propertyName=SLA-Office, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf={SLA-Platform=BASIC}, totalsValidator=false}", + "{type=boolean, propertyName=SLA-Web, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf={SLA-Platform=BASIC}, totalsValidator=false}"); } @Test @@ -80,6 +91,7 @@ class HsManagedServerBookingItemValidatorUnitTest { .build(); final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() .type(PRIVATE_CLOUD) + .project(project) .resources(ofEntries( entry("CPUs", 4), entry("RAM", 20), @@ -91,18 +103,19 @@ class HsManagedServerBookingItemValidatorUnitTest { subCloudServerBookingItemEntity )) .build(); + subManagedServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); subCloudServerBookingItemEntity.setParentItem(privateCloudBookingItemEntity); // when - final var result = HsBookingItemEntityValidators.validate(subManagedServerBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(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" + "D-12345:Test-Project:null.parentItem.total CPUs is 5 exceeds max total CPUs 4", + "D-12345:Test-Project:null.parentItem.total RAM is 30 GB exceeds max total RAM 20 GB", + "D-12345:Test-Project:null.parentItem.total SSD is 150 GB exceeds max total SSD 100 GB", + "D-12345:Test-Project:null.parentItem.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 9d12c996..7d6e1c27 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 @@ -1,6 +1,8 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; +import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; import org.junit.jupiter.api.Test; import java.util.Map; @@ -11,11 +13,21 @@ import static org.assertj.core.api.Assertions.assertThat; class HsManagedWebspaceBookingItemValidatorUnitTest { + final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder() + .debitorNumber(12345) + .build(); + final HsBookingProjectEntity project = HsBookingProjectEntity.builder() + .debitor(debitor) + .caption("Test-Project") + .build(); + @Test void validatesProperties() { // given final var mangedServerBookingItemEntity = HsBookingItemEntity.builder() .type(MANAGED_WEBSPACE) + .project(project) + .caption("Test Managed-Webspace") .resources(Map.ofEntries( entry("CPUs", 2), entry("RAM", 25), @@ -26,27 +38,29 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { .build(); // when - final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(mangedServerBookingItemEntity); // then assertThat(result).containsExactlyInAnyOrder( - "'resources.CPUs' is not expected but is set to '2'", - "'resources.SLA-EMail' is not expected but is set to 'true'", - "'resources.RAM' is not expected but is set to '25'"); + "D-12345:Test-Project:Test Managed-Webspace.resources.SLA-EMail is not expected but is set to 'true'", + "D-12345:Test-Project:Test Managed-Webspace.resources.CPUs is not expected but is set to '2'", + "D-12345:Test-Project:Test Managed-Webspace.resources.RAM is not expected but is set to '25'", + "D-12345:Test-Project:Test Managed-Webspace.resources.MultiOptions is required but missing"); } @Test void containsAllValidations() { // when - final var validator = HsBookingItemEntityValidators.forType(MANAGED_WEBSPACE); + final var validator = HsBookingItemEntityValidator.forType(MANAGED_WEBSPACE); // then assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( - "{type=integer, propertyName=SSD, required=true, unit=GB, min=1, max=100, step=1}", - "{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=250, step=10}", - "{type=integer, propertyName=Traffic, required=true, unit=GB, min=10, max=1000, step=10}", - "{type=enumeration, propertyName=SLA-Platform, required=false, values=[BASIC, EXT24H]}", - "{type=integer, propertyName=Daemons, required=false, unit=null, min=0, max=10, step=null}", - "{type=boolean, propertyName=Online Office Server, required=false, falseIf=null}"); + "{type=integer, propertyName=SSD, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=1, max=100, step=1, totalsValidator=false}", + "{type=integer, propertyName=HDD, required=false, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=0, max=250, step=10, totalsValidator=false}", + "{type=integer, propertyName=Traffic, required=true, defaultValue=null, asTotalLimitValidator=null, unit=GB, min=10, max=1000, step=10, totalsValidator=false}", + "{type=integer, propertyName=MultiOptions, required=true, defaultValue=null, asTotalLimitValidator={}, unit=null, min=1, max=100, step=1, totalsValidator=true}", + "{type=integer, propertyName=Daemons, required=false, defaultValue=null, asTotalLimitValidator=null, unit=null, min=0, max=10, step=null, totalsValidator=false}", + "{type=boolean, propertyName=Online Office Server, required=false, defaultValue=null, asTotalLimitValidator=null, falseIf=null, totalsValidator=false}", + "{type=enumeration, propertyName=SLA-Platform, required=false, defaultValue=null, asTotalLimitValidator=null, values=[BASIC, EXT24H], totalsValidator=false}"); } } 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 index 78ab0a66..4d3f4326 100644 --- 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 @@ -48,7 +48,7 @@ class HsPrivateCloudBookingItemValidatorTest { .build(); // when - final var result = HsBookingItemEntityValidators.validate(privateCloudBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(privateCloudBookingItemEntity); // then assertThat(result).isEmpty(); @@ -88,7 +88,7 @@ class HsPrivateCloudBookingItemValidatorTest { .build(); // when - final var result = HsBookingItemEntityValidators.validate(privateCloudBookingItemEntity); + final var result = HsBookingItemEntityValidator.doValidate(privateCloudBookingItemEntity); // then assertThat(result).containsExactlyInAnyOrder( 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 682787fb..a64f14d6 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 @@ -1,13 +1,14 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import org.junit.jupiter.api.Test; 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.HsHostingAssetType.MANAGED_WEBSPACE; import static org.assertj.core.api.Assertions.assertThat; import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.TEST_PROJECT; @@ -16,14 +17,15 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder() .project(TEST_PROJECT) + .type(HsBookingItemType.MANAGED_SERVER) .build(); final HsHostingAssetEntity mangedServerAssetEntity = HsHostingAssetEntity.builder() - .type(MANAGED_SERVER) + .type(HsHostingAssetType.MANAGED_SERVER) .bookingItem(managedServerBookingItem) .config(Map.ofEntries( - entry("HDD", 0), - entry("SSD", 1), - entry("Traffic", 10) + entry("monit_max_ssd_usage", 70), + entry("monit_max_cpu_usage", 80), + entry("monit_max_ram_usage", 90) )) .build();