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
4 changed files with 213 additions and 196 deletions
Showing only changes of commit 4d1d4cb9e9 - Show all commits

View File

@ -1,196 +0,0 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetSubInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetTypeResource;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import jakarta.validation.ValidationException;
import java.net.IDN;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
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;
@Component
public class HsBookingItemCreatedListener implements ApplicationListener<BookingItemCreatedAppEvent> {
@Autowired
private EntityManagerWrapper emw;
@Autowired
private ObjectMapper jsonMapper;
@Autowired
private StandardMapper standardMapper;
@Override
@SneakyThrows
public void onApplicationEvent(final BookingItemCreatedAppEvent event) {
final var newBookingItemRealEntity = event.getEntity().getBookingItem();
final var asset = jsonMapper.readValue(event.getEntity().getAssetJson(), HsHostingAssetAutoInsertResource.class);
final var factory = switch (newBookingItemRealEntity.getType()) {
case PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER -> null; // for now, no automatic HostingAsset possible
case MANAGED_WEBSPACE -> null; // TODO.impl: implement ManagedWebspace HostingAsset creation, where possible
case DOMAIN_SETUP -> new DomainSetupHostingAssetFactory(newBookingItemRealEntity, asset);
};
if (factory != null) {
event.getEntity().setStatusMessage(factory.performSaveProcess());
emw.persist(event.getEntity()); // TODO.impl: once we implement retry, we might need merge
}
}
private <T> T ref(final Class<T> entityClass, final UUID uuid) {
return uuid != null ? emw.getReference(entityClass, uuid) : null;
}
@RequiredArgsConstructor
abstract class HostingAssetFactory {
final HsBookingItemRealEntity fromBookingItem;
final HsHostingAssetAutoInsertResource asset;
protected HsHostingAsset create() {
return null;
}
String performSaveProcess() {
final var newHostingAsset = create();
try {
persist(newHostingAsset);
return null;
} catch (final Exception e) {
return e.getMessage();
}
}
protected void persist(final HsHostingAsset newHostingAsset) {
new HostingAssetEntitySaveProcessor(emw, newHostingAsset)
.preprocessEntity()
.validateEntity()
.prepareForSave()
.save()
.validateContext();
}
}
class DomainSetupHostingAssetFactory extends HostingAssetFactory {
public DomainSetupHostingAssetFactory(
final HsBookingItemRealEntity newBookingItemRealEntity,
final HsHostingAssetAutoInsertResource asset) {
super(newBookingItemRealEntity, asset);
}
@Override
protected HsHostingAsset create() {
final var domainSetupAsset = createDomainSetupAsset(getDomainName());
// TODO.legacy: as long as we need to be compatible, we always do all technical domain-setups
final var subHostingAssetResources = getSubHostingAssetResources();
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.
createDomainSubSetupAssetEntity(
domainSetupAsset,
DOMAIN_MBOX_SETUP,
builder -> builder
.assignedToAsset(assignedToUnixUserAsset.getParentAsset())
.identifier(getDomainName() + "|MBOX")
.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())));
return domainSetupAsset;
}
private HsHostingAssetRealEntity createDomainSetupAsset(final String domainName) {
return HsHostingAssetRealEntity.builder()
.bookingItem(fromBookingItem)
.type(HsHostingAssetType.DOMAIN_SETUP)
.identifier(domainName)
.caption(asset.getCaption() != null ? asset.getCaption() : domainName)
.parentAsset(ref(HsHostingAssetRealEntity.class, asset.getParentAssetUuid()))
.alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid()))
.subHostingAssets(
standardMapper.mapList(getSubHostingAssetResources(), HsHostingAssetRealEntity.class)
)
.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 assignedToUnixUserAssetX =
emw.find(HsHostingAssetRealEntity.class, domainHttpSetupAssetResource.getAssignedToAssetUuid());
domainHttpSetupAsset.setAssignedToAsset(assignedToUnixUserAssetX);
return domainHttpSetupAsset;
}
private void createDomainSubSetupAssetEntity(
final HsHostingAssetRealEntity domainSetupAsset,
final HsHostingAssetType subAssetType,
final Function<HsHostingAssetRealEntityBuilder<?, ?>, HsHostingAssetRealEntityBuilder<?, ?>> builderTransformer) {
final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name());
if (getSubHostingAssetResources().stream().noneMatch(ha -> ha.getType() == resourceType)) {
final var subAssetEntity = builderTransformer.apply(
HsHostingAssetRealEntity.builder()
.type(HsHostingAssetType.valueOf(subAssetType.name()))
.parentAsset(domainSetupAsset))
.build();
domainSetupAsset.getSubHostingAssets().add(subAssetEntity);
}
}
private String getDomainName() {
return asset.getIdentifier();
}
private List<HsHostingAssetSubInsertResource> getSubHostingAssetResources() {
return asset.getSubHostingAssets();
}
@Override
protected void persist(final HsHostingAsset newHostingAsset) {
super.persist(newHostingAsset);
newHostingAsset.getSubHostingAssets().forEach(super::persist);
}
}
}

View File

