hierarchical validation for booking-items up- and downwards

This commit is contained in:
Michael Hoennig 2024-06-05 15:07:32 +02:00
parent fc2b437a55
commit eb98ab99be
20 changed files with 365 additions and 34 deletions

View File

@ -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<String, Object> resources = new HashMap<>();
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true)
@JoinColumn(name="parentitemuuid", referencedColumnName="uuid")
private List<HsBookingItemEntity> subBookingItems;
@Transient
private PatchableMapWrapper<Object> resourcesWrapper;

View File

@ -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<Enum<HsBookingItemType>, HsEntityValidator<HsBookingItemEntity, HsBookingItemType>> 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<String> validate(final HsBookingItemEntity bookingItem) {
return HsBookingItemEntityValidators.forType(bookingItem.getType()).validate(bookingItem);
}
}

View File

@ -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<HsBookingItemE
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional()
);
}
@Override
public List<String> validate(final HsBookingItemEntity cloudServerBookingItem) {
final var selfValidation = super.validate(cloudServerBookingItem);
return !selfValidation.isEmpty() ? selfValidation : validateParentEntities(cloudServerBookingItem);
}
}

View File

@ -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<HsBookingIte
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional()
);
}
@Override
public List<String> validate(final HsBookingItemEntity managedServerBookingItem) {
final var selfValidation = super.validate(managedServerBookingItem);
return !selfValidation.isEmpty() ? selfValidation : validateParentEntities(managedServerBookingItem);
}
}

View File

@ -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<HsBookingItemEntity, HsBookingItemType> {
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<String> validate(final HsBookingItemEntity privateCloudBookingItem) {
final var selfValidation = super.validate(privateCloudBookingItem);
return !selfValidation.isEmpty() ? selfValidation : validateSubEntities(privateCloudBookingItem);
}
private List<String> validateSubEntities(final HsBookingItemEntity privateCloudBookingItem) {
return validateSubEntities(privateCloudBookingItem, of(CLOUD_SERVER, MANAGED_SERVER));
}
}

View File

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

View File

@ -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<List<String>> 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<List<Object>> listAssetTypeProps(
final HsHostingAssetTypeResource assetType) {
final var propValidators = HsHostingAssetEntityValidators.forType(HsHostingAssetType.of(assetType));
final var propValidators = HsHostingAssetEntityValidator.forType(HsHostingAssetType.of(assetType));
final List<Map<String, Object>> resource = propValidators.properties();
return ResponseEntity.ok(toListOfObjects(resource));
}

View File

@ -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<HsHostingAssetEntity, HsHostingAssetType> {
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType>> 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<String> 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<String> optionallyValidate(final HsHostingAssetEntity assetEntity) {
return assetEntity != null ? forType(assetEntity.getType()).validate(assetEntity) : emptyList();
}
private static List<String> optionallyValidate(final HsBookingItemEntity bookingItem) {
return bookingItem != null ? HsBookingItemEntityValidators.validate(bookingItem) : emptyList();
}
}

View File

@ -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<E extends Validatable<E, T>, T extends Enum<T>> {
@ -32,7 +39,7 @@ public class HsEntityValidator<E extends Validatable<E, T>, T extends Enum<T>> {
return result;
}
public List<Map<String, Object>> properties() {
public final List<Map<String, Object>> properties() {
final var mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
return Arrays.stream(propertyValidators)
@ -46,4 +53,40 @@ public class HsEntityValidator<E extends Validatable<E, T>, T extends Enum<T>> {
return (Map<String, Object>) map;
}
protected List<String> validateSubEntities(
final HsBookingItemEntity assetEntity,
final EnumSet<HsBookingItemType> subItemTypes) {
return properties().stream()
.map(prop -> validateMaxTotalValue(assetEntity, subItemTypes, prop))
.filter(Objects::nonNull)
.toList();
}
protected String validateMaxTotalValue(
final HsBookingItemEntity assetEntity,
final EnumSet<HsBookingItemType> subTypes,
final Map<String, Object> 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<String> 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;
}
}

View File

@ -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<T> {
final String propertyName;
private Boolean required;
public static <K, V> Map.Entry<K, V> defType(K k, V v) {
return new SimpleImmutableEntry<>(k, v);
}
public HsPropertyValidator<T> required() {
required = Boolean.TRUE;
return this;

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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(

View File

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

View File

@ -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;

View File

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

View File

@ -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() {

View File

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

View File

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