implements EMailAlias-Property with ArrayProperty+multi-RegEx

This commit is contained in:
Michael Hoennig 2024-07-02 16:09:51 +02:00
parent bdb85e9be1
commit fcbbd13a7e
6 changed files with 35 additions and 21 deletions

View File

@ -12,20 +12,22 @@ import static net.hostsharing.hsadminng.mapper.Array.insertAfterEntries;
public class ArrayProperty<P extends ValidatableProperty<?, E>, E> extends ValidatableProperty<ArrayProperty<P, E>, E[]> { public class ArrayProperty<P extends ValidatableProperty<?, E>, E> extends ValidatableProperty<ArrayProperty<P, E>, E[]> {
private static final String[] KEY_ORDER = private static final String[] KEY_ORDER =
insertAfterEntries(ValidatableProperty.KEY_ORDER, "required", "minLength" ,"maxLength"); insertAfterEntries(
private final ValidatableProperty<?, E> elementProperty; insertAfterEntries(ValidatableProperty.KEY_ORDER, "required", "minLength" ,"maxLength"),
"propertyName", "elementProperty");
private final ValidatableProperty<?, E> elementsOf;
private Integer minLength; private Integer minLength;
private Integer maxLength; private Integer maxLength;
private ArrayProperty(final ValidatableProperty<?, E> elementProperty) { private ArrayProperty(final ValidatableProperty<?, E> elementsOf) {
//noinspection unchecked //noinspection unchecked
super((Class<E[]>) elementProperty.type.arrayType(), elementProperty.propertyName, KEY_ORDER); super((Class<E[]>) elementsOf.type.arrayType(), elementsOf.propertyName, KEY_ORDER);
this.elementProperty = elementProperty; this.elementsOf = elementsOf;
} }
public static <T> ArrayProperty<?, T[]> arrayOf(final ValidatableProperty<?, T> elementProperty) { public static <T> ArrayProperty<?, T[]> arrayOf(final ValidatableProperty<?, T> elementsOf) {
//noinspection unchecked //noinspection unchecked
return (ArrayProperty<?, T[]>) new ArrayProperty<>(elementProperty); return (ArrayProperty<?, T[]>) new ArrayProperty<>(elementsOf);
} }
public ValidatableProperty<?, ?> minLength(final int minLength) { public ValidatableProperty<?, ?> minLength(final int minLength) {
@ -46,14 +48,15 @@ public class ArrayProperty<P extends ValidatableProperty<?, E>, E> extends Valid
if (maxLength != null && propValue.length > maxLength) { if (maxLength != null && propValue.length > maxLength) {
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length); result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length);
} }
stream(propValue).forEach(e -> elementProperty.validate(result, e, propProvider)); stream(propValue).forEach(e -> elementsOf.validate(result, e, propProvider));
} }
@Override @Override
protected String simpleTypeName() { protected String simpleTypeName() {
return elementProperty.simpleTypeName() + "[]"; return elementsOf.simpleTypeName() + "[]";
} }
@SafeVarargs
private String display(final E... propValue) { private String display(final E... propValue) {
return "[" + Arrays.toString(propValue) + "]"; return "[" + Arrays.toString(propValue) + "]";
} }

View File

@ -69,7 +69,7 @@ public class StringProperty<P extends StringProperty<P>> extends ValidatableProp
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length()); result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length());
} }
if (matchesRegEx != null && if (matchesRegEx != null &&
stream(matchesRegEx).map(p -> p.matcher(propValue)).map(Matcher::matches).findAny().isEmpty()) { stream(matchesRegEx).map(p -> p.matcher(propValue)).noneMatch(Matcher::matches)) {
result.add(propertyName + "' is expected to match any of " + Arrays.toString(matchesRegEx) + " but " + display(propValue) + " does not match any"); result.add(propertyName + "' is expected to match any of " + Arrays.toString(matchesRegEx) + " but " + display(propValue) + " does not match any");
} }
if (isReadOnly() && propValue != null) { if (isReadOnly() && propValue != null) {

View File

@ -1,15 +1,16 @@
package net.hostsharing.hsadminng.hs.validation; package net.hostsharing.hsadminng.hs.validation;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.experimental.Accessors;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.mapper.Array;
import org.apache.commons.lang3.function.TriFunction; import org.apache.commons.lang3.function.TriFunction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -22,6 +23,7 @@ import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.ObjectUtils.isArray;
@Getter @Getter
@RequiredArgsConstructor @RequiredArgsConstructor
@ -239,8 +241,8 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
} }
private Object arrayToList(final Object value) { private Object arrayToList(final Object value) {
if ( value instanceof String[]) { if (isArray(value)) {
return List.of((String[])value); return Arrays.stream((Object[])value).map(Object::toString).toList();
} }
return value; return value;
} }
@ -265,4 +267,9 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
public <E extends PropertiesProvider> T compute(final E entity) { public <E extends PropertiesProvider> T compute(final E entity) {
return computedBy.apply(entity); return computedBy.apply(entity);
} }
@Override
public String toString() {
return toOrderedMap().toString();
}
} }

