hosting-asset-validation-baseline #56

Merged
hsh-michaelhoennig merged 7 commits from hosting-asset-validation-baseline into master 2024-05-30 10:45:13 +02:00
7 changed files with 151 additions and 55 deletions
Showing only changes of commit 409f6c973c - Show all commits

View File

@ -38,7 +38,7 @@ public abstract class HsHostingAssetPropertyValidator<T> {
final var propValue = props.get(propertyName);
if (propValue == null) {
if (required) {
result.add("'" + propertyName + "' is required but missing");
result.add("'config." + propertyName + "' is required but missing");
}
}
if (propValue != null){
@ -46,7 +46,7 @@ public abstract class HsHostingAssetPropertyValidator<T> {
//noinspection unchecked
validate(result, (T) propValue, props);
} else {
result.add("'" + propertyName + "' is expected to be of type " + type + ", " +
result.add("'config." + propertyName + "' is expected to be of type " + type + ", " +
"but is of type '" + propValue.getClass().getSimpleName() + "'");
}
}
@ -90,13 +90,13 @@ class IntegerPropertyValidator extends HsHostingAssetPropertyValidator<Integer>{
@Override
protected void validate(final ArrayList<String> result, final Integer propValue, final Map<String, Object> props) {
if (min != null && propValue < min) {
result.add("'" + propertyName + "' is expected to be >= " + min + " but is " + propValue);
result.add("'config." + 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);
result.add("'config." + 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);
result.add("'config." + propertyName + "' is expected to be multiple of " + step + " but is " + propValue);
}
}
@ -127,7 +127,7 @@ class EnumPropertyValidator extends HsHostingAssetPropertyValidator<String> {
@Override
protected void validate(final ArrayList<String> result, final String propValue, final Map<String, Object> 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 + "'");
result.add("'config." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'");
}
}
@ -159,8 +159,8 @@ class BooleanPropertyValidator extends HsHostingAssetPropertyValidator<Boolean>
protected void validate(final ArrayList<String> result, final Boolean propValue, final Map<String, Object> props) {
if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) {
if (propValue) {
result.add("'" + propertyName + "' is expected to be false because " +
falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
result.add("'config." + propertyName + "' is expected to be false because " +
"config." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
}
}
}

View File

@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -20,7 +21,7 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerProper
public class HsHostingAssetValidator {
private static final Map<HsHostingAssetType, HsHostingAssetValidator> validators = Map.ofEntries(
private static final Map<HsHostingAssetType, HsHostingAssetValidator> validators = new HashMap<>(Map.ofEntries(
defType(HsHostingAssetType.CLOUD_SERVER, new HsHostingAssetValidator(
integerProperty("CPUs").min(1).max(32).required(),
integerProperty("RAM").unit("GB").min(1).max(128).required(),
@ -39,15 +40,7 @@ public class HsHostingAssetValidator {
booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(),
booleanProperty("SLA-PgSQL").falseIf("SLA-Platform", "BASIC").optional(),
booleanProperty("SLA-Office").falseIf("SLA-Platform", "BASIC").optional(),
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional())),
defType(HsHostingAssetType.MANAGED_WEBSPACE, new HsHostingAssetValidator(
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("Daemons").min(0).max(10).optional(),
booleanProperty("Online Office Server").optional())
));
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional()))));
static {
validators.entrySet().forEach(typeDef -> {
stream(typeDef.getValue().propertyValidators).forEach( entry -> {
@ -57,6 +50,10 @@ public class HsHostingAssetValidator {
}
private final HsHostingAssetPropertyValidator<?>[] propertyValidators;
public static void register(final HsHostingAssetType type, final HsHostingAssetValidator validator) {
validators.put(type, validator);
}
public static HsHostingAssetValidator forType(final HsHostingAssetType type) {
return validators.get(type);
}
@ -73,7 +70,7 @@ public class HsHostingAssetValidator {
final var result = new ArrayList<String>();
assetEntity.getConfig().keySet().forEach( givenPropName -> {
if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) {
result.add("'" + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'");
result.add("'config." + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'");
}
});
stream(propertyValidators).forEach(pv -> {

View File

@ -0,0 +1,40 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import org.springframework.stereotype.Component;
import java.util.List;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.BooleanPropertyValidator.booleanProperty;
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.EnumPropertyValidator.enumerationProperty;
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerPropertyValidator.integerProperty;
@Component
class HsManagedWebspaceAssetValidator extends HsHostingAssetValidator {
static {
HsHostingAssetValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator());
}
public HsManagedWebspaceAssetValidator() {
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("Daemons").min(0).max(10).optional(),
booleanProperty("Online Office Server").optional()
);
}
@Override
public List<String> validate(final HsHostingAssetEntity assetEntity) {
final var result = super.validate(assetEntity);
final var expectedIdentifierPattern = "^" + assetEntity.getParentAsset().getBookingItem().getDebitor().getDefaultPrefix() + "[0-9][0-9]$";
if ( !assetEntity.getIdentifier().matches(expectedIdentifierPattern)) {
result.add("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
}
return result;
}
}

View File

@ -33,8 +33,6 @@ create table if not exists hs_hosting_asset
constraint chk_hs_hosting_asset_has_booking_item_or_parent_asset check (bookingItemUuid is not null or parentAssetUuid is not null)
);
--//

View File

@ -215,7 +215,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
{
"parentAssetUuid": "%s",
"type": "MANAGED_WEBSPACE",
"identifier": "xyz00",
"identifier": "fir90",
"caption": "some new ManagedWebspace in client's ManagedServer",
"config": { "SSD": 100, "Traffic": 250 }
}
@ -229,7 +229,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
.body("", lenientlyEquals("""
{
"type": "MANAGED_WEBSPACE",
"identifier": "xyz00",
"identifier": "fir90",
"caption": "some new ManagedWebspace in client's ManagedServer",
"config": { "SSD": 100, "Traffic": 250 }
}
@ -271,7 +271,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
.body("", lenientlyEquals("""
{
"statusPhrase": "Bad Request",
"message": "['extra' is not expected but is '42', 'CPUs' is expected to be >= 1 but is 0, 'RAM' is required but missing, 'SSD' is required but missing, 'Traffic' is required but missing]"
"message": "['config.extra' is not expected but is '42', 'config.CPUs' is expected to be >= 1 but is 0, 'config.RAM' is required but missing, 'config.SSD' is required but missing, 'config.Traffic' is required but missing]"
}
""")); // @formatter:on
}

View File

@ -0,0 +1,35 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
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 org.assertj.core.api.Assertions.assertThat;
class HsHostingAssetValidatorUnitTest {
@Test
void validatesDependentProperties() {
// given
final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.config(Map.ofEntries(
entry("CPUs", 2),
entry("RAM", 25),
entry("SSD", 25),
entry("Traffic", 250),
entry("SLA-EMail", true)
))
.build();
// when
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
// then
assertThat(result).containsExactly("'config.SLA-EMail' is expected to be false because config.SLA-Platform=BASIC but is true");
}
}

View File

@ -1,6 +1,8 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
package net.hostsharing.hsadminng.hs.hosting.asset.validator;
import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import org.junit.jupiter.api.Test;
import java.util.Map;
@ -11,24 +13,66 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANA
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
import static org.assertj.core.api.Assertions.assertThat;
class HsHostingAssetValidatorUnitTest {
class HsManagedWebspaceAssetValidatorUnitTest {
@SuppressWarnings("unused") // just to make sure the class is loaded
HsHostingAssetValidator validator = new HsManagedWebspaceAssetValidator();
final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder()
.debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build()
)
.build();
final HsHostingAssetEntity mangedServerAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.bookingItem(managedServerBookingItem)
.config(Map.ofEntries(
entry("HDD", 0),
entry("SSD", 1),
entry("Traffic", 10)
))
.build();
@Test
void validatesIdentifier() {
// given
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity)
.identifier("xyz00")
.config(Map.ofEntries(
entry("HDD", 0),
entry("SSD", 1),
entry("Traffic", 10)
))
.build();
// when
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
// then
assertThat(result).containsExactly("'identifier' expected to match '^abc[0-9][0-9]$', but is 'xyz00'");
}
@Test
void validatesMissingProperties() {
// given
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.config(emptyMap())
.build();
.type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity)
.identifier("abc00")
.config(emptyMap())
.build();
// when
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'SSD' is required but missing",
"'Traffic' is required but missing"
"'config.SSD' is required but missing",
"'config.Traffic' is required but missing"
);
}
@ -38,6 +82,8 @@ class HsHostingAssetValidatorUnitTest {
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity)
.identifier("abc00")
.config(Map.ofEntries(
entry("HDD", 0),
entry("SSD", 1),
@ -50,29 +96,7 @@ class HsHostingAssetValidatorUnitTest {
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
// then
assertThat(result).containsExactly("'unknown' is not expected but is 'some value'");
}
@Test
void validatesDependentProperties() {
// given
final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.config(Map.ofEntries(
entry("CPUs", 2),
entry("RAM", 25),
entry("SSD", 25),
entry("Traffic", 250),
entry("SLA-EMail", true)
))
.build();
// when
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
// then
assertThat(result).containsExactly("'SLA-EMail' is expected to be false because SLA-Platform=BASIC but is true");
assertThat(result).containsExactly("'config.unknown' is not expected but is 'some value'");
}
@Test
@ -81,6 +105,8 @@ class HsHostingAssetValidatorUnitTest {
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.parentAsset(mangedServerAssetEntity)
.identifier("abc00")
.config(Map.ofEntries(
entry("HDD", 200),
entry("SSD", 25),