This commit is contained in:
Michael Hoennig 2024-10-04 17:19:37 +02:00
parent 7f066f8b6a
commit 4c4cd886bc
3 changed files with 128 additions and 89 deletions

View File

@ -15,11 +15,9 @@ import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import java.net.IDN; import java.net.IDN;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP;
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_HTTP_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_MBOX_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP;
@ -37,33 +35,62 @@ public class DomainSetupHostingAssetFactory extends HostingAssetFactory {
@Override @Override
protected HsHostingAsset create() { protected HsHostingAsset create() {
final var domainSetupAsset = createDomainSetupAsset(getDomainName()); 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 // TODO.legacy: as long as we need to be compatible, we always do all technical domain-setups
final var subHostingAssetResources = getSubHostingAssetResources(); 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( final var domainHttpSetupAsset = createDomainHttpSetupAssetEntity(
subHostingAssetResources, subHostingAssetResources,
getDomainName(), getDomainName(),
domainSetupAsset); domainSetupAsset);
final var assignedToUnixUserAsset = domainHttpSetupAsset.getAssignedToAsset(); final var assignedToUnixUserAsset = domainHttpSetupAsset.getAssignedToAsset();
// TODO.legacy: don't create DNS setup as long as we need to remain support legacy web-ui etc. // do not add to subHostingAssets, in compatibility mode, DNS setup works via file system
assertDomainDnsSetupAssetNotSupplied(domainSetupAsset);
createDomainSubSetupAssetEntity( createDomainSubSetupAssetEntity(
domainSetupAsset, domainSetupAsset,
DOMAIN_MBOX_SETUP, DOMAIN_DNS_SETUP,
builder -> builder builder -> builder
.assignedToAsset(assignedToUnixUserAsset.getParentAsset()) .assignedToAsset(assignedToUnixUserAsset.getParentAsset())
.identifier(getDomainName() + "|MBOX") .identifier(getDomainName() + "|DNS")
.caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))); .caption("HTTP-Setup für " + IDN.toUnicode(getDomainName())));
createDomainSubSetupAssetEntity( subHostingAssets.add(
domainSetupAsset, createDomainSubSetupAssetEntity(
DOMAIN_SMTP_SETUP, domainSetupAsset,
builder -> builder DOMAIN_MBOX_SETUP,
.assignedToAsset(assignedToUnixUserAsset.getParentAsset()) builder -> builder
.identifier(getDomainName() + "|SMTP") .assignedToAsset(assignedToUnixUserAsset.getParentAsset())
.caption("HTTP-Setup für " + IDN.toUnicode(getDomainName()))); .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; return domainSetupAsset;
} }
@ -75,68 +102,41 @@ public class DomainSetupHostingAssetFactory extends HostingAssetFactory {
.identifier(domainName) .identifier(domainName)
.caption(asset.getCaption() != null ? asset.getCaption() : domainName) .caption(asset.getCaption() != null ? asset.getCaption() : domainName)
.alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid())) .alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid()))
.subHostingAssets( // FIXME: is this even used? // the sub-hosting-assets get added later
standardMapper.mapList(getSubHostingAssetResources(), HsHostingAssetRealEntity.class)
)
.build(); .build();
} }
private HsHostingAssetRealEntity createDomainHttpSetupAssetEntity( private HsHostingAssetRealEntity createDomainSubSetupAssetEntity(
final List<HsHostingAssetSubInsertResource> 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(
final HsHostingAssetRealEntity domainSetupAsset, final HsHostingAssetRealEntity domainSetupAsset,
final HsHostingAssetType subAssetType, final HsHostingAssetType subAssetType,
final Function<HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>, HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>> builderTransformer) { final Function<HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>, HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>> builderTransformer) {
final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name()); final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name());
final var subAssetResource = getSubHostingAssetResources().stream()
final var subAssetResourceOptional = getSubHostingAssetResources().stream()
.filter(ha -> ha.getType() == resourceType) .filter(ha -> ha.getType() == resourceType)
.reduce(Reducer::toSingleElement); .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() HsHostingAssetRealEntity.builder()
.type(subAssetType) .type(subAssetType)
.parentAsset(domainSetupAsset)) .parentAsset(domainSetupAsset))
.build(); .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() { private String getDomainName() {

View File

@ -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<String> 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(", "))
+ " }";
}
}

View File

@ -201,33 +201,33 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
.header("current-subject", "superuser-alex@hostsharing.net") .header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {
"projectUuid": "{projectUuid}", "projectUuid": "{projectUuid}",
"type": "DOMAIN_SETUP", "type": "DOMAIN_SETUP",
"caption": "Domain-Setup for example.org", "caption": "Domain-Setup for example.org",
"resources": { "resources": {
"domainName": "example.org", "domainName": "example.org",
"verificationCode": "just-a-fake-verification-code" "verificationCode": "just-a-fake-verification-code"
}, },
"asset": { // FIXME: rename to hostingAsset "asset": { // FIXME: rename to hostingAsset
"identifier": "example.org", // also as default for all subAssets "identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [ "subHostingAssets": [
{ {
"type": "DOMAIN_DNS_SETUP" "type": "DOMAIN_DNS_SETUP"
}, },
{ {
"type": "DOMAIN_HTTP_SETUP", "type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserUuid}" "assignedToAssetUuid": "{unixUserUuid}"
}, },
{ {
"type": "DOMAIN_MBOX_SETUP" "type": "DOMAIN_MBOX_SETUP"
}, },
{ {
"type": "DOMAIN_SMTP_SETUP" "type": "DOMAIN_SMTP_SETUP"
} }
] ]
} }
} }
""" """
.replace("{projectUuid}", givenProject.getUuid().toString()) .replace("{projectUuid}", givenProject.getUuid().toString())
.replace("{unixUserUuid}", givenUnixUser.getUuid().toString()) .replace("{unixUserUuid}", givenUnixUser.getUuid().toString())