add DomainSetup-HostingAssets for new BookingItem via created-event #111

Merged
hsh-michaelhoennig merged 31 commits from add-hsoting-asset-to-booking-item-resource-and-created-event into master 2024-10-08 11:48:35 +02:00
3 changed files with 128 additions and 89 deletions
Showing only changes of commit 4c4cd886bc - Show all commits

View File

@ -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<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(
private HsHostingAssetRealEntity createDomainSubSetupAssetEntity(
final HsHostingAssetRealEntity domainSetupAsset,
final HsHostingAssetType subAssetType,
final Function<HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>, 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() {

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")
.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())