add-domain-setup-validation #71
@ -73,6 +73,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
final var entity = mapper.map(body, HsHostingAssetEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
final var entity = mapper.map(body, HsHostingAssetEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||||
|
|
||||||
final var mapped = new HsHostingAssetEntityProcessor(entity)
|
final var mapped = new HsHostingAssetEntityProcessor(entity)
|
||||||
|
.preprocessEntity()
|
||||||
.validateEntity()
|
.validateEntity()
|
||||||
.prepareForSave()
|
.prepareForSave()
|
||||||
.saveUsing(assetRepo::save)
|
.saveUsing(assetRepo::save)
|
||||||
@ -133,6 +134,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
new HsHostingAssetEntityPatcher(em, entity).apply(body);
|
new HsHostingAssetEntityPatcher(em, entity).apply(body);
|
||||||
|
|
||||||
final var mapped = new HsHostingAssetEntityProcessor(entity)
|
final var mapped = new HsHostingAssetEntityProcessor(entity)
|
||||||
|
.preprocessEntity()
|
||||||
.validateEntity()
|
.validateEntity()
|
||||||
.prepareForSave()
|
.prepareForSave()
|
||||||
.saveUsing(assetRepo::save)
|
.saveUsing(assetRepo::save)
|
||||||
|
@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
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.ArrayProperty.arrayOf;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
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-MX-RR").withDefault(true),
|
||||||
booleanProperty("auto-WILDCARD-A-RR").withDefault(true),
|
booleanProperty("auto-WILDCARD-A-RR").withDefault(true),
|
||||||
booleanProperty("auto-WILDCARD-AAAA-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),
|
booleanProperty("auto-WILDCARD-SPF-RR").withDefault(true),
|
||||||
arrayOf(
|
arrayOf(
|
||||||
stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required()
|
stringProperty("user-RR").matchesRegEx(RR_REGEX_TTL_IN, RR_REGEX_IN_TTL).required()
|
||||||
@ -59,10 +60,17 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
// FIXME: should be auto-initialized
|
|
||||||
return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + "$");
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public List<String> validateContext(final HsHostingAssetEntity assetEntity) {
|
public List<String> validateContext(final HsHostingAssetEntity assetEntity) {
|
||||||
@ -80,6 +88,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
|||||||
}
|
}
|
||||||
|
|
||||||
String toZonefileString(final HsHostingAssetEntity assetEntity) {
|
String toZonefileString(final HsHostingAssetEntity assetEntity) {
|
||||||
|
// TODO.spec: we need to expand the templates (auto-...) in the same way as in Saltstack
|
||||||
return """
|
return """
|
||||||
$ORIGIN {domain}.
|
$ORIGIN {domain}.
|
||||||
$TTL {ttl}
|
$TTL {ttl}
|
||||||
|
@ -14,6 +14,7 @@ import java.util.function.Function;
|
|||||||
public class HsHostingAssetEntityProcessor {
|
public class HsHostingAssetEntityProcessor {
|
||||||
|
|
||||||
private final HsEntityValidator<HsHostingAssetEntity> validator;
|
private final HsEntityValidator<HsHostingAssetEntity> validator;
|
||||||
|
private String expectedStep = "preprocessEntity";
|
||||||
private HsHostingAssetEntity entity;
|
private HsHostingAssetEntity entity;
|
||||||
private HsHostingAssetResource resource;
|
private HsHostingAssetResource resource;
|
||||||
|
|
||||||
@ -22,8 +23,16 @@ public class HsHostingAssetEntityProcessor {
|
|||||||
this.validator = HsHostingAssetEntityValidatorRegistry.forType(entity.getType());
|
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
|
/// validates the entity itself including its properties
|
||||||
public HsHostingAssetEntityProcessor validateEntity() {
|
public HsHostingAssetEntityProcessor validateEntity() {
|
||||||
|
step("validateEntity", "prepareForSave");
|
||||||
MultiValidationException.throwIfNotEmpty(validator.validateEntity(entity));
|
MultiValidationException.throwIfNotEmpty(validator.validateEntity(entity));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -31,17 +40,20 @@ public class HsHostingAssetEntityProcessor {
|
|||||||
/// hashing passwords etc.
|
/// hashing passwords etc.
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public HsHostingAssetEntityProcessor prepareForSave() {
|
public HsHostingAssetEntityProcessor prepareForSave() {
|
||||||
|
step("prepareForSave", "saveUsing");
|
||||||
validator.prepareProperties(entity);
|
validator.prepareProperties(entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HsHostingAssetEntityProcessor saveUsing(final Function<HsHostingAssetEntity, HsHostingAssetEntity> saveFunction) {
|
public HsHostingAssetEntityProcessor saveUsing(final Function<HsHostingAssetEntity, HsHostingAssetEntity> saveFunction) {
|
||||||
|
step("saveUsing", "validateContext");
|
||||||
entity = saveFunction.apply(entity);
|
entity = saveFunction.apply(entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits)
|
/// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits)
|
||||||
public HsHostingAssetEntityProcessor validateContext() {
|
public HsHostingAssetEntityProcessor validateContext() {
|
||||||
|
step("validateContext", "mapUsing");
|
||||||
MultiValidationException.throwIfNotEmpty(validator.validateContext(entity));
|
MultiValidationException.throwIfNotEmpty(validator.validateContext(entity));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -49,6 +61,7 @@ public class HsHostingAssetEntityProcessor {
|
|||||||
/// maps entity to JSON resource representation
|
/// maps entity to JSON resource representation
|
||||||
public HsHostingAssetEntityProcessor mapUsing(
|
public HsHostingAssetEntityProcessor mapUsing(
|
||||||
final Function<HsHostingAssetEntity, HsHostingAssetResource> mapFunction) {
|
final Function<HsHostingAssetEntity, HsHostingAssetResource> mapFunction) {
|
||||||
|
step("mapUsing", "revampProperties");
|
||||||
resource = mapFunction.apply(entity);
|
resource = mapFunction.apply(entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -56,8 +69,18 @@ public class HsHostingAssetEntityProcessor {
|
|||||||
/// removes write-only-properties and ads computed-properties
|
/// removes write-only-properties and ads computed-properties
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public HsHostingAssetResource revampProperties() {
|
public HsHostingAssetResource revampProperties() {
|
||||||
|
step("revampProperties", null);
|
||||||
final var revampedProps = validator.revampProperties(entity, (Map<String, Object>) resource.getConfig());
|
final var revampedProps = validator.revampProperties(entity, (Map<String, Object>) resource.getConfig());
|
||||||
resource.setConfig(revampedProps);
|
resource.setConfig(revampedProps);
|
||||||
return resource;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,13 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
|||||||
.collect(Collectors.toMap(p -> p.get("propertyName").toString(), p -> p));
|
.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<String> validateProperties(final PropertiesProvider propsProvider) {
|
protected ArrayList<String> validateProperties(final PropertiesProvider propsProvider) {
|
||||||
final var result = new ArrayList<String>();
|
final var result = new ArrayList<String>();
|
||||||
|
|
||||||
|
@ -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
|
@Test
|
||||||
void rejectsInvalidIdentifier() {
|
void rejectsInvalidIdentifier() {
|
||||||
// given
|
// given
|
||||||
|
Loading…
Reference in New Issue
Block a user