From ccd7fead30731825b78db374b14ed2e06816b623 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 15 Jul 2024 15:58:02 +0200 Subject: [PATCH] add IPV4_NUMBER/HsIPv4NumberHostingAssetValidator and properly support multiple optionallyAssignedTo --- .../hs/hosting/asset/HsHostingAssetType.java | 142 +++++++++++++----- .../HostingAssetEntityValidator.java | 30 ++-- .../HostingAssetEntityValidatorRegistry.java | 1 + .../HsIPv4NumberHostingAssetValidator.java | 26 ++++ .../hostsharing/hsadminng/mapper/Array.java | 6 + .../hs-hosting/hs-hosting-asset-schemas.yaml | 1 + .../7010-hs-hosting-asset.sql | 6 +- .../HsHostingAssetControllerRestTest.java | 19 +++ ...ingAssetPropsControllerAcceptanceTest.java | 3 +- .../asset/HsHostingAssetTypeUnitTest.java | 36 ++--- ...gAssetEntityValidatorRegistryUnitTest.java | 3 +- ...v4NumberHostingAssetValidatorUnitTest.java | 120 +++++++++++++++ ...DatabaseHostingAssetValidatorUnitTest.java | 13 +- 13 files changed, 328 insertions(+), 78 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java index 6d9fa75e..e9e42918 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetType.java @@ -5,11 +5,13 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.Node; +import jakarta.validation.constraints.NotNull; import javax.naming.NamingException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; @@ -18,12 +20,17 @@ import java.util.function.Function; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toSet; -import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.assignedTo; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.optionalParent; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.optionallyAssignedTo; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.requiredParent; +import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.requires; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.ASSIGNED_TO_ASSET; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.BOOKING_ITEM; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.PARENT_ASSET; +import static net.hostsharing.hsadminng.mapper.Array.emptyArray; public enum HsHostingAssetType implements Node { SAME_TYPE, // pseudo-type for recursive references @@ -106,27 +113,25 @@ public enum HsHostingAssetType implements Node { inGroup("MariaDB"), requiredParent(MARIADB_USER)), // thus, the MARIADB_USER:Agent becomes RBAC owner - IP_NUMBER( + IPV4_NUMBER( inGroup("Server"), - assignedTo(CLOUD_SERVER), - assignedTo(MANAGED_SERVER), - assignedTo(MANAGED_WEBSPACE) + optionallyAssignedTo(CLOUD_SERVER).or(MANAGED_SERVER).or(MANAGED_WEBSPACE) ); private final String groupName; private final EntityTypeRelation[] relations; HsHostingAssetType( - final String groupName, - final EntityTypeRelation... relations - ) { + String groupName, + @NotNull final EntityTypeRelation... relations + ) { this.groupName = groupName; this.relations = relations; } HsHostingAssetType() { this.groupName = null; - this.relations = null; + this.relations = emptyArray(EntityTypeRelation.class); } /// just syntactic sugar @@ -144,44 +149,50 @@ public enum HsHostingAssetType implements Node { .orElse(RelationPolicy.FORBIDDEN); } - public HsBookingItemType bookingItemType() { + public Set bookingItemTypes() { return stream(relations) .filter(r -> r.relationType == BOOKING_ITEM) - .map(r -> HsBookingItemType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsBookingItemType) r) + .collect(toSet()); } public RelationPolicy parentAssetPolicy() { return stream(relations) .filter(r -> r.relationType == PARENT_ASSET) - .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .map(r -> r.relationPolicy) .orElse(RelationPolicy.FORBIDDEN); } - public HsHostingAssetType parentAssetType() { + public Set parentAssetTypes() { return stream(relations) .filter(r -> r.relationType == PARENT_ASSET) - .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsHostingAssetType) r) + .collect(toSet()); } public RelationPolicy assignedToAssetPolicy() { return stream(relations) .filter(r -> r.relationType == ASSIGNED_TO_ASSET) - .map(r -> r.relationPolicy) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) + .map(r -> r.relationPolicy) .orElse(RelationPolicy.FORBIDDEN); } - public HsHostingAssetType assignedToAssetType() { + public Set assignedToAssetTypes() { return stream(relations) .filter(r -> r.relationType == ASSIGNED_TO_ASSET) - .map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString())) .reduce(HsHostingAssetType::onlyASingleElementExpectedException) - .orElse(null); + .map(r -> r.relatedTypes(this)) + .stream().flatMap(Set::stream) + .map(r -> (HsHostingAssetType) r) + .collect(toSet()); } private static X onlyASingleElementExpectedException(Object a, Object b) { @@ -191,7 +202,9 @@ public enum HsHostingAssetType implements Node { @Override public List edges() { return stream(relations) - .map(r -> nodeName() + r.edge + r.relatedType(this).nodeName()) + .map(r -> r.relatedTypes(this).stream().map(x -> nodeName() + r.edge + x.nodeName()).toList()) + .flatMap(List::stream) + .sorted() .toList(); } @@ -224,7 +237,7 @@ public enum HsHostingAssetType implements Node { .flatMap(Collection::stream) .collect(joining("\n")); final String hostingAssetEdges = stream(HsHostingAssetType.values()) - .filter(t -> t.isInGroups(includedHostingGroups)) + .filter(t -> t.isInGroups(includedHostingGroups)) .map(HsHostingAssetType::edges) .flatMap(Collection::stream) .collect(joining("\n")); @@ -239,7 +252,7 @@ public enum HsHostingAssetType implements Node { package Booking #feb28c { %{bookingNodes} } - + package Hosting #feb28c{ %{hostingGroups} } @@ -257,12 +270,12 @@ public enum HsHostingAssetType implements Node { Booking -down[hidden]->Legend ``` """ - .replace("%{caption}", caption) - .replace("%{bookingNodes}", bookingNodes) - .replace("%{hostingGroups}", hostingGroups) - .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) - .replace("%{bookingItemEdges}", bookingItemEdges) - .replace("%{hostingAssetEdges}", hostingAssetEdges); + .replace("%{caption}", caption) + .replace("%{bookingNodes}", bookingNodes) + .replace("%{hostingGroups}", hostingGroups) + .replace("%{hostingAssetNodeStyles}", hostingAssetNodes) + .replace("%{bookingItemEdges}", bookingItemEdges) + .replace("%{hostingAssetEdges}", hostingAssetEdges); } private boolean isInGroups(final Set assetGroups) { @@ -299,7 +312,7 @@ public enum HsHostingAssetType implements Node { This code generated was by %{this}.main, do not amend manually. """ - .replace("%{this}", HsHostingAssetType.class.getSimpleName())); + .replace("%{this}", HsHostingAssetType.class.getSimpleName())); return markdown.toString(); } @@ -317,8 +330,8 @@ public enum HsHostingAssetType implements Node { public enum RelationType { BOOKING_ITEM, - PARENT_ASSET, - ASSIGNED_TO_ASSET + PARENT_ASSET, + ASSIGNED_TO_ASSET } } @@ -328,27 +341,78 @@ class EntityTypeRelation { final HsHostingAssetType.RelationPolicy relationPolicy; final HsHostingAssetType.RelationType relationType; final Function getter; - private final T relatedType; + private final List acceptedRelatedTypes; final String edge; - public T relatedType(final HsHostingAssetType referringType) { + private EntityTypeRelation( + final HsHostingAssetType.RelationPolicy relationPolicy, + final HsHostingAssetType.RelationType relationType, + final Function getter, + final T acceptedRelatedType, + final String edge + ) { + this(relationPolicy, relationType, getter, modifiyableListOf(acceptedRelatedType), edge); + } + + public Set relatedTypes(final HsHostingAssetType referringType) { + final Set result = acceptedRelatedTypes.stream() + .map(t -> t == HsHostingAssetType.SAME_TYPE ? referringType : t) + .collect(toSet()); //noinspection unchecked - return relatedType == HsHostingAssetType.SAME_TYPE ? (T) referringType : relatedType; + return (Set) result; } static EntityTypeRelation requires(final HsBookingItemType bookingItemType) { - return new EntityTypeRelation<>(REQUIRED, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> "); + return new EntityTypeRelation<>( + REQUIRED, + BOOKING_ITEM, + HsHostingAssetEntity::getBookingItem, + bookingItemType, + " *==> "); } static EntityTypeRelation optionalParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(OPTIONAL, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> "); + return new EntityTypeRelation<>( + OPTIONAL, + PARENT_ASSET, + HsHostingAssetEntity::getParentAsset, + hostingAssetType, + " o..> "); } static EntityTypeRelation requiredParent(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> "); + return new EntityTypeRelation<>( + REQUIRED, + PARENT_ASSET, + HsHostingAssetEntity::getParentAsset, + hostingAssetType, + " *==> "); } static EntityTypeRelation assignedTo(final HsHostingAssetType hostingAssetType) { - return new EntityTypeRelation<>(REQUIRED, ASSIGNED_TO_ASSET, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> "); + return new EntityTypeRelation<>( + REQUIRED, + ASSIGNED_TO_ASSET, + HsHostingAssetEntity::getAssignedToAsset, + hostingAssetType, + " o--> "); + } + + EntityTypeRelation or(final T alternativeHostingAssetType) { + acceptedRelatedTypes.add(alternativeHostingAssetType); + return this; + } + + static EntityTypeRelation optionallyAssignedTo(final HsHostingAssetType hostingAssetType) { + return new EntityTypeRelation<>( + OPTIONAL, + ASSIGNED_TO_ASSET, + HsHostingAssetEntity::getAssignedToAsset, + hostingAssetType, + " o..> "); + } + + private static ArrayList modifiyableListOf(final T acceptedRelatedType) { + return new ArrayList<>(List.of(acceptedRelatedType)); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java index 7257f70c..6433814c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidator.java @@ -12,9 +12,11 @@ import net.hostsharing.hsadminng.hs.validation.ValidatableProperty; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Arrays.stream; @@ -37,17 +39,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator( assetType.bookingItemPolicy(), - assetType.bookingItemType(), + assetType.bookingItemTypes(), HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType); this.parentAssetReferenceValidation = new ReferenceValidator<>( assetType.parentAssetPolicy(), - assetType.parentAssetType(), + assetType.parentAssetTypes(), HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType); this.assignedToAssetReferenceValidation = new ReferenceValidator<>( assetType.assignedToAssetPolicy(), - assetType.assignedToAssetType(), + assetType.assignedToAssetTypes(), HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType); this.alarmContactValidation = alarmContactValidation; @@ -154,17 +156,17 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator { private final HsHostingAssetType.RelationPolicy policy; - private final T referencedEntityType; + private final Set referencedEntityTypes; private final Function referencedEntityGetter; private final Function referencedEntityTypeGetter; public ReferenceValidator( final HsHostingAssetType.RelationPolicy policy, - final T subEntityType, + final Set referencedEntityTypes, final Function referencedEntityGetter, final Function referencedEntityTypeGetter) { this.policy = policy; - this.referencedEntityType = subEntityType; + this.referencedEntityTypes = referencedEntityTypes; this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityTypeGetter = referencedEntityTypeGetter; } @@ -173,7 +175,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator referencedEntityGetter) { this.policy = policy; - this.referencedEntityType = null; + this.referencedEntityTypes = Set.of(); this.referencedEntityGetter = referencedEntityGetter; this.referencedEntityTypeGetter = e -> null; } @@ -185,15 +187,15 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator referencedEntityTypes) { + return referencedEntityTypes.stream().sorted().map(Object::toString).collect(Collectors.joining(" or ")); + } } static class AlarmContact extends ReferenceValidator> { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java index 4456a751..93247466 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistry.java @@ -32,6 +32,7 @@ public class HostingAssetEntityValidatorRegistry { register(PGSQL_INSTANCE, new HsPostgreSqlDbInstanceHostingAssetValidator()); register(PGSQL_USER, new HsPostgreSqlUserHostingAssetValidator()); register(PGSQL_DATABASE, new HsPostgreSqlDatabaseHostingAssetValidator()); + register(IPV4_NUMBER, new HsIPv4NumberHostingAssetValidator()); } private static void register(final Enum type, final HsEntityValidator validator) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java new file mode 100644 index 00000000..b4889fe0 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidator.java @@ -0,0 +1,26 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; + +import java.util.regex.Pattern; + +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; + +class HsIPv4NumberHostingAssetValidator extends HostingAssetEntityValidator { + + public static Pattern IPV4_REGEX = Pattern.compile("^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$"); + + HsIPv4NumberHostingAssetValidator() { + super( + IPV4_NUMBER, + AlarmContact.isOptional(), + + NO_EXTRA_PROPERTIES + ); + } + + @Override + protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { + return IPV4_REGEX; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/Array.java b/src/main/java/net/hostsharing/hsadminng/mapper/Array.java index 80970aa4..7f3e32d0 100644 --- a/src/main/java/net/hostsharing/hsadminng/mapper/Array.java +++ b/src/main/java/net/hostsharing/hsadminng/mapper/Array.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.mapper; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -51,6 +52,11 @@ public class Array { return of(); } + public static T[] emptyArray(final Class elementClass) { + //noinspection unchecked + return (T[]) java.lang.reflect.Array.newInstance(elementClass, 0); + } + @SafeVarargs public static T[] insertNewEntriesAfterExistingEntry(final T[] array, final T entryToFind, final T... newEntries) { final var arrayList = new ArrayList<>(asList(array)); diff --git a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml index b531fe8a..da0fec1e 100644 --- a/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml +++ b/src/main/resources/api-definition/hs-hosting/hs-hosting-asset-schemas.yaml @@ -23,6 +23,7 @@ components: - MARIADB_INSTANCE - MARIADB_USER - MARIADB_DATABASE + - IPV4_NUMBER HsHostingAsset: type: object diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 4497b675..c218a701 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -21,7 +21,8 @@ create type HsHostingAssetType as enum ( 'PGSQL_DATABASE', 'MARIADB_INSTANCE', 'MARIADB_USER', - 'MARIADB_DATABASE' + 'MARIADB_DATABASE', + 'IPV4_NUMBER' ); CREATE CAST (character varying as HsHostingAssetType) WITH INOUT AS IMPLICIT; @@ -85,6 +86,9 @@ begin when 'MARIADB_INSTANCE' then 'MANAGED_SERVER' when 'MARIADB_USER' then 'MANAGED_WEBSPACE' when 'MARIADB_DATABASE' then 'MARIADB_USER' + + when 'IPV4_NUMBER' then null + else raiseException(format('[400] unknown asset type %s', NEW.type::text)) end); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 15c27420..a1842e0f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -488,6 +488,25 @@ public class HsHostingAssetControllerRestTest { } } ] + """), + IPV4_NUMBER( + List.of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.IPV4_NUMBER) + .assignedToAsset(TEST_MANAGED_SERVER_HOSTING_ASSET) + .identifier("11.12.13.14") + .caption("some fake IPv4 number") + .build()), + """ + [ + { + "type": "IPV4_NUMBER", + "identifier": "11.12.13.14", + "caption": "some fake IPv4 number", + "alarmContact": null, + "config": {} + } + ] """); final HsHostingAssetType assetType; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java index dfce271b..41518b2e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java @@ -47,7 +47,8 @@ class HsHostingAssetPropsControllerAcceptanceTest { "MARIADB_DATABASE", "PGSQL_INSTANCE", "PGSQL_USER", - "PGSQL_DATABASE" + "PGSQL_DATABASE", + "IPV4_NUMBER" ] """)); // @formatter:on diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java index 09d9537a..3d29dc3c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetTypeUnitTest.java @@ -40,7 +40,7 @@ class HsHostingAssetTypeUnitTest { package Server #99bcdb { entity HA_CLOUD_SERVER entity HA_MANAGED_SERVER - entity HA_IP_NUMBER + entity HA_IPV4_NUMBER } package Webspace #99bcdb { @@ -63,17 +63,17 @@ class HsHostingAssetTypeUnitTest { HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_DNS_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_DNS_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER + HA_DOMAIN_HTTP_SETUP o--> HA_UNIX_USER HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_SMTP_SETUP o--> HA_MANAGED_WEBSPACE HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP - HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE + HA_DOMAIN_MBOX_SETUP o--> HA_MANAGED_WEBSPACE HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_SETUP - HA_IP_NUMBER o..> HA_CLOUD_SERVER - HA_IP_NUMBER o..> HA_MANAGED_SERVER - HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE + HA_IPV4_NUMBER o..> HA_CLOUD_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY @@ -107,7 +107,7 @@ class HsHostingAssetTypeUnitTest { package Server #99bcdb { entity HA_CLOUD_SERVER entity HA_MANAGED_SERVER - entity HA_IP_NUMBER + entity HA_IPV4_NUMBER } package Webspace #99bcdb { @@ -130,11 +130,11 @@ class HsHostingAssetTypeUnitTest { HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE - HA_MARIADB_USER o..> HA_MARIADB_INSTANCE + HA_MARIADB_USER o--> HA_MARIADB_INSTANCE HA_MARIADB_DATABASE *==> HA_MARIADB_USER - HA_IP_NUMBER o..> HA_CLOUD_SERVER - HA_IP_NUMBER o..> HA_MANAGED_SERVER - HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE + HA_IPV4_NUMBER o..> HA_CLOUD_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY @@ -168,7 +168,7 @@ class HsHostingAssetTypeUnitTest { package Server #99bcdb { entity HA_CLOUD_SERVER entity HA_MANAGED_SERVER - entity HA_IP_NUMBER + entity HA_IPV4_NUMBER } package Webspace #99bcdb { @@ -191,11 +191,11 @@ class HsHostingAssetTypeUnitTest { HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER HA_PGSQL_USER *==> HA_MANAGED_WEBSPACE - HA_PGSQL_USER o..> HA_PGSQL_INSTANCE + HA_PGSQL_USER o--> HA_PGSQL_INSTANCE HA_PGSQL_DATABASE *==> HA_PGSQL_USER - HA_IP_NUMBER o..> HA_CLOUD_SERVER - HA_IP_NUMBER o..> HA_MANAGED_SERVER - HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE + HA_IPV4_NUMBER o..> HA_CLOUD_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_SERVER + HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE package Legend #white { SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java index 4b752663..3840b426 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HostingAssetEntityValidatorRegistryUnitTest.java @@ -45,7 +45,8 @@ class HostingAssetEntityValidatorRegistryUnitTest { HsHostingAssetType.MARIADB_DATABASE, HsHostingAssetType.PGSQL_INSTANCE, HsHostingAssetType.PGSQL_USER, - HsHostingAssetType.PGSQL_DATABASE + HsHostingAssetType.PGSQL_DATABASE, + HsHostingAssetType.IPV4_NUMBER ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..0d219ad2 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsIPv4NumberHostingAssetValidatorUnitTest.java @@ -0,0 +1,120 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validators; + +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; +import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Map; + +import static java.util.Map.entry; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; +import static org.assertj.core.api.Assertions.assertThat; + +class HsIPv4NumberHostingAssetValidatorUnitTest { + + static HsHostingAssetEntityBuilder validEntityBuilder() { + return HsHostingAssetEntity.builder() + .type(IPV4_NUMBER) + .identifier("83.223.95.145"); + } + + @Test + void containsExpectedProperties() { + // when + final var validator = HostingAssetEntityValidatorRegistry.forType(IPV4_NUMBER); + + // then + assertThat(validator.properties()).map(Map::toString).isEmpty(); + } + + @Test + void acceptsValidEntity() { + // given + final var givenEntity = validEntityBuilder().build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).isEmpty(); + } + + @ParameterizedTest + @ValueSource(strings = {"a.b.c.d", "83.223.95", "83.223.95.145.1", "2a01:37:1000::53df:5f91:0"}) + void rejectsInvalidIdentifier(final String givenIdentifier) { + // given + final var givenEntity = validEntityBuilder().identifier(givenIdentifier).build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); + + // when + final var result = validator.validateEntity(givenEntity); + + // then + assertThat(result).containsExactly( + "'identifier' expected to match '^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$', but is '" + givenIdentifier + "'" + ); + } + + @ParameterizedTest + @EnumSource(value = HsHostingAssetType.class, names = { "CLOUD_SERVER", "MANAGED_SERVER", "MANAGED_WEBSPACE" }) + void acceptsValidReferencedEntity(final HsHostingAssetType givenAssignedToAssetType) { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .assignedToAsset(HsHostingAssetEntity.builder().type(givenAssignedToAssetType).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).isEmpty(); + } + + @Test + void rejectsInvalidReferencedEntities() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) + .parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build()) + .assignedToAsset(HsHostingAssetEntity.builder().type(UNIX_USER).build()) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV4_NUMBER:83.223.95.145.bookingItem' must be null but is of type CLOUD_SERVER", + "'IPV4_NUMBER:83.223.95.145.parentAsset' must be null but is of type MANAGED_WEBSPACE", + "'IPV4_NUMBER:83.223.95.145.assignedToAsset' must be null or of type CLOUD_SERVER or MANAGED_SERVER or MANAGED_WEBSPACE but is of type UNIX_USER"); + } + + @Test + void rejectsInvalidProperties() { + // given + final var ipNumberHostingAssetEntity = validEntityBuilder() + .config(Map.ofEntries( + entry("any", "false") + )) + .build(); + final var validator = HostingAssetEntityValidatorRegistry.forType(ipNumberHostingAssetEntity.getType()); + + // when + final var result = validator.validateEntity(ipNumberHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'IPV4_NUMBER:83.223.95.145.config.any' is not expected but is set to 'false'"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java index 092c253b..35780466 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsPostgreSqlDatabaseHostingAssetValidatorUnitTest.java @@ -31,7 +31,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { .type(PGSQL_USER) .parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET) .assignedToAsset(GIVEN_PGSQL_INSTANCE) - .identifier("xyz00_temp") + .identifier("xyz00_user") .caption("some valid test PgSql-User") .config(new HashMap<>(ofEntries( entry("password", "Hallo Datenbank, lass mich rein!") @@ -42,7 +42,7 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { return HsHostingAssetEntity.builder() .type(PGSQL_DATABASE) .parentAsset(GIVEN_PGSQL_USER) - .identifier("xyz00_temp") + .identifier("xyz00_db") .caption("some valid test PgSql-Database") .config(new HashMap<>(ofEntries( entry("encoding", "LATIN1") @@ -94,8 +94,9 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", - "'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" + "'PGSQL_DATABASE:xyz00_db.bookingItem' must be null but is of type CLOUD_SERVER", + "'PGSQL_DATABASE:xyz00_db.parentAsset' must be of type PGSQL_USER but is of type PGSQL_INSTANCE", + "'PGSQL_DATABASE:xyz00_db.assignedToAsset' must be null but is of type PGSQL_INSTANCE" ); } @@ -115,8 +116,8 @@ class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest { // then assertThat(result).containsExactlyInAnyOrder( - "'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'", - "'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer" + "'PGSQL_DATABASE:xyz00_db.config.unknown' is not expected but is set to 'wrong'", + "'PGSQL_DATABASE:xyz00_db.config.encoding' is expected to be of type String, but is of type Integer" ); }