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 d9b6492f..6e082c05 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 @@ -73,6 +73,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { final var entity = mapper.map(body, HsHostingAssetEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var mapped = new HsHostingAssetEntityProcessor(entity) + .preprocessEntity() .validateEntity() .prepareForSave() .saveUsing(assetRepo::save) @@ -133,6 +134,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi { new HsHostingAssetEntityPatcher(em, entity).apply(body); final var mapped = new HsHostingAssetEntityProcessor(entity) + .preprocessEntity() .validateEntity() .prepareForSave() .saveUsing(assetRepo::save) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java index bd6a1cb2..e09f77ef 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidator.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.regex.Pattern; import static java.util.Arrays.stream; +import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf; import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; @@ -50,7 +51,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato booleanProperty("auto-WILDCARD-MX-RR").withDefault(true), booleanProperty("auto-WILDCARD-A-RR").withDefault(true), booleanProperty("auto-WILDCARD-AAAA-RR").withDefault(true), - booleanProperty("auto-WILDCARD-DKIM-RR").withDefault(true), // TODO.spec: @Peter + booleanProperty("auto-WILDCARD-DKIM-RR").withDefault(true), // TODO.spec: check, if that really works booleanProperty("auto-WILDCARD-SPF-RR").withDefault(true), arrayOf( stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required() @@ -59,10 +60,17 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato @Override protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { - // FIXME: should be auto-initialized return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + "$"); } + @Override + public void preprocessEntity(final HsHostingAssetEntity entity) { + super.preprocessEntity(entity); + if (entity.getIdentifier() == null) { + ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier())); + } + } + @Override @SneakyThrows public List validateContext(final HsHostingAssetEntity assetEntity) { @@ -80,6 +88,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato } String toZonefileString(final HsHostingAssetEntity assetEntity) { + // TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack return """ $ORIGIN {domain}. $TTL {ttl} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityProcessor.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityProcessor.java index 5e270c86..cc192bc7 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityProcessor.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsHostingAssetEntityProcessor.java @@ -14,6 +14,7 @@ import java.util.function.Function; public class HsHostingAssetEntityProcessor { private final HsEntityValidator validator; + private String expectedStep = "preprocessEntity"; private HsHostingAssetEntity entity; private HsHostingAssetResource resource; @@ -22,8 +23,16 @@ public class HsHostingAssetEntityProcessor { this.validator = HsHostingAssetEntityValidatorRegistry.forType(entity.getType()); } + /// initial step allowing to set default values before any validations + public HsHostingAssetEntityProcessor preprocessEntity() { + step("preprocessEntity", "validateEntity"); + validator.preprocessEntity(entity); + return this; + } + /// validates the entity itself including its properties public HsHostingAssetEntityProcessor validateEntity() { + step("validateEntity", "prepareForSave"); MultiValidationException.throwIfNotEmpty(validator.validateEntity(entity)); return this; } @@ -31,17 +40,20 @@ public class HsHostingAssetEntityProcessor { /// hashing passwords etc. @SuppressWarnings("unchecked") public HsHostingAssetEntityProcessor prepareForSave() { + step("prepareForSave", "saveUsing"); validator.prepareProperties(entity); return this; } public HsHostingAssetEntityProcessor saveUsing(final Function saveFunction) { + step("saveUsing", "validateContext"); entity = saveFunction.apply(entity); return this; } /// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits) public HsHostingAssetEntityProcessor validateContext() { + step("validateContext", "mapUsing"); MultiValidationException.throwIfNotEmpty(validator.validateContext(entity)); return this; } @@ -49,6 +61,7 @@ public class HsHostingAssetEntityProcessor { /// maps entity to JSON resource representation public HsHostingAssetEntityProcessor mapUsing( final Function mapFunction) { + step("mapUsing", "revampProperties"); resource = mapFunction.apply(entity); return this; } @@ -56,8 +69,18 @@ public class HsHostingAssetEntityProcessor { /// removes write-only-properties and ads computed-properties @SuppressWarnings("unchecked") public HsHostingAssetResource revampProperties() { + step("revampProperties", null); final var revampedProps = validator.revampProperties(entity, (Map) resource.getConfig()); resource.setConfig(revampedProps); return resource; } + + // Makes sure that the steps are called in the correct order. + // Could also be implemented using an interface per method, but that seems exaggerated. + private void step(final String current, final String next) { + if (!expectedStep.equals(current)) { + throw new IllegalStateException("expected " + expectedStep + " but got " + current); + } + expectedStep = next; + } } 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 9905d2fa..de4b70bc 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/HsEntityValidator.java @@ -49,6 +49,13 @@ public abstract class HsEntityValidator { .collect(Collectors.toMap(p -> p.get("propertyName").toString(), p -> p)); } + /** + Gets called before any validations take place. + Allows to initialize fields and properties to default values. + */ + public void preprocessEntity(final E entity) { + } + protected ArrayList validateProperties(final PropertiesProvider propsProvider) { final var result = new ArrayList(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java index d5b684a7..c2ec5c52 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainDnsSetupHostingAssetValidatorUnitTest.java @@ -43,6 +43,19 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest { )); } + @Test + void preprocessesTakesIdentifierFromParent() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HsHostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + validator.preprocessEntity(givenEntity); + + // then + assertThat(givenEntity.getIdentifier()).isEqualTo(givenEntity.getParentAsset().getIdentifier()); + } + @Test void rejectsInvalidIdentifier() { // given