diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/DomainSetupHostingAssetFactory.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/DomainSetupHostingAssetFactory.java index 248d3bee..3babaf41 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/DomainSetupHostingAssetFactory.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/DomainSetupHostingAssetFactory.java @@ -15,11 +15,9 @@ import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import jakarta.validation.ValidationException; import java.net.IDN; import java.util.List; -import java.util.Optional; import java.util.function.Function; -import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetTypeResource.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_HTTP_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_MBOX_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP; @@ -37,33 +35,62 @@ public class DomainSetupHostingAssetFactory extends HostingAssetFactory { @Override protected HsHostingAsset create() { final var domainSetupAsset = createDomainSetupAsset(getDomainName()); + final var subHostingAssets = domainSetupAsset.getSubHostingAssets(); // TODO.legacy: as long as we need to be compatible, we always do all technical domain-setups + final var subHostingAssetResources = getSubHostingAssetResources(); + + subHostingAssets.add( + createDomainSubSetupAssetEntity( + domainSetupAsset, + DOMAIN_HTTP_SETUP, + builder -> builder + .assignedToAsset(assignedToUnixUserAsset.getParentAsset()) + .identifier(getDomainName() + "|MBOX") + .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))) + ); + + domainHttpSetupAsset.setParentAsset(domainSetupAsset); + final var assignedToUnixUserAsset = + emw.find(HsHostingAssetRealEntity.class, domainHttpSetupAssetResource.getAssignedToAssetUuid()); + domainHttpSetupAsset.setAssignedToAsset(assignedToUnixUserAsset); + + final var domainHttpSetupAsset = createDomainHttpSetupAssetEntity( subHostingAssetResources, getDomainName(), domainSetupAsset); final var assignedToUnixUserAsset = domainHttpSetupAsset.getAssignedToAsset(); - // TODO.legacy: don't create DNS setup as long as we need to remain support legacy web-ui etc. - assertDomainDnsSetupAssetNotSupplied(domainSetupAsset); - + // do not add to subHostingAssets, in compatibility mode, DNS setup works via file system createDomainSubSetupAssetEntity( domainSetupAsset, - DOMAIN_MBOX_SETUP, + DOMAIN_DNS_SETUP, builder -> builder .assignedToAsset(assignedToUnixUserAsset.getParentAsset()) - .identifier(getDomainName() + "|MBOX") + .identifier(getDomainName() + "|DNS") .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))); - createDomainSubSetupAssetEntity( - domainSetupAsset, - DOMAIN_SMTP_SETUP, - builder -> builder - .assignedToAsset(assignedToUnixUserAsset.getParentAsset()) - .identifier(getDomainName() + "|SMTP") - .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))); + subHostingAssets.add( + createDomainSubSetupAssetEntity( + domainSetupAsset, + DOMAIN_MBOX_SETUP, + builder -> builder + .assignedToAsset(assignedToUnixUserAsset.getParentAsset()) + .identifier(getDomainName() + "|MBOX") + .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))) + ); + + subHostingAssets.add( + createDomainSubSetupAssetEntity( + domainSetupAsset, + DOMAIN_SMTP_SETUP, + builder -> builder + .assignedToAsset(assignedToUnixUserAsset.getParentAsset()) + .identifier(getDomainName() + "|SMTP") + .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))) + ); return domainSetupAsset; } @@ -75,68 +102,41 @@ public class DomainSetupHostingAssetFactory extends HostingAssetFactory { .identifier(domainName) .caption(asset.getCaption() != null ? asset.getCaption() : domainName) .alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid())) - .subHostingAssets( // FIXME: is this even used? - standardMapper.mapList(getSubHostingAssetResources(), HsHostingAssetRealEntity.class) - ) + // the sub-hosting-assets get added later .build(); } - private HsHostingAssetRealEntity createDomainHttpSetupAssetEntity( - final List subHostingAssetResources, - final String domainName, - final HsHostingAssetRealEntity domainSetupAsset) { - final var domainHttpSetupAssetResource = subHostingAssetResources.stream() - .filter(ha -> ha.getType() == HsHostingAssetTypeResource.DOMAIN_HTTP_SETUP) - .findFirst().orElseThrow(() -> new ValidationException( - domainName + ": missing target unix user (assignedToHostingAssetUuid) for DOMAIN_HTTP_SETUP ")); - final var domainHttpSetupAsset = domainSetupAsset.getSubHostingAssets() - .stream() - .filter(sha -> sha.getType() == DOMAIN_HTTP_SETUP) - .findFirst() - .orElseThrow(); - domainHttpSetupAsset.setParentAsset(domainSetupAsset); - final var assignedToUnixUserAsset = - emw.find(HsHostingAssetRealEntity.class, domainHttpSetupAssetResource.getAssignedToAssetUuid()); - domainHttpSetupAsset.setAssignedToAsset(assignedToUnixUserAsset); - return domainHttpSetupAsset; - } - - private void assertDomainDnsSetupAssetNotSupplied(final HsHostingAssetRealEntity domainSetupAsset) { - if (getSubHostingAssetResources().stream().anyMatch(ha -> ha.getType() == DOMAIN_DNS_SETUP)) { - throw new ValidationException("domain DNS setup not allowed for legacy compatibility"); - } - } - - private void createDomainSubSetupAssetEntity( + private HsHostingAssetRealEntity createDomainSubSetupAssetEntity( final HsHostingAssetRealEntity domainSetupAsset, final HsHostingAssetType subAssetType, final Function, HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder> builderTransformer) { final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name()); - final var subAssetResource = getSubHostingAssetResources().stream() + + final var subAssetResourceOptional = getSubHostingAssetResources().stream() .filter(ha -> ha.getType() == resourceType) .reduce(Reducer::toSingleElement); - final var subAssetEntity = builderTransformer.apply( + + subAssetResourceOptional.ifPresentOrElse( + subAssetResource -> verifyNotOverspecified(subAssetResource), + () -> { throw new ValidationException("sub-asset of type " + resourceType.name() + " required in legacy mode, but missing"); } + ); + + return builderTransformer.apply( HsHostingAssetRealEntity.builder() .type(subAssetType) .parentAsset(domainSetupAsset)) .build(); - domainSetupAsset.getSubHostingAssets().add(subAssetEntity); - subAssetResource.ifPresent( - res -> { - ofNullable(res.getAssignedToAssetUuid()) - .map(uuid -> emw.find(HsHostingAssetRealEntity.class, uuid)) - .ifPresent(subAssetEntity::setAssignedToAsset); - ofNullable(res.getAlarmContactUuid()) - .map(uuid -> emw.find(HsOfficeContactRealEntity.class, uuid)) - .ifPresent(subAssetEntity::setAlarmContact); - ofNullable(res.getIdentifier()).ifPresent(subAssetEntity::setIdentifier); - ofNullable(res.getCaption()).ifPresent(subAssetEntity::setCaption); - res.getConfig(); - res.get + } + // TODO.legacy: while we need to stay compatible, only default values can be used, thus only the type can be specified + private void verifyNotOverspecified(final HsHostingAssetSubInsertResource givenSubAssetResource) { + final var convert = new ToStringConverter().ignoring("assignedToAssetUuid"); + final var expectedSubAssetResource = new HsHostingAssetSubInsertResource(); + expectedSubAssetResource.setType(givenSubAssetResource.getType()); + if ( !convert.from(givenSubAssetResource).equals(convert.from(expectedSubAssetResource)) ) { + throw new ValidationException("sub asset " + givenSubAssetResource.getType() + " is over-specified, in compatibilty mode, only default values allowed"); + } - } - ); } private String getDomainName() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/ToStringConverter.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/ToStringConverter.java new file mode 100644 index 00000000..e436cfec --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/factories/ToStringConverter.java @@ -0,0 +1,39 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.factories; +import net.hostsharing.hsadminng.mapper.Array; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static java.util.stream.Collectors.joining; + +public class ToStringConverter { + + final public Set ignoredFields = new HashSet<>(); + + public ToStringConverter ignoring(final String fieldName) { + ignoredFields.add(fieldName); + return this; + } + + public String from(Object obj) { + StringBuilder result = new StringBuilder(); + return "{ " + + Arrays.stream(obj.getClass().getDeclaredFields()) + .filter(f -> !ignoredFields.contains(f.getName())) + .map(field -> { + try { + field.setAccessible(true); + return field.getName() + ": " + field.get(obj); + } catch (IllegalAccessException e) { + // ignore inaccessible fields + return null; + } + }) + .filter(Objects::nonNull) + .collect(joining(", ")) + + " }"; + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 15855fc9..d55b22bb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -201,33 +201,33 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup .header("current-subject", "superuser-alex@hostsharing.net") .contentType(ContentType.JSON) .body(""" -{ - "projectUuid": "{projectUuid}", - "type": "DOMAIN_SETUP", - "caption": "Domain-Setup for example.org", - "resources": { - "domainName": "example.org", - "verificationCode": "just-a-fake-verification-code" - }, - "asset": { // FIXME: rename to hostingAsset - "identifier": "example.org", // also as default for all subAssets - "subHostingAssets": [ - { - "type": "DOMAIN_DNS_SETUP" - }, - { - "type": "DOMAIN_HTTP_SETUP", - "assignedToAssetUuid": "{unixUserUuid}" - }, - { - "type": "DOMAIN_MBOX_SETUP" - }, - { - "type": "DOMAIN_SMTP_SETUP" - } - ] - } -} + { + "projectUuid": "{projectUuid}", + "type": "DOMAIN_SETUP", + "caption": "Domain-Setup for example.org", + "resources": { + "domainName": "example.org", + "verificationCode": "just-a-fake-verification-code" + }, + "asset": { // FIXME: rename to hostingAsset + "identifier": "example.org", // also as default for all subAssets + "subHostingAssets": [ + { + "type": "DOMAIN_DNS_SETUP" + }, + { + "type": "DOMAIN_HTTP_SETUP", + "assignedToAssetUuid": "{unixUserUuid}" + }, + { + "type": "DOMAIN_MBOX_SETUP" + }, + { + "type": "DOMAIN_SMTP_SETUP" + } + ] + } + } """ .replace("{projectUuid}", givenProject.getUuid().toString()) .replace("{unixUserUuid}", givenUnixUser.getUuid().toString())