diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java index 5eb831de..5b8bb362 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntity.java @@ -11,6 +11,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity; +import net.hostsharing.hsadminng.hs.validation.Validatable; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; @@ -65,7 +66,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Setter @NoArgsConstructor @AllArgsConstructor -public class HsBookingItemEntity implements Stringifyable, RbacObject { +public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatable { private static Stringify stringify = stringify(HsBookingItemEntity.class) .withProp(HsBookingItemEntity::getDebitor) @@ -142,6 +143,11 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject { ":" + caption; } + @Override + public Map getProperties() { + return resources; + } + public static RbacView rbac() { return rbacViewFor("bookingItem", HsBookingItemEntity.class) .withIdentityView(SQL.query(""" diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityValidators.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityValidators.java new file mode 100644 index 00000000..36b14223 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemEntityValidators.java @@ -0,0 +1,40 @@ +package net.hostsharing.hsadminng.hs.booking.item; + +import lombok.experimental.UtilityClass; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; + +import jakarta.validation.ValidationException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static java.util.Arrays.stream; + +@UtilityClass +public class HsBookingItemEntityValidators { + + private static final Map, HsEntityValidator> validators = new HashMap<>(); + + 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 Enum type) { + return validators.get(type); + } + + public static Set> types() { + return validators.keySet(); + } + + static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) { + final var violations = HsBookingItemEntityValidators.forType(entityToSave.getType()).validate(entityToSave); + if (!violations.isEmpty()) { + throw new ValidationException(violations.toString()); + } + return entityToSave; + } +} 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 index acef7bd9..3f9cb2a5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsCloudServerAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsCloudServerAssetValidator.java @@ -8,10 +8,10 @@ import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidat import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; @Component -class HsCloudServerAssetValidator extends HsEntityValidator { +class HsCloudServerAssetValidator extends HsEntityValidator { static { - HsEntityValidator.register(CLOUD_SERVER, new HsCloudServerAssetValidator()); + HsHostingAssetEntityValidators.register(CLOUD_SERVER, new HsCloudServerAssetValidator()); } public HsCloudServerAssetValidator() { 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 9b8b484e..04221a8a 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,5 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi; import net.hostsharing.hsadminng.context.Context; @@ -17,11 +16,11 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import jakarta.persistence.EntityNotFoundException; -import jakarta.validation.ValidationException; import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntityValidators.valid; @RestController public class HsHostingAssetController implements HsHostingAssetsApi { @@ -118,19 +117,11 @@ public class HsHostingAssetController implements HsHostingAssetsApi { new HsHostingAssetEntityPatcher(current).apply(body); - final var saved = assetRepo.save(current); + final var saved = assetRepo.save(valid(current)); final var mapped = mapper.map(saved, HsHostingAssetResource.class); return ResponseEntity.ok(mapped); } - private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { - final var violations = HsEntityValidator.forType(entityToSave.getType()).validate(entityToSave); - if (!violations.isEmpty()) { - throw new ValidationException(violations.toString()); - } - return entityToSave; - } - final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { entity.putConfig(KeyValueMap.from(resource.getConfig())); if (resource.getParentAssetUuid() != null) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java index af91e0db..5571dd13 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntity.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.validation.Validatable; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; @@ -60,7 +61,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Setter @NoArgsConstructor @AllArgsConstructor -public class HsHostingAssetEntity implements Stringifyable, RbacObject { +public class HsHostingAssetEntity implements Stringifyable, RbacObject, Validatable { private static Stringify stringify = stringify(HsHostingAssetEntity.class) .withProp(HsHostingAssetEntity::getType) @@ -113,6 +114,11 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject { PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg); } + @Override + public Map getProperties() { + return config; + } + @Override public String toString() { return stringify.apply(this); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityValidators.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityValidators.java new file mode 100644 index 00000000..42567993 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetEntityValidators.java @@ -0,0 +1,41 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import lombok.experimental.UtilityClass; +import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; + +import jakarta.validation.ValidationException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static java.util.Arrays.stream; + +@UtilityClass +public class HsHostingAssetEntityValidators { + + private static final Map, HsEntityValidator> validators = new HashMap<>(); + + 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 Enum type) { + return validators.get(type); + } + + public static Set> types() { + return validators.keySet(); + } + + + static HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { + final var violations = HsHostingAssetEntityValidators.forType(entityToSave.getType()).validate(entityToSave); + if (!violations.isEmpty()) { + throw new ValidationException(violations.toString()); + } + return entityToSave; + } +} 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 50e236eb..1ef68798 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,5 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -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 +14,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi { @Override public ResponseEntity> listAssetTypes() { - final var resource = HsEntityValidator.types().stream() + final var resource = HsHostingAssetEntityValidators.types().stream() .map(Enum::name) .toList(); return ResponseEntity.ok(resource); @@ -25,7 +24,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi { public ResponseEntity> listAssetTypeProps( final HsHostingAssetTypeResource assetType) { - final var propValidators = HsEntityValidator.forType(HsHostingAssetType.of(assetType)); + final var propValidators = HsHostingAssetEntityValidators.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 index 95bbfb96..c7b35550 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedServerAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedServerAssetValidator.java @@ -9,10 +9,10 @@ import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidat import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; @Component -class HsManagedServerAssetValidator extends HsEntityValidator { +class HsManagedServerAssetValidator extends HsEntityValidator { static { - HsEntityValidator.register(MANAGED_SERVER, new HsManagedServerAssetValidator()); + HsHostingAssetEntityValidators.register(MANAGED_SERVER, new HsManagedServerAssetValidator()); } public HsManagedServerAssetValidator() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java index 4deade19..5b898976 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidator.java @@ -11,10 +11,10 @@ import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidat import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty; @Component -class HsManagedWebspaceAssetValidator extends HsEntityValidator { +class HsManagedWebspaceAssetValidator extends HsEntityValidator { static { - HsEntityValidator.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator()); + HsHostingAssetEntityValidators.register(MANAGED_WEBSPACE, new HsManagedWebspaceAssetValidator()); } public HsManagedWebspaceAssetValidator() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java index 3288b8fb..15f62916 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -3,54 +3,31 @@ 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 { +public class HsEntityValidator, T extends Enum> { - 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 final HsPropertyValidator[] propertyValidators; public HsEntityValidator(final HsPropertyValidator... validators) { propertyValidators = validators; } - public static Set> types() { - return validators.keySet(); - } - - public List validate(final HsHostingAssetEntity assetEntity) { + public List validate(final E assetEntity) { final var result = new ArrayList(); - assetEntity.getConfig().keySet().forEach( givenPropName -> { + assetEntity.getProperties().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) + "'"); + result.add("'config." + givenPropName + "' is not expected but is '" +assetEntity.getProperties().get(givenPropName) + "'"); } }); stream(propertyValidators).forEach(pv -> { - result.addAll(pv.validate(assetEntity.getConfig())); + result.addAll(pv.validate(assetEntity.getProperties())); }); return result; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java index 4408a1f3..4a7bb05e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsPropertyValidator.java @@ -2,7 +2,6 @@ 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; @@ -52,7 +51,7 @@ public abstract class HsPropertyValidator { protected abstract void validate(final ArrayList result, final T propValue, final Map props); - public void verifyConsistency(final Map.Entry, HsEntityValidator> typeDef) { + 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()" ); } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java new file mode 100644 index 00000000..09cf4388 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/Validatable.java @@ -0,0 +1,11 @@ +package net.hostsharing.hsadminng.hs.validation; + + +import java.util.Map; + +public interface Validatable> { + + Map getProperties(); + + Enum getType(); +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java index 86836cec..66e3f16d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsEntityValidatorUnitTest.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hosting.asset; -import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import org.junit.jupiter.api.Test; import java.util.Map; @@ -14,7 +13,7 @@ class HsManagedServerValidatorUnitTest { @Test void validatesDependentProperties() { // given - final var validator = HsEntityValidator.forType(MANAGED_SERVER); + final var validator = HsHostingAssetEntityValidators.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/HsManagedWebspaceAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java index 2bc82650..73873b24 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsManagedWebspaceAssetValidatorUnitTest.java @@ -1,7 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; -import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import org.junit.jupiter.api.Test; @@ -15,8 +14,10 @@ import static org.assertj.core.api.Assertions.assertThat; class HsManagedWebspaceAssetValidatorUnitTest { - @SuppressWarnings("unused") // just to make sure the class is loaded - HsEntityValidator validator = new HsManagedWebspaceAssetValidator(); + // just to make sure the class is loaded + static { + new HsManagedWebspaceAssetValidator(); + } final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder() .debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build() @@ -35,7 +36,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesIdentifier() { // given - final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -58,7 +59,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesMissingProperties() { // given - final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -79,7 +80,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesUnknownProperties() { // given - final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity) @@ -102,7 +103,7 @@ class HsManagedWebspaceAssetValidatorUnitTest { @Test void validatesValidProperties() { // given - final var validator = HsEntityValidator.forType(MANAGED_WEBSPACE); + final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE); final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() .type(MANAGED_WEBSPACE) .parentAsset(mangedServerAssetEntity)