@ -0,0 +1,128 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetSubInsertResource;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetTypeResource;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import jakarta.validation.ValidationException;
import java.net.IDN;
import java.util.List;
import java.util.function.Function;
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;
public class DomainSetupHostingAssetFactory extends HostingAssetFactory {
public DomainSetupHostingAssetFactory(
final EntityManagerWrapper emw,
final HsBookingItemRealEntity newBookingItemRealEntity,
final HsHostingAssetAutoInsertResource asset,
final StandardMapper standardMapper) {
super(emw, newBookingItemRealEntity, asset, standardMapper);
}
@Override
protected HsHostingAsset create() {
final var domainSetupAsset = createDomainSetupAsset(getDomainName());
// TODO.legacy: as long as we need to be compatible, we always do all technical domain-setups
final var subHostingAssetResources = getSubHostingAssetResources();
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.
createDomainSubSetupAssetEntity(
domainSetupAsset,
DOMAIN_MBOX_SETUP,
builder -> builder
.assignedToAsset(assignedToUnixUserAsset.getParentAsset())
.identifier(getDomainName() + "|MBOX")
.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())));
return domainSetupAsset;
}
private HsHostingAssetRealEntity createDomainSetupAsset(final String domainName) {
return HsHostingAssetRealEntity.builder()
.bookingItem(fromBookingItem)
.type(HsHostingAssetType.DOMAIN_SETUP)
.identifier(domainName)
.caption(asset.getCaption() != null ? asset.getCaption() : domainName)
.parentAsset(ref(HsHostingAssetRealEntity.class, asset.getParentAssetUuid()))
.alarmContact(ref(HsOfficeContactRealEntity.class, asset.getAlarmContactUuid()))
.subHostingAssets(
standardMapper.mapList(getSubHostingAssetResources(), HsHostingAssetRealEntity.class)
)
.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 createDomainSubSetupAssetEntity(
final HsHostingAssetRealEntity domainSetupAsset,
final HsHostingAssetType subAssetType,
final Function<HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>, HsHostingAssetRealEntity.HsHostingAssetRealEntityBuilder<?, ?>> builderTransformer) {
final var resourceType = HsHostingAssetTypeResource.valueOf(subAssetType.name());
if (getSubHostingAssetResources().stream().noneMatch(ha -> ha.getType() == resourceType)) {
final var subAssetEntity = builderTransformer.apply(
HsHostingAssetRealEntity.builder()
.type(HsHostingAssetType.valueOf(subAssetType.name()))
.parentAsset(domainSetupAsset))
.build();
domainSetupAsset.getSubHostingAssets().add(subAssetEntity);
}
}
private String getDomainName() {
return asset.getIdentifier();
}
private List<HsHostingAssetSubInsertResource> getSubHostingAssetResources() {
return asset.getSubHostingAssets();
}
@Override
protected void persist(final HsHostingAsset newHostingAsset) {
super.persist(newHostingAsset);
newHostingAsset.getSubHostingAssets().forEach(super::persist);
}
}

View File

@ -0,0 +1,45 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import lombok.RequiredArgsConstructor;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import java.util.UUID;
@RequiredArgsConstructor
abstract class HostingAssetFactory {
final EntityManagerWrapper emw;
final HsBookingItemRealEntity fromBookingItem;
final HsHostingAssetAutoInsertResource asset;
final StandardMapper standardMapper;
protected abstract HsHostingAsset create();
public String performSaveProcess() {
final var newHostingAsset = create();
try {
persist(newHostingAsset);
return null;
} catch (final Exception e) {
return e.getMessage();
}
}
protected void persist(final HsHostingAsset newHostingAsset) {
new HostingAssetEntitySaveProcessor(emw, newHostingAsset)
.preprocessEntity()
.validateEntity()
.prepareForSave()
.save()
.validateContext();
}
protected <T> T ref(final Class<T> entityClass, final UUID uuid) {
return uuid != null ? emw.getReference(entityClass, uuid) : null;
}
}

View File

@ -0,0 +1,40 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsHostingAssetAutoInsertResource;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class HsBookingItemCreatedListener implements ApplicationListener<BookingItemCreatedAppEvent> {
@Autowired
private EntityManagerWrapper emw;
@Autowired
private ObjectMapper jsonMapper;
@Autowired
private StandardMapper standardMapper;
@Override
@SneakyThrows
public void onApplicationEvent(final BookingItemCreatedAppEvent event) {
final var newBookingItemRealEntity = event.getEntity().getBookingItem();
final var asset = jsonMapper.readValue(event.getEntity().getAssetJson(), HsHostingAssetAutoInsertResource.class);
final var factory = switch (newBookingItemRealEntity.getType()) {
case PRIVATE_CLOUD, CLOUD_SERVER, MANAGED_SERVER -> null; // for now, no automatic HostingAsset possible
case MANAGED_WEBSPACE -> null; // TODO.impl: implement ManagedWebspace HostingAsset creation, where possible
case DOMAIN_SETUP -> new DomainSetupHostingAssetFactory(emw, newBookingItemRealEntity, asset, standardMapper);
};
if (factory != null) {
event.getEntity().setStatusMessage(factory.performSaveProcess());
emw.persist(event.getEntity()); // TODO.impl: once we implement retry, we might need merge
}
}
}