HostingAsset-Hierarchie spec in enum HsHostingAssetType and generates PlantUML #72

Merged
hsh-michaelhoennig merged 9 commits from add-mermaid-graph-generator-for-hosting-asset-type-structure into master 2024-07-09 14:32:14 +02:00
14 changed files with 414 additions and 140 deletions
Showing only changes of commit f5802ed404 - Show all commits

View File

@ -1,14 +1,12 @@
## HostingAsset Type Structure ## HostingAsset Type Structure
### packages Server, Webspace, Domain ### Domain
```plantuml ```plantuml
@startuml @startuml
left to right direction left to right direction
package Booking #99bcdb { package Booking #feb28c {
entity BI_PRIVATE_CLOUD entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER entity BI_MANAGED_SERVER
@ -17,7 +15,16 @@ package Booking #99bcdb {
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
} }
package Hosting #white { package Hosting #feb28c{
package Domain #99bcdb {
entity HA_DOMAIN_SETUP
entity HA_DOMAIN_DNS_SETUP
entity HA_DOMAIN_HTTP_SETUP
entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP
entity HA_DOMAIN_EMAIL_MAILBOX_SETUP
entity HA_EMAIL_ADDRESS
}
package Server #99bcdb { package Server #99bcdb {
entity HA_CLOUD_SERVER entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER entity HA_MANAGED_SERVER
@ -30,15 +37,6 @@ package Hosting #white {
entity HA_EMAIL_ALIAS entity HA_EMAIL_ALIAS
} }
package Domain #99bcdb {
entity HA_DOMAIN_SETUP
entity HA_DOMAIN_DNS_SETUP
entity HA_DOMAIN_HTTP_SETUP
entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP
entity HA_DOMAIN_EMAIL_MAILBOX_SETUP
entity HA_EMAIL_ADDRESS
}
} }
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
@ -50,18 +48,21 @@ HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
HA_DOMAIN_DNS_SETUP ==* BI_DOMAIN_DNS_SETUP HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
HA_DOMAIN_HTTP_SETUP *==> HA_MANAGED_WEBSPACE HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP
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_EMAIL_SUBMISSION_SETUP ==* BI_DOMAIN_EMAIL_SUBMISSION_SETUP HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE
HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_MANAGED_WEBSPACE HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_EMAIL_MAILBOX_SETUP o..> HA_MANAGED_WEBSPACE
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend {
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
@ -69,13 +70,13 @@ package Legend {
} }
Booking -down[hidden]->Legend Booking -down[hidden]->Legend
``` ```
### packages MariaDB, Server, Webspace ### MariaDB
```plantuml ```plantuml
@startuml @startuml
left to right direction left to right direction
package Booking #99bcdb { package Booking #feb28c {
entity BI_PRIVATE_CLOUD entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER entity BI_MANAGED_SERVER
@ -84,7 +85,7 @@ package Booking #99bcdb {
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
} }
package Hosting #white { package Hosting #feb28c{
package MariaDB #99bcdb { package MariaDB #99bcdb {
entity HA_MARIADB_INSTANCE entity HA_MARIADB_INSTANCE
entity HA_MARIADB_USER entity HA_MARIADB_USER
@ -123,7 +124,8 @@ HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend {
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
@ -131,13 +133,13 @@ package Legend {
} }
Booking -down[hidden]->Legend Booking -down[hidden]->Legend
``` ```
### packages Server, PostgreSQL, Webspace ### PostgreSQL
```plantuml ```plantuml
@startuml @startuml
left to right direction left to right direction
package Booking #99bcdb { package Booking #feb28c {
entity BI_PRIVATE_CLOUD entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER entity BI_MANAGED_SERVER
@ -146,19 +148,19 @@ package Booking #99bcdb {
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
} }
package Hosting #white { package Hosting #feb28c{
package Server #99bcdb {
entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER
entity HA_IP_NUMBER
}
package PostgreSQL #99bcdb { package PostgreSQL #99bcdb {
entity HA_PGSQL_INSTANCE entity HA_PGSQL_INSTANCE
entity HA_PGSQL_USER entity HA_PGSQL_USER
entity HA_PGSQL_DATABASE entity HA_PGSQL_DATABASE
} }
package Server #99bcdb {
entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER
entity HA_IP_NUMBER
}
package Webspace #99bcdb { package Webspace #99bcdb {
entity HA_MANAGED_WEBSPACE entity HA_MANAGED_WEBSPACE
entity HA_UNIX_USER entity HA_UNIX_USER
@ -185,7 +187,8 @@ HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE
HA_IP_NUMBER o..> HA_CLOUD_SERVER HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend {
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1 ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1

View File

@ -203,6 +203,9 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
.switchOnColumn("type", .switchOnColumn("type",
inCaseOf("DOMAIN_SETUP", then -> { inCaseOf("DOMAIN_SETUP", then -> {
// grant(ADMIN).to(currentlyAssumedRole() // FIXME
// oder:
// via with.incomingSuperRole("bookingItem", ADMIN); (s.u.)
then.toRole(GLOBAL, GUEST).grantPermission(INSERT); then.toRole(GLOBAL, GUEST).grantPermission(INSERT);
then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution
}) })

View File

@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.item.Node; import net.hostsharing.hsadminng.hs.booking.item.Node;
import javax.naming.NamingException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -16,11 +17,14 @@ import java.util.function.Function;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining; 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.*;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL; 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.RelationPolicy.REQUIRED;
public enum HsHostingAssetType implements Node { public enum HsHostingAssetType implements Node {
SAME_TYPE, // pseudo-type for recursive references
CLOUD_SERVER( // named e.g. vm1234 CLOUD_SERVER( // named e.g. vm1234
inGroup("Server"), inGroup("Server"),
requires(HsBookingItemType.CLOUD_SERVER)), requires(HsBookingItemType.CLOUD_SERVER)),
@ -38,28 +42,29 @@ public enum HsHostingAssetType implements Node {
inGroup("Webspace"), inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)), requiredParent(MANAGED_WEBSPACE)),
@Deprecated
DOMAIN_SETUP( // named e.g. example.org DOMAIN_SETUP( // named e.g. example.org
inGroup("Domain") inGroup("Domain"),
optionalParent(SAME_TYPE)
), ),
DOMAIN_DNS_SETUP( // named e.g. example.org DOMAIN_DNS_SETUP( // named e.g. example.org
inGroup("Domain"), inGroup("Domain"),
requires(HsBookingItemType.DOMAIN_DNS_SETUP)), requiredParent(DOMAIN_SETUP)),
DOMAIN_HTTP_SETUP( // named e.g. example.org DOMAIN_HTTP_SETUP( // named e.g. example.org
inGroup("Domain"), inGroup("Domain"),
requiredParent(MANAGED_WEBSPACE), requiredParent(DOMAIN_SETUP),
assignedTo(UNIX_USER)), assignedTo(UNIX_USER)),
DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org
inGroup("Domain"), inGroup("Domain"),
requires(HsBookingItemType.DOMAIN_EMAIL_SUBMISSION_SETUP), requiredParent(DOMAIN_SETUP),
optionallyAssignedTo(MANAGED_WEBSPACE)), assignedTo(MANAGED_WEBSPACE)),
DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org
inGroup("Domain"), inGroup("Domain"),
requiredParent(MANAGED_WEBSPACE)), requiredParent(DOMAIN_SETUP),
assignedTo(MANAGED_WEBSPACE)),
// TODO.spec: SECURE_MX // TODO.spec: SECURE_MX
@ -106,7 +111,7 @@ public enum HsHostingAssetType implements Node {
assignedTo(MANAGED_WEBSPACE) assignedTo(MANAGED_WEBSPACE)
); );
final String groupName; private final String groupName;
private final EntityTypeRelation<?, ?>[] relations; private final EntityTypeRelation<?, ?>[] relations;
HsHostingAssetType( HsHostingAssetType(
@ -117,6 +122,11 @@ public enum HsHostingAssetType implements Node {
this.relations = relations; this.relations = relations;
} }
HsHostingAssetType() {
this.groupName = null;
this.relations = null;
}
/// just syntactic sugar /// just syntactic sugar
private static String inGroup(final String groupName) { private static String inGroup(final String groupName) {
return groupName; return groupName;
@ -124,50 +134,62 @@ public enum HsHostingAssetType implements Node {
public RelationPolicy bookingItemPolicy() { public RelationPolicy bookingItemPolicy() {
return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType)
.map(r -> r.relationType) .map(r -> r.relationType)
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsBookingItemType bookingItemType() { public HsBookingItemType bookingItemType() {
return stream(relations).filter(r -> r.relatedType == (Object) HsBookingItemType.class).findAny() return stream(relations).filter(r -> r.relatedType instanceof HsBookingItemType)
.map(r -> HsBookingItemType.valueOf(r.relatedType.toString())) .map(r -> HsBookingItemType.valueOf(r.relatedType.toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .orElse(null);
} }
public RelationPolicy parentAssetPolicy() { public RelationPolicy parentAssetPolicy() {
return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType)
.map(r -> r.relationType) .map(r -> r.relationType)
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsHostingAssetType parentAssetType() { public HsHostingAssetType parentAssetType() {
return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() return stream(relations).filter(r -> r.relatedType instanceof HsHostingAssetType)
.map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .orElse(null);
} }
public RelationPolicy assignedToAssetPolicy() { public RelationPolicy assignedToAssetPolicy() {
return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class)
.map(r -> r.relationType) .map(r -> r.relationType)
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(RelationPolicy.FORBIDDEN); .orElse(RelationPolicy.FORBIDDEN);
} }
public HsHostingAssetType assignedToAssetType() { public HsHostingAssetType assignedToAssetType() {
return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class).findAny() return stream(relations).filter(r -> r.relatedType == (Object) HsHostingAssetType.class)
.map(r -> HsHostingAssetType.valueOf(r.relatedType.toString())) .map(r -> HsHostingAssetType.valueOf(r.relatedType.toString()))
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
.orElse(null); .orElse(null);
} }
private static <X> X onlyASingleElementExpectedException(Object a, Object b) {
throw new IllegalStateException("Only a single element expected to match criteria.");
}
@Override @Override
public List<String> edges() { public List<String> edges() {
return stream(relations) return stream(relations)
.map(r -> nodeName() + r.edge + r.relatedType.nodeName()) .map(r -> nodeName() + r.edge + resolveNode(r.relatedType).nodeName())
.toList(); .toList();
} }
private Node resolveNode(final Node node) {
return node == SAME_TYPE ? this : node;
}
@Override @Override
public String nodeName() { public String nodeName() {
return "HA_" + name(); return "HA_" + name();
@ -181,100 +203,107 @@ public enum HsHostingAssetType implements Node {
return type == null ? null : type.name(); return type == null ? null : type.name();
} }
private static void renderAsPlantUML(final Set<String> includedHostingGroups) throws IOException { private static String renderAsPlantUML(final String caption, final Set<String> includedHostingGroups) {
final String bookingNodes = stream(HsBookingItemType.values()) final String bookingNodes = stream(HsBookingItemType.values())
.map(t -> " entity " + t.nodeName()) .map(t -> " entity " + t.nodeName())
.collect(joining("\n")); .collect(joining("\n"));
final String hostingGroups = includedHostingGroups.stream() final String hostingGroups = includedHostingGroups.stream().sorted()
.map(HsHostingAssetType::generateGroup) .map(HsHostingAssetType::generateGroup)
.collect(joining("\n")); .collect(joining("\n"));
final String hostingAssetNodes = stream(HsHostingAssetType.values()) final String hostingAssetNodes = stream(HsHostingAssetType.values())
.filter(a -> includedHostingGroups.contains(a.groupName)) .filter(t -> t.isInGroups(includedHostingGroups))
.map(a -> "entity "+a.nodeName()) .map(t -> "entity " + t.nodeName())
.collect(joining("\n")); .collect(joining("\n"));
final String bookingItemEdges = stream(HsBookingItemType.values()) final String bookingItemEdges = stream(HsBookingItemType.values())
.map(HsBookingItemType::edges) .map(HsBookingItemType::edges)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(joining("\n")); .collect(joining("\n"));
final String hostingAssetEdges = stream(HsHostingAssetType.values()) final String hostingAssetEdges = stream(HsHostingAssetType.values())
.filter(t -> includedHostingGroups.contains(t.groupName)) .filter(t -> t.isInGroups(includedHostingGroups))
.map(HsHostingAssetType::edges) .map(HsHostingAssetType::edges)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.collect(joining("\n")); .collect(joining("\n"));
Files.writeString( return """
Path.of("doc/hs-hosting-asset-type-structure.md"),
### %{caption}
```plantuml
@startuml
left to right direction
package Booking #feb28c {
%{bookingNodes}
}
package Hosting #feb28c{
%{hostingGroups}
}
%{bookingItemEdges}
%{hostingAssetEdges}
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
}
Booking -down[hidden]->Legend
```
""" """
### packages %{packages} .replace("%{caption}", caption)
.replace("%{bookingNodes}", bookingNodes)
.replace("%{hostingGroups}", hostingGroups)
.replace("%{hostingAssetNodeStyles}", hostingAssetNodes)
.replace("%{bookingItemEdges}", bookingItemEdges)
.replace("%{hostingAssetEdges}", hostingAssetEdges);
}
```plantuml private boolean isInGroups(final Set<String> assetGroups) {
@startuml return groupName != null && assetGroups.contains(groupName);
left to right direction
package Booking #99bcdb {
%{bookingNodes}
}
package Hosting #white {
%{hostingGroups}
}
%{bookingItemEdges}
%{hostingAssetEdges}
package Legend {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
}
Booking -down[hidden]->Legend
```
"""
.replace("%{packages}", String.join(", ", includedHostingGroups))
.replace("%{bookingNodes}", bookingNodes)
.replace("%{hostingGroups}", hostingGroups)
.replace("%{hostingAssetNodeStyles}", hostingAssetNodes)
.replace("%{bookingItemEdges}", bookingItemEdges)
.replace("%{hostingAssetEdges}", hostingAssetEdges),
StandardOpenOption.APPEND);
} }
private static String generateGroup(final String group) { private static String generateGroup(final String group) {
return " package " + group + " #99bcdb {\n" return " package " + group + " #99bcdb {\n"
+ stream(HsHostingAssetType.values()) + stream(HsHostingAssetType.values())
.filter(t -> t.groupName.equals(group)) .filter(t -> group.equals(t.groupName))
.map(t -> " entity " + t.nodeName()) .map(t -> " entity " + t.nodeName())
.collect(joining(" \n")) .collect(joining("\n"))
+ "\n }\n"; + "\n }\n";
} }
public static void main(final String[] args) throws IOException { static String renderAsEmbeddedPlantUml() {
Files.writeString(
Path.of("doc/hs-hosting-asset-type-structure.md"),
"""
final var markdown = new StringBuilder("""
## HostingAsset Type Structure ## HostingAsset Type Structure
""", """);
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
// renderAsPlantUML(stream(HsHostingAssetType.values()) // rendering all types in a single diagram is currently ignored
// .map(t -> t.groupName) renderAsPlantUML("Domain", stream(HsHostingAssetType.values())
// .collect(toSet())); .filter(t -> t.groupName != null)
.map(t -> t.groupName)
.collect(toSet()));
renderAsPlantUML(Set.of("Domain", "Webspace", "Server")); markdown.append(renderAsPlantUML("Domain", Set.of("Domain", "Webspace", "Server")))
renderAsPlantUML(Set.of("MariaDB", "Webspace", "Server")); .append(renderAsPlantUML("MariaDB", Set.of("MariaDB", "Webspace", "Server")))
renderAsPlantUML(Set.of("PostgreSQL", "Webspace", "Server")); .append(renderAsPlantUML("PostgreSQL", Set.of("PostgreSQL", "Webspace", "Server")));
markdown.append("""
This code generated was by %{this}.main, do not amend manually.
"""
.replace("%{this}", HsHostingAssetType.class.getSimpleName()));
return markdown.toString();
}
public static void main(final String[] args) throws IOException, NamingException {
Files.writeString( Files.writeString(
Path.of("doc/hs-hosting-asset-type-structure.md"), Path.of("doc/hs-hosting-asset-type-structure.md"),
""" renderAsEmbeddedPlantUml(),
This code generated was by %{this}.main, do not amend manually. StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
"""
.replace("%{this}", HsHostingAssetType.class.getSimpleName()),
StandardOpenOption.APPEND);
} }
public enum RelationPolicy { public enum RelationPolicy {
@ -285,27 +314,23 @@ public enum HsHostingAssetType implements Node {
@AllArgsConstructor @AllArgsConstructor
class EntityTypeRelation<E, T extends Node> { class EntityTypeRelation<E, T extends Node> {
final HsHostingAssetType.RelationPolicy relationType; final HsHostingAssetType.RelationPolicy relationType;
private final Function<HsHostingAssetEntity, E> getter; final Function<HsHostingAssetEntity, E> getter;
final T relatedType; final T relatedType;
final String edge; final String edge;
static EntityTypeRelation<HsBookingItemEntity, HsBookingItemType> requires(final HsBookingItemType bookingItemType) { static EntityTypeRelation<HsBookingItemEntity, HsBookingItemType> requires(final HsBookingItemType bookingItemType) {
return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " ==* "); return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> "); return new EntityTypeRelation<>(OPTIONAL, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " *==> "); return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> ");
} }
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) { static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(REQUIRED, null, hostingAssetType, " o..> "); return new EntityTypeRelation<>(REQUIRED, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> ");
}
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionallyAssignedTo(final HsHostingAssetType hostingAssetType) {
return new EntityTypeRelation<>(OPTIONAL, null, hostingAssetType, " o..> ");
} }
} }

View File

@ -1,15 +1,16 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators; package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator { class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator {
HsCloudServerHostingAssetValidator() { HsCloudServerHostingAssetValidator() {
super( super(
HsHostingAssetType.CLOUD_SERVER, CLOUD_SERVER,
AlarmContact.isOptional(), AlarmContact.isOptional(),
NO_EXTRA_PROPERTIES); NO_EXTRA_PROPERTIES);
} }

View File

@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
@ -20,6 +21,36 @@ class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator {
this.identifierPattern = Pattern.compile(DOMAIN_NAME_REGEX); this.identifierPattern = Pattern.compile(DOMAIN_NAME_REGEX);
} }
@Override
public List<String> validateEntity(final HsHostingAssetEntity assetEntity) {
// TODO.impl: for newly created entities, check the permission of setting up a domain
//
// reject, if the domain is any of these:
// hostsharing.com|net|org|coop, // just to be on the safe side
// [^.}+, // top-level-domain
// co.uk, org.uk, gov.uk, ac.uk, sch.uk,
// com.au, net.au, org.au, edu.au, gov.au, asn.au, id.au,
// co.jp, ne.jp, or.jp, ac.jp, go.jp,
// com.cn, net.cn, org.cn, gov.cn, edu.cn, ac.cn,
// com.br, net.br, org.br, gov.br, edu.br, mil.br, art.br,
// co.in, net.in, org.in, gen.in, firm.in, ind.in,
// com.mx, net.mx, org.mx, gob.mx, edu.mx,
// gov.it, edu.it,
// co.nz, net.nz, org.nz, govt.nz, ac.nz, school.nz, geek.nz, kiwi.nz,
// co.kr, ne.kr, or.kr, go.kr, re.kr, pe.kr
//
// allow if
// - user has Admin/Agent-role for all its sub-domains and the direct parent-Domain which are set up at at Hostsharing
// - domain has DNS zone with TXT record approval
// - parent-domain has DNS zone with TXT record approval
// - dom
//
// TXT-Record check:
// new InitialDirContext().getAttributes("dns:_netblocks.google.com", new String[] { "TXT"}).get("TXT").getAll();
return super.validateEntity(assetEntity);
}
@Override @Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return identifierPattern; return identifierPattern;

View File

@ -190,20 +190,6 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
} }
} }
static class ParentAssetReferenceValidation extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
ParentAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType parentAssetType) {
super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType);
}
}
static class AssignedToAssetReferenceValidation extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
AssignedToAssetReferenceValidation(final HsHostingAssetType.RelationPolicy policy, final HsHostingAssetType assignedToAssetType) {
super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType);
}
}
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> { static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
AlarmContact(final HsHostingAssetType.RelationPolicy policy) { AlarmContact(final HsHostingAssetType.RelationPolicy policy) {

View File

@ -20,6 +20,7 @@ public class HsHostingAssetEntityValidatorRegistry {
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator()); register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
register(UNIX_USER, new HsUnixUserHostingAssetValidator()); register(UNIX_USER, new HsUnixUserHostingAssetValidator());
register(EMAIL_ALIAS, new HsEMailAliasHostingAssetValidator()); register(EMAIL_ALIAS, new HsEMailAliasHostingAssetValidator());
register(DOMAIN_SETUP, new HsDomainSetupHostingAssetValidator());
register(DOMAIN_DNS_SETUP, new HsDomainDnsSetupHostingAssetValidator()); register(DOMAIN_DNS_SETUP, new HsDomainDnsSetupHostingAssetValidator());
} }

View File

@ -4,12 +4,12 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator { class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
public HsManagedWebspaceHostingAssetValidator() { public HsManagedWebspaceHostingAssetValidator() {
super( super(
MANAGED_SERVER, MANAGED_WEBSPACE,
AlarmContact.isOptional(), // hostmaster alert address is implicitly added AlarmContact.isOptional(), // hostmaster alert address is implicitly added
NO_EXTRA_PROPERTIES); NO_EXTRA_PROPERTIES);
} }

View File

@ -18,7 +18,7 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
HsUnixUserHostingAssetValidator() { HsUnixUserHostingAssetValidator() {
super( super(
HsHostingAssetType.DOMAIN_DNS_SETUP, HsHostingAssetType.UNIX_USER,
AlarmContact.isOptional(), AlarmContact.isOptional(),
integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(), integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(),

View File

@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.hs.validation; package net.hostsharing.hsadminng.hs.validation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -18,7 +19,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
public final ValidatableProperty<?, ?>[] propertyValidators; public final ValidatableProperty<?, ?>[] propertyValidators;
public HsEntityValidator(final ValidatableProperty<?, ?>... validators) { public <T extends Enum <T>> HsEntityValidator(final ValidatableProperty<?, ?>... validators) {
propertyValidators = validators; propertyValidators = validators;
stream(propertyValidators).forEach(p -> p.deferredInit(propertyValidators)); stream(propertyValidators).forEach(p -> p.deferredInit(propertyValidators));
} }

View File

@ -27,6 +27,7 @@ import java.util.Map;
import static java.util.Map.entry; import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
@ -165,8 +166,9 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
context("person-SmithPeter@example.com"); context("person-SmithPeter@example.com");
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newAsset = HsHostingAssetEntity.builder() final var newAsset = HsHostingAssetEntity.builder()
.caption("some new domain setup") .type(DOMAIN_SETUP)
.identifier("example.org") .identifier("example.org")
.caption("some new domain setup")
.build(); .build();
return toCleanup(assetRepo.save(newAsset)); return toCleanup(assetRepo.save(newAsset));
}); });
@ -181,7 +183,6 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
} }
private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) { private void assertThatAssetIsPersisted(final HsHostingAssetEntity saved) {
final var context =
attempt(em, () -> { attempt(em, () -> {
final var found = assetRepo.findByUuid(saved.getUuid()); final var found = assetRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).get().isEqualTo(saved.toString()); assertThat(found).isNotEmpty().map(HsHostingAssetEntity::toString).get().isEqualTo(saved.toString());

View File

@ -0,0 +1,219 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class HsHostingAssetTypeUnitTest {
@Test
void generatedPlantUML() {
final var result = HsHostingAssetType.renderAsEmbeddedPlantUml();
assertThat(result).isEqualTo("""
## HostingAsset Type Structure
### Domain
```plantuml
@startuml
left to right direction
package Booking #feb28c {
entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER
entity BI_MANAGED_WEBSPACE
entity BI_DOMAIN_DNS_SETUP
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
}
package Hosting #feb28c{
package Domain #99bcdb {
entity HA_DOMAIN_SETUP
entity HA_DOMAIN_DNS_SETUP
entity HA_DOMAIN_HTTP_SETUP
entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP
entity HA_DOMAIN_EMAIL_MAILBOX_SETUP
entity HA_EMAIL_ADDRESS
}
package Server #99bcdb {
entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER
entity HA_IP_NUMBER
}
package Webspace #99bcdb {
entity HA_MANAGED_WEBSPACE
entity HA_UNIX_USER
entity HA_EMAIL_ALIAS
}
}
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER
HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE
HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_DOMAIN_SETUP
HA_DOMAIN_EMAIL_MAILBOX_SETUP o..> HA_MANAGED_WEBSPACE
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP
HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
}
Booking -down[hidden]->Legend
```
### MariaDB
```plantuml
@startuml
left to right direction
package Booking #feb28c {
entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER
entity BI_MANAGED_WEBSPACE
entity BI_DOMAIN_DNS_SETUP
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
}
package Hosting #feb28c{
package MariaDB #99bcdb {
entity HA_MARIADB_INSTANCE
entity HA_MARIADB_USER
entity HA_MARIADB_DATABASE
}
package Server #99bcdb {
entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER
entity HA_IP_NUMBER
}
package Webspace #99bcdb {
entity HA_MANAGED_WEBSPACE
entity HA_UNIX_USER
entity HA_EMAIL_ALIAS
}
}
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
HA_MARIADB_USER *==> HA_MARIADB_INSTANCE
HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE
HA_MARIADB_DATABASE *==> HA_MANAGED_WEBSPACE
HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE
HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
}
Booking -down[hidden]->Legend
```
### PostgreSQL
```plantuml
@startuml
left to right direction
package Booking #feb28c {
entity BI_PRIVATE_CLOUD
entity BI_CLOUD_SERVER
entity BI_MANAGED_SERVER
entity BI_MANAGED_WEBSPACE
entity BI_DOMAIN_DNS_SETUP
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
}
package Hosting #feb28c{
package PostgreSQL #99bcdb {
entity HA_PGSQL_INSTANCE
entity HA_PGSQL_USER
entity HA_PGSQL_DATABASE
}
package Server #99bcdb {
entity HA_CLOUD_SERVER
entity HA_MANAGED_SERVER
entity HA_IP_NUMBER
}
package Webspace #99bcdb {
entity HA_MANAGED_WEBSPACE
entity HA_UNIX_USER
entity HA_EMAIL_ALIAS
}
}
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER
HA_PGSQL_USER *==> HA_PGSQL_INSTANCE
HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE
HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE
HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE
HA_IP_NUMBER o..> HA_CLOUD_SERVER
HA_IP_NUMBER o..> HA_MANAGED_SERVER
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
package Legend #white {
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
}
Booking -down[hidden]->Legend
```
This code generated was by HsHostingAssetType.main, do not amend manually.
""");
}
}

View File

@ -11,6 +11,7 @@ import java.util.Map;
import static java.util.Map.entry; import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_COMMENT; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_COMMENT;
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_DATA; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_DATA;
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_TYPE; import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator.RR_RECORD_TYPE;
@ -22,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class HsDomainDnsSetupHostingAssetValidatorUnitTest { class HsDomainDnsSetupHostingAssetValidatorUnitTest {
static final HsHostingAssetEntity validDomainSetupEntity = HsHostingAssetEntity.builder() static final HsHostingAssetEntity validDomainSetupEntity = HsHostingAssetEntity.builder()
.type(DOMAIN_SETUP)
.identifier("example.org") .identifier("example.org")
.build(); .build();

View File

@ -34,6 +34,7 @@ class HsHostingAssetEntityValidatorRegistryUnitTest {
HsHostingAssetType.MANAGED_WEBSPACE, HsHostingAssetType.MANAGED_WEBSPACE,
HsHostingAssetType.UNIX_USER, HsHostingAssetType.UNIX_USER,
HsHostingAssetType.EMAIL_ALIAS, HsHostingAssetType.EMAIL_ALIAS,
HsHostingAssetType.DOMAIN_SETUP,
HsHostingAssetType.DOMAIN_DNS_SETUP HsHostingAssetType.DOMAIN_DNS_SETUP
); );
} }