hosting-asset-validation-baseline #56
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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.validator.HsHostingAssetValidator;
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
@ -124,7 +124,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) {
|
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()) {
|
if (!violations.isEmpty()) {
|
||||||
throw new ValidationException(violations.toString());
|
throw new ValidationException(violations.toString());
|
||||||
}
|
}
|
||||||
|
@ -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.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.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 = HsHostingAssetValidator.types().stream()
|
final var resource = HsEntityValidator.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 = HsHostingAssetValidator.forType(HsHostingAssetType.of(assetType));
|
final var propValidators = HsEntityValidator.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));
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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 org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
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.validation.BooleanPropertyValidator.booleanProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.EnumPropertyValidator.enumerationProperty;
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerPropertyValidator.integerProperty;
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class HsManagedWebspaceAssetValidator extends HsHostingAssetValidator {
|
class HsManagedWebspaceAssetValidator extends HsEntityValidator {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HsHostingAssetValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator());
|
HsEntityValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HsManagedWebspaceAssetValidator() {
|
public HsManagedWebspaceAssetValidator() {
|
@ -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<T> {
|
|
||||||
|
|
||||||
final Class<T> type;
|
|
||||||
final String propertyName;
|
|
||||||
private Boolean required;
|
|
||||||
|
|
||||||
public static <K, V> Map.Entry<K, V> defType(K k, V v) {
|
|
||||||
return new SimpleImmutableEntry<>(k, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HsHostingAssetPropertyValidator<T> required() {
|
|
||||||
required = Boolean.TRUE;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HsHostingAssetPropertyValidator<T> optional() {
|
|
||||||
required = Boolean.FALSE;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<String> validate(final Map<String, Object> props) {
|
|
||||||
final var result = new ArrayList<String>();
|
|
||||||
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<String> result, final T propValue, final Map<String, Object> props);
|
|
||||||
|
|
||||||
public void verifyConsistency(final Map.Entry<HsHostingAssetType, HsHostingAssetValidator> typeDef) {
|
|
||||||
if (required == null ) {
|
|
||||||
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> toMap(final ObjectMapper mapper) {
|
|
||||||
final Map<String, Object> map = mapper.convertValue(this, Map.class);
|
|
||||||
map.put("type", simpleTypeName());
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String simpleTypeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
class IntegerPropertyValidator extends HsHostingAssetPropertyValidator<Integer>{
|
|
||||||
|
|
||||||
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<String> result, final Integer propValue, final Map<String, Object> 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<String> {
|
|
||||||
|
|
||||||
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<String> values(final String... values) {
|
|
||||||
this.values = values;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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("'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<Boolean> {
|
|
||||||
|
|
||||||
private Map.Entry<String, String> falseIf;
|
|
||||||
|
|
||||||
private BooleanPropertyValidator(final String propertyName) {
|
|
||||||
super(Boolean.class, propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BooleanPropertyValidator booleanProperty(final String propertyName) {
|
|
||||||
return new BooleanPropertyValidator(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
HsHostingAssetPropertyValidator<Boolean> falseIf(final String refPropertyName, final String refPropertyValue) {
|
|
||||||
this.falseIf = new SimpleImmutableEntry<>(refPropertyName, refPropertyValue);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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("'config." + propertyName + "' is expected to be false because " +
|
|
||||||
"config." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String simpleTypeName() {
|
|
||||||
return "boolean";
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<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(),
|
|
||||||
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<HsHostingAssetType> types() {
|
|
||||||
return validators.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
|
||||||
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("'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<Map<String, Object>> 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<String, Object> asKeyValueMap(final Map map) {
|
|
||||||
return (Map<String, Object>) map;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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<Boolean> {
|
||||||
|
|
||||||
|
private Map.Entry<String, String> falseIf;
|
||||||
|
|
||||||
|
private BooleanPropertyValidator(final String propertyName) {
|
||||||
|
super(Boolean.class, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BooleanPropertyValidator booleanProperty(final String propertyName) {
|
||||||
|
return new BooleanPropertyValidator(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<Boolean> falseIf(final String refPropertyName, final String refPropertyValue) {
|
||||||
|
this.falseIf = new AbstractMap.SimpleImmutableEntry<>(refPropertyName, refPropertyValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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("'config." + propertyName + "' is expected to be false because " +
|
||||||
|
"config." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "boolean";
|
||||||
|
}
|
||||||
|
}
|
@ -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<String> {
|
||||||
|
|
||||||
|
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<String> values(final String... values) {
|
||||||
|
this.values = values;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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("'config." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "enumeration";
|
||||||
|
}
|
||||||
|
}
|
@ -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<Enum<?>, 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<? extends Enum<?>> types() {
|
||||||
|
return validators.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||||
|
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("'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<Map<String, Object>> 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<String, Object> asKeyValueMap(final Map map) {
|
||||||
|
return (Map<String, Object>) map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<T> {
|
||||||
|
|
||||||
|
final Class<T> type;
|
||||||
|
final String propertyName;
|
||||||
|
private Boolean required;
|
||||||
|
|
||||||
|
public static <K, V> Map.Entry<K, V> defType(K k, V v) {
|
||||||
|
return new SimpleImmutableEntry<>(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<T> required() {
|
||||||
|
required = Boolean.TRUE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<T> optional() {
|
||||||
|
required = Boolean.FALSE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<String> validate(final Map<String, Object> props) {
|
||||||
|
final var result = new ArrayList<String>();
|
||||||
|
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<String> result, final T propValue, final Map<String, Object> props);
|
||||||
|
|
||||||
|
public void verifyConsistency(final Map.Entry<? extends Enum<?>, HsEntityValidator> typeDef) {
|
||||||
|
if (required == null ) {
|
||||||
|
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap(final ObjectMapper mapper) {
|
||||||
|
final Map<String, Object> map = mapper.convertValue(this, Map.class);
|
||||||
|
map.put("type", simpleTypeName());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String simpleTypeName();
|
||||||
|
}
|
@ -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<Integer> {
|
||||||
|
|
||||||
|
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<String> result, final Integer propValue, final Map<String, Object> 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";
|
||||||
|
}
|
||||||
|
}
|
@ -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 org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
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 net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class HsHostingAssetValidatorUnitTest {
|
class HsManagedServerValidatorUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validatesDependentProperties() {
|
void validatesDependentProperties() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER);
|
final var validator = HsEntityValidator.forType(MANAGED_SERVER);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_SERVER)
|
.type(MANAGED_SERVER)
|
||||||
.config(Map.ofEntries(
|
.config(Map.ofEntries(
|
@ -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.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 net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
class HsManagedWebspaceAssetValidatorUnitTest {
|
class HsManagedWebspaceAssetValidatorUnitTest {
|
||||||
|
|
||||||
@SuppressWarnings("unused") // just to make sure the class is loaded
|
@SuppressWarnings("unused") // just to make sure the class is loaded
|
||||||
HsHostingAssetValidator validator = new HsManagedWebspaceAssetValidator();
|
HsEntityValidator validator = new HsManagedWebspaceAssetValidator();
|
||||||
|
|
||||||
final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder()
|
final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder()
|
||||||
.debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build()
|
.debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build()
|
||||||
@ -35,7 +35,7 @@ class HsManagedWebspaceAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void validatesIdentifier() {
|
void validatesIdentifier() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
@ -58,7 +58,7 @@ class HsManagedWebspaceAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void validatesMissingProperties() {
|
void validatesMissingProperties() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
@ -79,7 +79,7 @@ class HsManagedWebspaceAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void validatesUnknownProperties() {
|
void validatesUnknownProperties() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
@ -102,7 +102,7 @@ class HsManagedWebspaceAssetValidatorUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
void validatesValidProperties() {
|
void validatesValidProperties() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
Loading…
Reference in New Issue
Block a user