hierarchical-validation-baseline #59

Merged
hsh-michaelhoennig merged 18 commits from hierarchical-validation-baseline into master 2024-06-14 16:48:01 +02:00
20 changed files with 365 additions and 34 deletions
Showing only changes of commit eb98ab99be - Show all commits

View File

@ -19,6 +19,7 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
@ -27,12 +28,14 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.Transient; import jakarta.persistence.Transient;
import jakarta.persistence.Version; import jakarta.persistence.Version;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -105,6 +108,10 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatab
@Column(columnDefinition = "resources") @Column(columnDefinition = "resources")
private Map<String, Object> resources = new HashMap<>(); private Map<String, Object> resources = new HashMap<>();
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true)
@JoinColumn(name="parentitemuuid", referencedColumnName="uuid")
private List<HsBookingItemEntity> subBookingItems;
@Transient @Transient
private PatchableMapWrapper<Object> resourcesWrapper; private PatchableMapWrapper<Object> resourcesWrapper;

View File

@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.CLOUD_SERVER;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_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.MANAGED_WEBSPACE;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD;
@UtilityClass @UtilityClass
public class HsBookingItemEntityValidators { public class HsBookingItemEntityValidators {
private static final Map<Enum<HsBookingItemType>, HsEntityValidator<HsBookingItemEntity, HsBookingItemType>> validators = new HashMap<>(); private static final Map<Enum<HsBookingItemType>, HsEntityValidator<HsBookingItemEntity, HsBookingItemType>> validators = new HashMap<>();
static { static {
register(PRIVATE_CLOUD, new HsPrivateCloudBookingItemValidator());
register(CLOUD_SERVER, new HsCloudServerBookingItemValidator()); register(CLOUD_SERVER, new HsCloudServerBookingItemValidator());
register(MANAGED_SERVER, new HsManagedServerBookingItemValidator()); register(MANAGED_SERVER, new HsManagedServerBookingItemValidator());
register(MANAGED_WEBSPACE, new HsManagedWebspaceBookingItemValidator()); register(MANAGED_WEBSPACE, new HsManagedWebspaceBookingItemValidator());
@ -41,10 +44,14 @@ public class HsBookingItemEntityValidators {
} }
public static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) { public static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) {
final var violations = HsBookingItemEntityValidators.forType(entityToSave.getType()).validate(entityToSave); final var violations = validate(entityToSave);
if (!violations.isEmpty()) { if (!violations.isEmpty()) {
throw new ValidationException(violations.toString()); throw new ValidationException(violations.toString());
} }
return entityToSave; 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.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; 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.EnumerationPropertyValidator.enumerationProperty;
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; 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() 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.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; 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.BooleanPropertyValidator.booleanProperty;
import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; 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.IntegerPropertyValidator.integerProperty;
@ -25,4 +27,11 @@ class HsManagedServerBookingItemValidator extends HsEntityValidator<HsBookingIte
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional() 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.UUID;
import java.util.function.BiConsumer; 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 @RestController
public class HsHostingAssetController implements HsHostingAssetsApi { public class HsHostingAssetController implements HsHostingAssetsApi {

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.hosting.asset; 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.api.HsHostingAssetPropsApi;
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -15,7 +15,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
@Override @Override
public ResponseEntity<List<String>> listAssetTypes() { public ResponseEntity<List<String>> listAssetTypes() {
final var resource = HsHostingAssetEntityValidators.types().stream() final var resource = HsHostingAssetEntityValidator.types().stream()
.map(Enum::name) .map(Enum::name)
.toList(); .toList();
return ResponseEntity.ok(resource); return ResponseEntity.ok(resource);
@ -25,7 +25,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
public ResponseEntity<List<Object>> listAssetTypeProps( public ResponseEntity<List<Object>> listAssetTypeProps(
final HsHostingAssetTypeResource assetType) { 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(); final List<Map<String, Object>> resource = propValidators.properties();
return ResponseEntity.ok(toListOfObjects(resource)); return ResponseEntity.ok(toListOfObjects(resource));
} }

View File

@ -1,22 +1,25 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; 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.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
import org.apache.commons.collections4.ListUtils;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static java.util.Arrays.stream; 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.CLOUD_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
@UtilityClass public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> {
public class HsHostingAssetEntityValidators {
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType>> validators = new HashMap<>(); private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType>> validators = new HashMap<>();
static { static {
@ -40,12 +43,30 @@ public class HsHostingAssetEntityValidators {
return validators.keySet(); return validators.keySet();
} }
public static HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { 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()) { if (!violations.isEmpty()) {
throw new ValidationException(violations.toString()); throw new ValidationException(violations.toString());
} }
return entityToSave; 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.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import static java.util.Arrays.stream; 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>> { 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; return result;
} }
public List<Map<String, Object>> properties() { public final List<Map<String, Object>> properties() {
final var mapper = new ObjectMapper(); final var mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
return Arrays.stream(propertyValidators) 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; 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 com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -15,10 +14,6 @@ public abstract class HsPropertyValidator<T> {
final String propertyName; final String propertyName;
private Boolean required; 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() { public HsPropertyValidator<T> required() {
required = Boolean.TRUE; required = Boolean.TRUE;
return this; return this;

View File

@ -5,10 +5,11 @@ import org.junit.jupiter.api.Test;
import jakarta.validation.ValidationException; 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_SERVER;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; 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.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.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
@ -39,6 +40,6 @@ class HsBookingItemEntityValidatorsUnitTest {
final var result = HsBookingItemEntityValidators.types(); final var result = HsBookingItemEntityValidators.types();
// then // 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 java.util.Map;
import static java.util.List.of;
import static java.util.Map.entry; 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.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.HsBookingItemEntityValidators.forType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -15,7 +19,6 @@ class HsCloudServerBookingItemValidatorUnitTest {
@Test @Test
void validatesProperties() { void validatesProperties() {
// given // given
final var validator = HsBookingItemEntityValidators.forType(CLOUD_SERVER);
final var cloudServerBookingItemEntity = HsBookingItemEntity.builder() final var cloudServerBookingItemEntity = HsBookingItemEntity.builder()
.type(CLOUD_SERVER) .type(CLOUD_SERVER)
.resources(Map.ofEntries( .resources(Map.ofEntries(
@ -28,7 +31,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
.build(); .build();
// when // when
final var result = validator.validate(cloudServerBookingItemEntity); final var result = HsBookingItemEntityValidators.validate(cloudServerBookingItemEntity);
// then // then
assertThat(result).containsExactly("'resources.SLA-EMail' is not expected but is set to 'true'"); 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=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=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 java.util.Map;
import static java.util.List.of;
import static java.util.Map.entry; 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.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.HsBookingItemEntityValidators.forType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -15,7 +19,6 @@ class HsManagedServerBookingItemValidatorUnitTest {
@Test @Test
void validatesProperties() { void validatesProperties() {
// given // given
final var validator = HsBookingItemEntityValidators.forType(MANAGED_SERVER);
final var mangedServerBookingItemEntity = HsBookingItemEntity.builder() final var mangedServerBookingItemEntity = HsBookingItemEntity.builder()
.type(MANAGED_SERVER) .type(MANAGED_SERVER)
.resources(Map.ofEntries( .resources(Map.ofEntries(
@ -28,7 +31,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
.build(); .build();
// when // when
final var result = validator.validate(mangedServerBookingItemEntity); final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity);
// then // then
assertThat(result).containsExactly("'resources.SLA-EMail' is expected to be false because resources.SLA-Platform=BASIC but is true"); 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-Office, required=false, falseIf={SLA-Platform=BASIC}}",
"{type=boolean, propertyName=SLA-Web, 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 java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; 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; import static org.assertj.core.api.Assertions.assertThat;
class HsManagedWebspaceBookingItemValidatorUnitTest { class HsManagedWebspaceBookingItemValidatorUnitTest {
@ -25,10 +24,9 @@ class HsManagedWebspaceBookingItemValidatorUnitTest {
entry("SLA-EMail", true) entry("SLA-EMail", true)
)) ))
.build(); .build();
final var validator = forType(mangedServerBookingItemEntity.getType());
// when // when
final var result = validator.validate(mangedServerBookingItemEntity); final var result = HsBookingItemEntityValidators.validate(mangedServerBookingItemEntity);
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
@ -40,7 +38,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest {
@Test @Test
void containsAllValidations() { void containsAllValidations() {
// when // when
final var validator = forType(MANAGED_WEBSPACE); final var validator = HsBookingItemEntityValidators.forType(MANAGED_WEBSPACE);
// then // then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( 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 java.util.UUID;
import static java.util.Map.entry; 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.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat; 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 java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; 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; import static org.assertj.core.api.Assertions.assertThat;
class HsCloudServerHostingAssetValidatorUnitTest { class HsCloudServerHostingAssetValidatorUnitTest {

View File

@ -6,11 +6,11 @@ import org.junit.jupiter.api.Test;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; 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.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
class HsHostingAssetEntityValidatorsUnitTest { class HsHostingAssetEntityValidatorUnitTest {
@Test @Test
void validThrowsException() { void validThrowsException() {

View File

@ -7,7 +7,7 @@ import java.util.Map;
import static java.util.Map.entry; 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_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; import static org.assertj.core.api.Assertions.assertThat;
class HsManagedServerHostingAssetValidatorUnitTest { class HsManagedServerHostingAssetValidatorUnitTest {

View File

@ -30,7 +30,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
@Test @Test
void validatesIdentifier() { void validatesIdentifier() {
// given // given
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE) .type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity) .parentAsset(mangedServerAssetEntity)
@ -47,7 +47,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
@Test @Test
void validatesUnknownProperties() { void validatesUnknownProperties() {
// given // given
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE) .type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity) .parentAsset(mangedServerAssetEntity)
@ -67,7 +67,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
@Test @Test
void validatesValidEntity() { void validatesValidEntity() {
// given // given
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var validator = HsHostingAssetEntityValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE) .type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity) .parentAsset(mangedServerAssetEntity)