diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsCloudServerAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsCloudServerAssetValidator.java new file mode 100644 index 00000000..acef7bd9 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsCloudServerAssetValidator.java @@ -0,0 +1,27 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import org.springframework.stereotype.Component; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; +import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; + +@Component +class HsCloudServerAssetValidator extends HsEntityValidator { + + static { + HsEntityValidator.register(CLOUD_SERVER, new HsCloudServerAssetValidator()); + } + + public HsCloudServerAssetValidator() { + super( + integerProperty("CPUs").min(1).max(32).required(), + integerProperty("RAM").unit("GB").min(1).max(128).required(), + integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), + integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), + integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), + enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional() + ); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java index ae13d0c8..9b8b484e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi; import net.hostsharing.hsadminng.context.Context; @@ -124,7 +124,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { } private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { - final var violations = HsHostingAssetValidator.forType(entityToSave.getType()).validate(entityToSave); + final var violations = HsEntityValidator.forType(entityToSave.getType()).validate(entityToSave); if (!violations.isEmpty()) { throw new ValidationException(violations.toString()); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java index 8a3f1523..50e236eb 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsController.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; 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> listAssetTypes() { - final var resource = HsHostingAssetValidator.types().stream() + final var resource = HsEntityValidator.types().stream() .map(Enum::name) .toList(); return ResponseEntity.ok(resource); @@ -25,7 +25,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi { public ResponseEntity> listAssetTypeProps( final HsHostingAssetTypeResource assetType) { - final var propValidators = HsHostingAssetValidator.forType(HsHostingAssetType.of(assetType)); + final var propValidators = HsEntityValidator.forType(HsHostingAssetType.of(assetType)); final List> resource = propValidators.properties(); return ResponseEntity.ok(toListOfObjects(resource)); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedServerAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedServerAssetValidator.java new file mode 100644 index 00000000..95bbfb96 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedServerAssetValidator.java @@ -0,0 +1,33 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; +import org.springframework.stereotype.Component; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; +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; + +@Component +class HsManagedServerAssetValidator extends HsEntityValidator { + + static { + HsEntityValidator.register(MANAGED_SERVER, new HsManagedServerAssetValidator()); + } + + public HsManagedServerAssetValidator() { + super( + integerProperty("CPUs").min(1).max(32).required(), + integerProperty("RAM").unit("GB").min(1).max(128).required(), + integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), + integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), + integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), + enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional(), + booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").optional(), + 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() + ); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java similarity index 67% rename from src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidator.java rename to src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java index 709a3ec5..4deade19 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java @@ -1,20 +1,20 @@ -package net.hostsharing.hsadminng.hs.hosting.asset.validator; +package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; 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; +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; @Component -class HsManagedWebspaceAssetValidator extends HsHostingAssetValidator { +class HsManagedWebspaceAssetValidator extends HsEntityValidator { static { - HsHostingAssetValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator()); + HsEntityValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator()); } public HsManagedWebspaceAssetValidator() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java deleted file mode 100644 index 15936ea3..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java +++ /dev/null @@ -1,172 +0,0 @@ -package net.hostsharing.hsadminng.hs.hosting.asset.validator; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; - -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -@RequiredArgsConstructor -public abstract class HsHostingAssetPropertyValidator { - - final Class type; - final String propertyName; - private Boolean required; - - public static Map.Entry defType(K k, V v) { - return new SimpleImmutableEntry<>(k, v); - } - - public HsHostingAssetPropertyValidator required() { - required = Boolean.TRUE; - return this; - } - - public HsHostingAssetPropertyValidator optional() { - required = Boolean.FALSE; - return this; - } - - public final List validate(final Map props) { - final var result = new ArrayList(); - final var propValue = props.get(propertyName); - if (propValue == null) { - if (required) { - result.add("'config." + propertyName + "' is required but missing"); - } - } - if (propValue != null){ - if ( type.isInstance(propValue)) { - //noinspection unchecked - validate(result, (T) propValue, props); - } else { - result.add("'config." + propertyName + "' is expected to be of type " + type + ", " + - "but is of type '" + propValue.getClass().getSimpleName() + "'"); - } - } - return result; - } - - protected abstract void validate(final ArrayList result, final T propValue, final Map props); - - public void verifyConsistency(final Map.Entry typeDef) { - if (required == null ) { - throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); - } - } - - public Map toMap(final ObjectMapper mapper) { - final Map map = mapper.convertValue(this, Map.class); - map.put("type", simpleTypeName()); - return map; - } - - protected abstract String simpleTypeName(); -} - -@Setter -class IntegerPropertyValidator extends HsHostingAssetPropertyValidator{ - - private String unit; - private Integer min; - private Integer max; - private Integer step; - - public static IntegerPropertyValidator integerProperty(final String propertyName) { - return new IntegerPropertyValidator(propertyName); - } - - private IntegerPropertyValidator(final String propertyName) { - super(Integer.class, propertyName); - } - - - @Override - protected void validate(final ArrayList result, final Integer propValue, final Map props) { - if (min != null && propValue < min) { - result.add("'config." + propertyName + "' is expected to be >= " + min + " but is " + propValue); - } - if (max != null && propValue > max) { - result.add("'config." + propertyName + "' is expected to be <= " + max + " but is " + propValue); - } - if (step != null && propValue % step != 0) { - result.add("'config." + propertyName + "' is expected to be multiple of " + step + " but is " + propValue); - } - } - - @Override - protected String simpleTypeName() { - return "integer"; - } -} - -@Setter -class EnumPropertyValidator extends HsHostingAssetPropertyValidator { - - private String[] values; - - private EnumPropertyValidator(final String propertyName) { - super(String.class, propertyName); - } - - public static EnumPropertyValidator enumerationProperty(final String propertyName) { - return new EnumPropertyValidator(propertyName); - } - - public HsHostingAssetPropertyValidator values(final String... values) { - this.values = values; - return this; - } - - @Override - protected void validate(final ArrayList result, final String propValue, final Map props) { - if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) { - result.add("'config." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'"); - } - } - - @Override - protected String simpleTypeName() { - return "enumeration"; - } -} - -@Setter -class BooleanPropertyValidator extends HsHostingAssetPropertyValidator { - - private Map.Entry falseIf; - - private BooleanPropertyValidator(final String propertyName) { - super(Boolean.class, propertyName); - } - - public static BooleanPropertyValidator booleanProperty(final String propertyName) { - return new BooleanPropertyValidator(propertyName); - } - - HsHostingAssetPropertyValidator falseIf(final String refPropertyName, final String refPropertyValue) { - this.falseIf = new SimpleImmutableEntry<>(refPropertyName, refPropertyValue); - return this; - } - - @Override - protected void validate(final ArrayList result, final Boolean propValue, final Map props) { - if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) { - if (propValue) { - result.add("'config." + propertyName + "' is expected to be false because " + - "config." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue); - } - } - } - - @Override - protected String simpleTypeName() { - return "boolean"; - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java deleted file mode 100644 index d713330b..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java +++ /dev/null @@ -1,96 +0,0 @@ -package net.hostsharing.hsadminng.hs.hosting.asset.validator; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.ObjectMapper; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; -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; - -import static java.util.Arrays.stream; -import static net.hostsharing.hsadminng.hs.hosting.asset.validator.EnumPropertyValidator.enumerationProperty; -import static net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetPropertyValidator.defType; -import static net.hostsharing.hsadminng.hs.hosting.asset.validator.BooleanPropertyValidator.booleanProperty; -import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerPropertyValidator.integerProperty; - -public class HsHostingAssetValidator { - - private static final Map 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(), - integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), - integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), - integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), - enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional())), - defType(HsHostingAssetType.MANAGED_SERVER, new HsHostingAssetValidator( - integerProperty("CPUs").min(1).max(32).required(), - integerProperty("RAM").unit("GB").min(1).max(128).required(), - integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), - integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), - integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), - enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional(), - booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").optional(), - 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())))); - static { - validators.entrySet().forEach(typeDef -> { - stream(typeDef.getValue().propertyValidators).forEach( entry -> { - entry.verifyConsistency(typeDef); - }); - }); - } - 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); - } - - HsHostingAssetValidator(final HsHostingAssetPropertyValidator... validators) { - propertyValidators = validators; - } - - public static Set types() { - return validators.keySet(); - } - - public List validate(final HsHostingAssetEntity assetEntity) { - final var result = new ArrayList(); - assetEntity.getConfig().keySet().forEach( givenPropName -> { - if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) { - result.add("'config." + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'"); - } - }); - stream(propertyValidators).forEach(pv -> { - result.addAll(pv.validate(assetEntity.getConfig())); - }); - return result; - } - - public List> properties() { - final var mapper = new ObjectMapper(); - mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - return Arrays.stream(propertyValidators) - .map(propertyValidator -> propertyValidator.toMap(mapper)) - .map(HsHostingAssetValidator::asKeyValueMap) - .toList(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static Map asKeyValueMap(final Map map) { - return (Map) map; - } - -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java new file mode 100644 index 00000000..8b6f1232 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/BooleanPropertyValidator.java @@ -0,0 +1,42 @@ +package net.hostsharing.hsadminng.hs.validation; + +import lombok.Setter; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; + +@Setter +public class BooleanPropertyValidator extends HsPropertyValidator { + + private Map.Entry falseIf; + + private BooleanPropertyValidator(final String propertyName) { + super(Boolean.class, propertyName); + } + + public static BooleanPropertyValidator booleanProperty(final String propertyName) { + return new BooleanPropertyValidator(propertyName); + } + + public HsPropertyValidator falseIf(final String refPropertyName, final String refPropertyValue) { + this.falseIf = new AbstractMap.SimpleImmutableEntry<>(refPropertyName, refPropertyValue); + return this; + } + + @Override + protected void validate(final ArrayList result, final Boolean propValue, final Map props) { + if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) { + if (propValue) { + result.add("'config." + propertyName + "' is expected to be false because " + + "config." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue); + } + } + } + + @Override + protected String simpleTypeName() { + return "boolean"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java new file mode 100644 index 00000000..e0d170d0 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/EnumerationPropertyValidator.java @@ -0,0 +1,38 @@ +package net.hostsharing.hsadminng.hs.validation; + +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; + +@Setter +public class EnumerationPropertyValidator extends HsPropertyValidator { + + private String[] values; + + private EnumerationPropertyValidator(final String propertyName) { + super(String.class, propertyName); + } + + public static EnumerationPropertyValidator enumerationProperty(final String propertyName) { + return new EnumerationPropertyValidator(propertyName); + } + + public HsPropertyValidator values(final String... values) { + this.values = values; + return this; + } + + @Override + protected void validate(final ArrayList result, final String propValue, final Map props) { + if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) { + result.add("'config." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'"); + } + } + + @Override + protected String simpleTypeName() { + return "enumeration"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java new file mode 100644 index 00000000..3288b8fb --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -0,0 +1,72 @@ +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.hosting.asset.HsHostingAssetEntity; +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; + +import static java.util.Arrays.stream; +import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty; +import static net.hostsharing.hsadminng.hs.validation.HsPropertyValidator.defType; +import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; + +public class HsEntityValidator { + + private static final Map, HsEntityValidator> validators = new HashMap<>(); + private final HsPropertyValidator[] propertyValidators; + + public static void register(final Enum type, final HsEntityValidator validator) { + stream(validator.propertyValidators).forEach( entry -> { + entry.verifyConsistency(Map.entry(type, validator)); + }); + validators.put(type, validator); + } + + public static HsEntityValidator forType(final HsHostingAssetType type) { + return validators.get(type); + } + + public HsEntityValidator(final HsPropertyValidator... validators) { + propertyValidators = validators; + } + + public static Set> types() { + return validators.keySet(); + } + + public List validate(final HsHostingAssetEntity assetEntity) { + final var result = new ArrayList(); + assetEntity.getConfig().keySet().forEach( givenPropName -> { + if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) { + result.add("'config." + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'"); + } + }); + stream(propertyValidators).forEach(pv -> { + result.addAll(pv.validate(assetEntity.getConfig())); + }); + return result; + } + + public List> properties() { + final var mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + return Arrays.stream(propertyValidators) + .map(propertyValidator -> propertyValidator.toMap(mapper)) + .map(HsEntityValidator::asKeyValueMap) + .toList(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static Map asKeyValueMap(final Map map) { + return (Map) map; + } + +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java new file mode 100644 index 00000000..4408a1f3 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java @@ -0,0 +1,68 @@ +package net.hostsharing.hsadminng.hs.validation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +public abstract class HsPropertyValidator { + + final Class type; + final String propertyName; + private Boolean required; + + public static Map.Entry defType(K k, V v) { + return new SimpleImmutableEntry<>(k, v); + } + + public HsPropertyValidator required() { + required = Boolean.TRUE; + return this; + } + + public HsPropertyValidator optional() { + required = Boolean.FALSE; + return this; + } + + public final List validate(final Map props) { + final var result = new ArrayList(); + final var propValue = props.get(propertyName); + if (propValue == null) { + if (required) { + result.add("'config." + propertyName + "' is required but missing"); + } + } + if (propValue != null){ + if ( type.isInstance(propValue)) { + //noinspection unchecked + validate(result, (T) propValue, props); + } else { + result.add("'config." + propertyName + "' is expected to be of type " + type + ", " + + "but is of type '" + propValue.getClass().getSimpleName() + "'"); + } + } + return result; + } + + protected abstract void validate(final ArrayList result, final T propValue, final Map props); + + public void verifyConsistency(final Map.Entry, HsEntityValidator> typeDef) { + if (required == null ) { + throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); + } + } + + public Map toMap(final ObjectMapper mapper) { + final Map map = mapper.convertValue(this, Map.class); + map.put("type", simpleTypeName()); + return map; + } + + protected abstract String simpleTypeName(); +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java new file mode 100644 index 00000000..d0727dd4 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/IntegerPropertyValidator.java @@ -0,0 +1,42 @@ +package net.hostsharing.hsadminng.hs.validation; + +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Map; + +@Setter +public class IntegerPropertyValidator extends HsPropertyValidator { + + private String unit; + private Integer min; + private Integer max; + private Integer step; + + public static IntegerPropertyValidator integerProperty(final String propertyName) { + return new IntegerPropertyValidator(propertyName); + } + + private IntegerPropertyValidator(final String propertyName) { + super(Integer.class, propertyName); + } + + + @Override + protected void validate(final ArrayList result, final Integer propValue, final Map props) { + if (min != null && propValue < min) { + result.add("'config." + propertyName + "' is expected to be >= " + min + " but is " + propValue); + } + if (max != null && propValue > max) { + result.add("'config." + propertyName + "' is expected to be <= " + max + " but is " + propValue); + } + if (step != null && propValue % step != 0) { + result.add("'config." + propertyName + "' is expected to be multiple of " + step + " but is " + propValue); + } + } + + @Override + protected String simpleTypeName() { + return "integer"; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config b/src/main/java/net/hostsharing/hsadminng/hs/validation/lombok.config similarity index 100% rename from src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config rename to src/main/java/net/hostsharing/hsadminng/hs/validation/lombok.config diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java similarity index 79% rename from src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidatorUnitTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java index 90ff4e42..86836cec 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java @@ -1,6 +1,6 @@ -package net.hostsharing.hsadminng.hs.hosting.asset.validator; +package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import org.junit.jupiter.api.Test; import java.util.Map; @@ -9,12 +9,12 @@ 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 { +class HsManagedServerValidatorUnitTest { @Test void validatesDependentProperties() { // given - final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER); + final var validator = HsEntityValidator.forType(MANAGED_SERVER); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_SERVER) .config(Map.ofEntries( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java similarity index 88% rename from src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidatorUnitTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java index 952498e0..2bc82650 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsManagedWebspaceAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java @@ -1,7 +1,7 @@ -package net.hostsharing.hsadminng.hs.hosting.asset.validator; +package net.hostsharing.hsadminng.hs.hosting.asset; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import org.junit.jupiter.api.Test; @@ -16,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; class HsManagedWebspaceAssetValidatorUnitTest { @SuppressWarnings("unused") // just to make sure the class is loaded - HsHostingAssetValidator validator = new HsManagedWebspaceAssetValidator(); + HsEntityValidator validator = new HsManagedWebspaceAssetValidator(); final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder() .debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build() @@ -35,7 +35,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesIdentifier() { // given - final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -58,7 +58,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesMissingProperties() { // given - final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -79,7 +79,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesUnknownProperties() { // given - final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -102,7 +102,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesValidProperties() { // given - final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity)