hierarchical-validation-baseline #59
@ -6,11 +6,13 @@ import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
|||||||
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
||||||
|
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
@ -77,14 +79,18 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> validateSubEntities(final HsBookingItemEntity bookingItem) {
|
protected List<String> validateSubEntities(final HsBookingItemEntity bookingItem) {
|
||||||
return stream(propertyValidators)
|
return Stream.concat(
|
||||||
.filter(ValidatableProperty::isTotalsValidator)
|
stream(propertyValidators)
|
||||||
.map(prop -> validateMaxTotalValue(bookingItem, prop))
|
.map(propDef -> propDef.validateTotals(bookingItem))
|
||||||
.filter(Objects::nonNull)
|
.flatMap(Collection::stream),
|
||||||
.toList();
|
stream(propertyValidators)
|
||||||
|
.filter(ValidatableProperty::isTotalsValidator)
|
||||||
|
.map(prop -> validateMaxTotalValue(bookingItem, prop))
|
||||||
|
).filter(Objects::nonNull).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String validateMaxTotalValue(
|
// FIXME: convert into generic shape like multi-options validator
|
||||||
|
private static String validateMaxTotalValue(
|
||||||
final HsBookingItemEntity bookingItem,
|
final HsBookingItemEntity bookingItem,
|
||||||
final ValidatableProperty<?> propDef) {
|
final ValidatableProperty<?> propDef) {
|
||||||
final var propName = propDef.propertyName();
|
final var propName = propDef.propertyName();
|
||||||
|
@ -25,21 +25,22 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
|
|||||||
integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(),
|
integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(),
|
||||||
integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(),
|
integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(),
|
||||||
integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(),
|
integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(),
|
||||||
integerProperty("MultiOptions").min(1).max(100).step(1).required()
|
integerProperty("MultiOptions").min(1).max(100).step(1).withDefault(1)
|
||||||
.eachComprising( 25, unixUsers())
|
.eachComprising( 25, unixUsers())
|
||||||
.eachComprising( 5, databaseUsers())
|
.eachComprising( 5, databaseUsers())
|
||||||
.eachComprising( 5, databases())
|
.eachComprising( 5, databases())
|
||||||
.eachComprising(250, eMailAddresses()),
|
.eachComprising(250, eMailAddresses()),
|
||||||
integerProperty("Daemons").min(0).max(10).optional(),
|
integerProperty("Daemons").min(0).max(10).withDefault(0),
|
||||||
booleanProperty("Online Office Server").optional(),
|
booleanProperty("Online Office Server").optional(),
|
||||||
enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional()
|
enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").withDefault("BASIC")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> unixUsers() {
|
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> unixUsers() {
|
||||||
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
||||||
final var unixUserCount = entity.getSubHostingAssets().stream()
|
final var unixUserCount = entity.getSubHostingAssets().stream()
|
||||||
.filter(bi -> bi.getType() == UNIX_USER)
|
.flatMap(ha -> ha.getSubHostingAssets().stream())
|
||||||
|
.filter(ha -> ha.getType() == UNIX_USER)
|
||||||
.count();
|
.count();
|
||||||
final long limitingValue = prop.getValue(entity.getResources());
|
final long limitingValue = prop.getValue(entity.getResources());
|
||||||
if (unixUserCount > factor*limitingValue) {
|
if (unixUserCount > factor*limitingValue) {
|
||||||
@ -52,6 +53,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
|
|||||||
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> databaseUsers() {
|
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> databaseUsers() {
|
||||||
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
||||||
final var unixUserCount = entity.getSubHostingAssets().stream()
|
final var unixUserCount = entity.getSubHostingAssets().stream()
|
||||||
|
.flatMap(ha -> ha.getSubHostingAssets().stream())
|
||||||
.filter(bi -> bi.getType() == PGSQL_USER || bi.getType() == MARIADB_USER )
|
.filter(bi -> bi.getType() == PGSQL_USER || bi.getType() == MARIADB_USER )
|
||||||
.count();
|
.count();
|
||||||
final long limitingValue = prop.getValue(entity.getResources());
|
final long limitingValue = prop.getValue(entity.getResources());
|
||||||
@ -65,6 +67,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
|
|||||||
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> databases() {
|
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> databases() {
|
||||||
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
||||||
final var unixUserCount = entity.getSubHostingAssets().stream()
|
final var unixUserCount = entity.getSubHostingAssets().stream()
|
||||||
|
.flatMap(ha -> ha.getSubHostingAssets().stream())
|
||||||
.filter(bi -> bi.getType()==PGSQL_USER || bi.getType()==MARIADB_USER )
|
.filter(bi -> bi.getType()==PGSQL_USER || bi.getType()==MARIADB_USER )
|
||||||
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
|
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
|
||||||
.filter(ha -> ha.getType()==PGSQL_DATABASE || ha.getType()==MARIADB_DATABASE))
|
.filter(ha -> ha.getType()==PGSQL_DATABASE || ha.getType()==MARIADB_DATABASE))
|
||||||
@ -80,6 +83,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
|
|||||||
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> eMailAddresses() {
|
private static TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> eMailAddresses() {
|
||||||
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
|
||||||
final var unixUserCount = entity.getSubHostingAssets().stream()
|
final var unixUserCount = entity.getSubHostingAssets().stream()
|
||||||
|
.flatMap(ha -> ha.getSubHostingAssets().stream())
|
||||||
.filter(bi -> bi.getType() == DOMAIN_EMAIL_SETUP)
|
.filter(bi -> bi.getType() == DOMAIN_EMAIL_SETUP)
|
||||||
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
|
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
|
||||||
.filter(ha -> ha.getType()==EMAIL_ADDRESS))
|
.filter(ha -> ha.getType()==EMAIL_ADDRESS))
|
||||||
|
@ -8,14 +8,17 @@ import net.hostsharing.hsadminng.mapper.Array;
|
|||||||
import org.apache.commons.lang3.function.TriFunction;
|
import org.apache.commons.lang3.function.TriFunction;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public abstract class ValidatableProperty<T> {
|
public abstract class ValidatableProperty<T> {
|
||||||
@ -64,7 +67,7 @@ public abstract class ValidatableProperty<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTotalsValidator() {
|
public boolean isTotalsValidator() {
|
||||||
return isTotalsValidator;
|
return isTotalsValidator || asTotalLimitValidators != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer thresholdPercentage() {
|
public Integer thresholdPercentage() {
|
||||||
@ -155,4 +158,20 @@ public abstract class ValidatableProperty<T> {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> validateTotals(final HsBookingItemEntity bookingItem) {
|
||||||
|
if (asTotalLimitValidators==null) {
|
||||||
|
return emptyList();
|
||||||
|
}
|
||||||
|
return asTotalLimitValidators.stream()
|
||||||
|
.map(v -> v.apply(bookingItem))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(v -> wrap(v))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String wrap(final String v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,23 @@ package net.hostsharing.hsadminng.hs.booking.item.validators;
|
|||||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
|
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.List.of;
|
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 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.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD;
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.forType;
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.forType;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@ -119,4 +127,79 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
|||||||
"D-12345:Test-Project:null.parentItem.total Traffic is 5500 GB exceeds max total Traffic 5000 GB"
|
"D-12345:Test-Project:null.parentItem.total Traffic is 5500 GB exceeds max total Traffic 5000 GB"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesExceedingTotals() {
|
||||||
|
// given
|
||||||
|
final var managedWebspaceBookingItem = HsBookingItemEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.caption("test Managed-Webspace")
|
||||||
|
.resources(ofEntries(
|
||||||
|
entry("SSD", 100),
|
||||||
|
entry("Traffic", 1000),
|
||||||
|
entry("MultiOptions", 1)
|
||||||
|
))
|
||||||
|
.subHostingAssets(of(
|
||||||
|
HsHostingAssetEntity.builder()
|
||||||
|
.type(HsHostingAssetType.MANAGED_WEBSPACE)
|
||||||
|
.identifier("abc00")
|
||||||
|
.subHostingAssets(concat(
|
||||||
|
generate(26, HsHostingAssetType.UNIX_USER, "xyz00-%c%c"),
|
||||||
|
generateDbUsersWithDatabases(3, HsHostingAssetType.PGSQL_USER,
|
||||||
|
"xyz00_%c%c",
|
||||||
|
1, HsHostingAssetType.PGSQL_DATABASE
|
||||||
|
),
|
||||||
|
generateDbUsersWithDatabases(3, HsHostingAssetType.MARIADB_USER,
|
||||||
|
"xyz00_%c%c",
|
||||||
|
1, HsHostingAssetType.MARIADB_DATABASE
|
||||||
|
)
|
||||||
|
))
|
||||||
|
.build()
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = HsBookingItemEntityValidator.doValidate(managedWebspaceBookingItem);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"MultiOptions=1 allows at maximum 25 unix users, but 26 found",
|
||||||
|
"MultiOptions=1 allows at maximum 5 database users, but 6 found",
|
||||||
|
"MultiOptions=1 allows at maximum 5 databases, but 6 found"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
private List<HsHostingAssetEntity> concat(final List<HsHostingAssetEntity>... hostingAssets) {
|
||||||
|
return stream(hostingAssets)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<HsHostingAssetEntity> generate(final int count, final HsHostingAssetType hostingAssetType,
|
||||||
|
final String identifierPattern) {
|
||||||
|
return IntStream.range(0, count)
|
||||||
|
.mapToObj(number -> HsHostingAssetEntity.builder()
|
||||||
|
.type(hostingAssetType)
|
||||||
|
.identifier(identifierPattern.formatted((number/'a')+'a', (number%'a')+'a'))
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<HsHostingAssetEntity> generateDbUsersWithDatabases(
|
||||||
|
final int userCount,
|
||||||
|
final HsHostingAssetType directAssetType,
|
||||||
|
final String directAssetIdentifierFormat, final int dbCount,
|
||||||
|
final HsHostingAssetType subAssetType) {
|
||||||
|
return IntStream.range(0, userCount)
|
||||||
|
.mapToObj(n -> HsHostingAssetEntity.builder()
|
||||||
|
.type(directAssetType)
|
||||||
|
.identifier(directAssetIdentifierFormat.formatted((n/'a')+'a', (n%'a')+'a'))
|
||||||
|
.subHostingAssets(
|
||||||
|
generate(dbCount, subAssetType, "xyz00_%c%c%%c%%c".formatted((n/'a')+'a', (n%'a')+'a'))
|
||||||
|
)
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user