View File

@ -36,7 +36,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string[], propertyName=target, required=true, minLength=1}"); "{type=string[], propertyName=target, elementProperty={type=string, propertyName=target, matchesRegEx=[^[a-z]{3}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_.±]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$], maxLength=320}, required=true, minLength=1}");
} }
@Test @Test
@ -77,7 +77,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'EMAIL_ALIAS:xyz00-office.parentAsset' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER"); "'EMAIL_ALIAS:xyz00-office.config.target' is expected to match any of [^[a-z]{3}[0-9]{2}(-[a-z0-9]+)?$, ^[a-zA-Z0-9_.±]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$] but 'garbage' does not match any");
} }
@Test @Test
@ -88,7 +88,7 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
.identifier("abc00-office") .identifier("abc00-office")
.config(Map.ofEntries( .config(Map.ofEntries(
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com")) entry("target", Array.of("office@example.com"))
)) ))
.build(); .build();
final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType()); final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType());
@ -108,9 +108,10 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
.type(EMAIL_ALIAS) .type(EMAIL_ALIAS)
.bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM) .bookingItem(TEST_MANAGED_SERVER_BOOKING_ITEM)
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) .parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
.assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
.identifier("abc00-office") .identifier("abc00-office")
.config(Map.ofEntries( .config(Map.ofEntries(
entry("target", Array.of("xyz00", "xyz00-abc", "garbage", "office@example.com")) entry("target", Array.of("office@example.com"))
)) ))
.build(); .build();
final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType()); final var validator = HsHostingAssetEntityValidatorRegistry.forType(emailAliasHostingAssetEntity.getType());
@ -120,6 +121,8 @@ class HsEMailAliasHostingAssetValidatorUnitTest {
// then // then
assertThat(result).containsExactlyInAnyOrder( assertThat(result).containsExactlyInAnyOrder(
"'identifier' expected to match '^xyz00$|^xyz00-[a-z0-9]+$', but is 'abc00-office'"); "'EMAIL_ALIAS:abc00-office.bookingItem' must be null but is set to D-1234500:test project:test project booking item",
"'EMAIL_ALIAS:abc00-office.parentAsset' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER",
"'EMAIL_ALIAS:abc00-office.assignedToAsset' must be null but is set to D-1234500:test project:test project booking item");
} }
} }

View File

@ -32,7 +32,8 @@ class HsHostingAssetEntityValidatorRegistryUnitTest {
HsHostingAssetType.CLOUD_SERVER, HsHostingAssetType.CLOUD_SERVER,
HsHostingAssetType.MANAGED_SERVER, HsHostingAssetType.MANAGED_SERVER,
HsHostingAssetType.MANAGED_WEBSPACE, HsHostingAssetType.MANAGED_WEBSPACE,
HsHostingAssetType.UNIX_USER HsHostingAssetType.UNIX_USER,
HsHostingAssetType.EMAIL_ALIAS
); );
} }
} }

View File

@ -110,7 +110,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
"'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200", "'UNIX_USER:abc00-temp.config.HDD soft quota' is expected to be at most 100 but is 200",
"'UNIX_USER:abc00-temp.config.shell' is expected to be one of [/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd] but is '/is/invalid'", "'UNIX_USER:abc00-temp.config.shell' is expected to be one of [/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd] but is '/is/invalid'",
"'UNIX_USER:abc00-temp.config.homedir' is readonly but given as '/is/read-only'", "'UNIX_USER:abc00-temp.config.homedir' is readonly but given as '/is/read-only'",
"'UNIX_USER:abc00-temp.config.totpKey' is expected to be match ^0x([0-9A-Fa-f]{2})+$ but provided value does not match", "'UNIX_USER:abc00-temp.config.totpKey' is expected to match any of [^0x([0-9A-Fa-f]{2})+$] but provided value does not match any",
"'UNIX_USER:abc00-temp.config.password' length is expected to be at min 8 but length of provided value is 5", "'UNIX_USER:abc00-temp.config.password' length is expected to be at min 8 but length of provided value is 5",
"'UNIX_USER:abc00-temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters" "'UNIX_USER:abc00-temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"
); );
@ -168,7 +168,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
"{type=integer, propertyName=HDD soft quota, unit=GB, maxFrom=HDD hard quota}", "{type=integer, propertyName=HDD soft quota, unit=GB, maxFrom=HDD hard quota}",
"{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}", "{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}",
"{type=string, propertyName=homedir, readOnly=true, computed=true}", "{type=string, propertyName=homedir, readOnly=true, computed=true}",
"{type=string, propertyName=totpKey, matchesRegEx=^0x([0-9A-Fa-f]{2})+$, minLength=20, maxLength=256, writeOnly=true, undisclosed=true}", "{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}",
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}" "{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}"
); );
} }