hosting-asset-validation-beyond-property-validators #65
@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne;
|
|||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.Transient;
|
import jakarta.persistence.Transient;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -60,8 +61,8 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetche
|
|||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Builder
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@Builder(toBuilder = true)
|
||||||
@Table(name = "hs_booking_item_rv")
|
@Table(name = "hs_booking_item_rv")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@ -92,6 +93,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
|||||||
@JoinColumn(name = "parentitemuuid")
|
@JoinColumn(name = "parentitemuuid")
|
||||||
private HsBookingItemEntity parentItem;
|
private HsBookingItemEntity parentItem;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Column(name = "type")
|
@Column(name = "type")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private HsBookingItemType type;
|
private HsBookingItemType type;
|
||||||
|
@ -29,6 +29,7 @@ import jakarta.persistence.JoinColumn;
|
|||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OneToOne;
|
import jakarta.persistence.OneToOne;
|
||||||
|
import jakarta.persistence.PostLoad;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.Transient;
|
import jakarta.persistence.Transient;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
@ -120,12 +121,20 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject {
|
|||||||
@Transient
|
@Transient
|
||||||
private PatchableMapWrapper<Object> configWrapper;
|
private PatchableMapWrapper<Object> configWrapper;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private boolean isLoaded = false;
|
||||||
|
|
||||||
|
@PostLoad
|
||||||
|
public void markAsLoaded() {
|
||||||
|
this.isLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
public PatchableMapWrapper<Object> getConfig() {
|
public PatchableMapWrapper<Object> getConfig() {
|
||||||
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config );
|
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putConfig(Map<String, Object> newConfg) {
|
public void putConfig(Map<String, Object> newConfig) {
|
||||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg);
|
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
|
|
||||||
|
HsCloudServerHostingAssetValidator() {
|
||||||
|
super(
|
||||||
|
BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER),
|
||||||
|
ParentAsset.mustBeNull(),
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(),
|
||||||
|
NO_EXTRA_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,80 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
||||||
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 net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||||
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
|
public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
|
||||||
|
|
||||||
public HsHostingAssetEntityValidator(final ValidatableProperty<?>... properties) {
|
static final ValidatableProperty<?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?>[0];
|
||||||
|
|
||||||
|
private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.AssignedToAsset assignedToAssetValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.AlarmContact alarmContactValidation;
|
||||||
|
|
||||||
|
HsHostingAssetEntityValidator(
|
||||||
|
@NotNull final BookingItem bookingItemValidation,
|
||||||
|
@NotNull final ParentAsset parentAssetValidation,
|
||||||
|
@NotNull final AssignedToAsset assignedToAssetValidation,
|
||||||
|
@NotNull final AlarmContact alarmContactValidation,
|
||||||
|
final ValidatableProperty<?>... properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
|
this.bookingItemValidation = bookingItemValidation;
|
||||||
|
this.parentAssetValidation = parentAssetValidation;
|
||||||
|
this.assignedToAssetValidation = assignedToAssetValidation;
|
||||||
|
this.alarmContactValidation = alarmContactValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||||
return sequentiallyValidate(
|
return sequentiallyValidate(
|
||||||
() -> validateProperties(assetEntity),
|
() -> validateEntityReferences(assetEntity),
|
||||||
|
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
|
||||||
() -> optionallyValidate(assetEntity.getBookingItem()),
|
() -> optionallyValidate(assetEntity.getBookingItem()),
|
||||||
() -> optionallyValidate(assetEntity.getParentAsset()),
|
() -> optionallyValidate(assetEntity.getParentAsset()),
|
||||||
() -> validateAgainstSubEntities(assetEntity)
|
() -> validateAgainstSubEntities(assetEntity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> validateEntityReferences(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Stream.of(
|
||||||
|
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate),
|
||||||
|
validateProperties(assetEntity))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> validateReferencedEntity(
|
||||||
|
final HsHostingAssetEntity assetEntity,
|
||||||
|
final String referenceFieldName,
|
||||||
|
final BiFunction<HsHostingAssetEntity, String, List<String>> validator) {
|
||||||
|
return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName));
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
|
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
|
||||||
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
||||||
}
|
}
|
||||||
@ -57,6 +102,7 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
|||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO.test: check, if there are any hosting assets which need this validation at all
|
||||||
private String validateMaxTotalValue(
|
private String validateMaxTotalValue(
|
||||||
final HsHostingAssetEntity hostingAsset,
|
final HsHostingAssetEntity hostingAsset,
|
||||||
final ValidatableProperty<?> propDef) {
|
final ValidatableProperty<?> propDef) {
|
||||||
@ -73,4 +119,120 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
|||||||
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var expectedIdentifierPattern = identifierPattern(assetEntity);
|
||||||
|
if (assetEntity.getIdentifier() == null ||
|
||||||
|
!expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) {
|
||||||
|
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Pattern identifierPattern(HsHostingAssetEntity assetEntity);
|
||||||
|
|
||||||
|
static abstract class ReferenceValidator<S, T> {
|
||||||
|
|
||||||
|
private final Policy policy;
|
||||||
|
private final T subEntityType;
|
||||||
|
private final Function<HsHostingAssetEntity, S> subEntityGetter;
|
||||||
|
private final Function<S,T> subEntityTypeGetter;
|
||||||
|
|
||||||
|
public ReferenceValidator(
|
||||||
|
final Policy policy,
|
||||||
|
final T subEntityType,
|
||||||
|
final Function<HsHostingAssetEntity, S> subEntityGetter,
|
||||||
|
final Function<S, T> subEntityTypeGetter) {
|
||||||
|
this.policy = policy;
|
||||||
|
this.subEntityType = subEntityType;
|
||||||
|
this.subEntityGetter = subEntityGetter;
|
||||||
|
this.subEntityTypeGetter = subEntityTypeGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReferenceValidator(
|
||||||
|
final Policy policy,
|
||||||
|
final Function<HsHostingAssetEntity, S> subEntityGetter) {
|
||||||
|
this.policy = policy;
|
||||||
|
this.subEntityType = null;
|
||||||
|
this.subEntityGetter = subEntityGetter;
|
||||||
|
this.subEntityTypeGetter = e -> null;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Policy {
|
||||||
|
OPTIONAL, FORBIDDEN, REQUIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) {
|
||||||
|
|
||||||
|
final var subEntity = subEntityGetter.apply(assetEntity);
|
||||||
|
if (policy == Policy.REQUIRED && subEntity == null) {
|
||||||
|
return List.of(referenceFieldName + "' must not be null but is null");
|
||||||
|
}
|
||||||
|
if (policy == Policy.FORBIDDEN && subEntity != null) {
|
||||||
|
return List.of(referenceFieldName + "' must be null but is set to "+ assetEntity.getBookingItem().toShortString());
|
||||||
|
}
|
||||||
|
final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null;
|
||||||
|
if (subEntityType != null && subItemType != subEntityType) {
|
||||||
|
return List.of(referenceFieldName + "' must be of type " + subEntityType + " but is of type " + subItemType);
|
||||||
|
}
|
||||||
|
return emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BookingItem extends ReferenceValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
|
BookingItem(final Policy policy, final HsBookingItemType bookingItemType) {
|
||||||
|
super(policy, bookingItemType, HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BookingItem mustBeNull() {
|
||||||
|
return new BookingItem(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BookingItem mustBeOfType(final HsBookingItemType hsBookingItemType) {
|
||||||
|
return new BookingItem(Policy.REQUIRED, hsBookingItemType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ParentAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
ParentAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType parentAssetType) {
|
||||||
|
super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeNull() {
|
||||||
|
return new ParentAsset(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeOfType(final HsHostingAssetType hostingAssetType) {
|
||||||
|
return new ParentAsset(Policy.REQUIRED, hostingAssetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeNullOrOfType(final HsHostingAssetType hostingAssetType) {
|
||||||
|
return new ParentAsset(Policy.OPTIONAL, hostingAssetType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AssignedToAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
AssignedToAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType assignedToAssetType) {
|
||||||
|
super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AssignedToAsset mustBeNull() {
|
||||||
|
return new AssignedToAsset(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
|
||||||
|
|
||||||
|
AlarmContact(final ReferenceValidator.Policy policy) {
|
||||||
|
super(policy, HsHostingAssetEntity::getAlarmContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlarmContact isOptional() {
|
||||||
|
return new AlarmContact(Policy.OPTIONAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,11 @@ public class HsHostingAssetEntityValidatorRegistry {
|
|||||||
|
|
||||||
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>();
|
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
register(CLOUD_SERVER, new HsHostingAssetEntityValidator());
|
// HOWTO: add (register) new HsHostingAssetType-specific validators
|
||||||
|
register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator());
|
||||||
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
|
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
|
||||||
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
|
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
|
||||||
register(UNIX_USER, new HsHostingAssetEntityValidator());
|
register(UNIX_USER, new HsUnixUserHostingAssetValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
@ -8,6 +13,11 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
|
|
||||||
public HsManagedServerHostingAssetValidator() {
|
public HsManagedServerHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
|
BookingItem.mustBeOfType(HsBookingItemType.MANAGED_SERVER),
|
||||||
|
ParentAsset.mustBeNull(), // until we introduce a hosting asset for 'HOST'
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
|
|
||||||
// monitoring
|
// monitoring
|
||||||
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
||||||
integerProperty("monit_max_ram_usage").unit("%").min(10).max(100).withDefault(92),
|
integerProperty("monit_max_ram_usage").unit("%").min(10).max(100).withDefault(92),
|
||||||
@ -15,7 +25,6 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
||||||
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
||||||
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
||||||
// stringProperty("monit_alarm_email").unit("GB").optional() TODO.impl: via Contact?
|
|
||||||
|
|
||||||
// other settings
|
// other settings
|
||||||
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
||||||
@ -45,4 +54,9 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
|
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,26 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
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.Collection;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
public HsManagedWebspaceHostingAssetValidator() {
|
public HsManagedWebspaceHostingAssetValidator() {
|
||||||
|
super(BookingItem.mustBeOfType(HsBookingItemType.MANAGED_WEBSPACE),
|
||||||
|
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_SERVER), // the (shared or private) ManagedServer
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
|
NO_EXTRA_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
hsh-michaelhoennig marked this conversation as resolved
|
|||||||
return Stream.of(validateIdentifierPattern(assetEntity), super.validate(assetEntity))
|
final var prefixPattern =
|
||||||
.flatMap(Collection::stream)
|
!assetEntity.isLoaded()
|
||||||
.collect(Collectors.toList());
|
? assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix()
|
||||||
}
|
: "[a-z][a-z0-9][a-z0-9]";
|
||||||
|
return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$");
|
||||||
private static List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var expectedIdentifierPattern = "^" + assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() + "[0-9][0-9]$";
|
|
||||||
if ( !assetEntity.getIdentifier().matches(expectedIdentifierPattern)) {
|
|
||||||
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
|
|
||||||
|
HsUnixUserHostingAssetValidator() {
|
||||||
|
super(BookingItem.mustBeNull(),
|
||||||
|
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // TODO.spec: for quota notifications
|
||||||
|
NO_EXTRA_PROPERTIES); // TODO.spec: yet to be specified
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
|
||||||
|
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9]+$");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
### HsHostingAssetEntity-Validation
|
||||||
|
|
||||||
|
There is just a single `HsHostingAssetEntity` class for all types of hosting assets like Managed-Server, Managed-Webspace, Unix-Users, Databases etc. These are distinguished by `HsHostingAssetType HsHostingAssetEntity.type`.
|
||||||
|
|
||||||
|
For each of these types, a distinct validator has to be
|
||||||
|
implemented as a subclass of `HsHostingAssetEntityValidator` which needs to be registered (see `HsHostingAssetEntityValidatorRegistry`) for the relevant type(s).
|
||||||
|
|
||||||
|
### Kinds of Validations
|
||||||
|
|
||||||
|
#### Identifier validation
|
||||||
|
|
||||||
|
The identifier of a Hosting-Asset is for example the Webspace-Name like "xyz00" or a Unix-User-Name like "xyz00-test".
|
||||||
|
|
||||||
|
To validate the identifier, vverride the method `identifierPattern(...)` and return a regular expression to validate the identifier against. The regular expression can depend on the actual entity instance.
|
||||||
|
|
||||||
|
#### Reference validation
|
||||||
|
|
||||||
|
References in this context are:
|
||||||
|
- the related Booking-Item,
|
||||||
|
- the parent-Hosting-Asset,
|
||||||
|
- the Assigned-To-Hosting-Asset and
|
||||||
|
- the Contact.
|
||||||
|
|
||||||
|
The first parameters of the `HsHostingAssetEntityValidator` superclass take rule descriptors for these references. These are all Subclasses fo
|
||||||
|
|
||||||
|
### Validation Order
|
||||||
|
|
||||||
|
The validations are called in a sensible order. E.g. if a property value is not numeric, it makes no sense to check the total sum of such values to be within certain numeric values. And if the related booking item is of wrong type, it makes no sense to validate limits against sub-entities.
|
||||||
|
|
||||||
|
Properties are validated all at once, though. Thus, if multiple properties fail validation, all error messages are returned at once.
|
||||||
|
|
||||||
|
In general, the validation es executed in this order:
|
||||||
|
|
||||||
|
1. the entity itself
|
||||||
|
1. its references
|
||||||
|
2. its properties
|
||||||
|
2. the limits of the parent entity (parent asset + booking item)
|
||||||
|
3. limits against the own own-sub-entities
|
||||||
|
|
||||||
|
This implementation can be found in `HsHostingAssetEntityValidator.validate`.
|
@ -12,13 +12,21 @@ import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class TestHsBookingItem {
|
public class TestHsBookingItem {
|
||||||
|
|
||||||
public static final HsBookingItemEntity TEST_BOOKING_ITEM = HsBookingItemEntity.builder()
|
public static final HsBookingItemEntity TEST_MANAGED_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
.project(TEST_PROJECT)
|
.project(TEST_PROJECT)
|
||||||
.caption("test booking item")
|
.type(HsBookingItemType.MANAGED_SERVER)
|
||||||
|
.caption("test project booking item")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("someThing", 1),
|
entry("someThing", 1),
|
||||||
entry("anotherThing", "blue")
|
entry("anotherThing", "blue")
|
||||||
))
|
))
|
||||||
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static final HsBookingItemEntity TEST_CLOUD_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
|
.project(TEST_PROJECT)
|
||||||
|
.type(HsBookingItemType.CLOUD_SERVER)
|
||||||
|
.caption("test cloud server booking item")
|
||||||
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -220,9 +221,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
void parentAssetAgent_canAddSubAsset() {
|
void parentAssetAgent_canAddSubAsset() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenParentAsset = givenParentAsset(MANAGED_SERVER, "vm1011");
|
final var givenParentAsset = givenParentAsset(MANAGED_WEBSPACE, "fir01");
|
||||||
|
|
||||||
context.define("person-FirbySusan@example.com");
|
|
||||||
|
|
||||||
final var location = RestAssured // @formatter:off
|
final var location = RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -232,9 +231,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"parentAssetUuid": "%s",
|
"parentAssetUuid": "%s",
|
||||||
"type": "MANAGED_WEBSPACE",
|
"type": "UNIX_USER",
|
||||||
"identifier": "fir90",
|
"identifier": "fir01-temp",
|
||||||
"caption": "some new ManagedWebspace in client's ManagedServer",
|
"caption": "some new UnixUser in client's ManagedWebspace",
|
||||||
"config": {}
|
"config": {}
|
||||||
}
|
}
|
||||||
""".formatted(givenParentAsset.getUuid()))
|
""".formatted(givenParentAsset.getUuid()))
|
||||||
@ -246,9 +245,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
{
|
{
|
||||||
"type": "MANAGED_WEBSPACE",
|
"type": "UNIX_USER",
|
||||||
"identifier": "fir90",
|
"identifier": "fir01-temp",
|
||||||
"caption": "some new ManagedWebspace in client's ManagedServer",
|
"caption": "some new UnixUser in client's ManagedWebspace",
|
||||||
"config": {}
|
"config": {}
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
@ -265,7 +264,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
void propertyValidationsArePerformend_whenAddingAsset() {
|
void propertyValidationsArePerformend_whenAddingAsset() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItem = givenBookingItem("D-1000111 default project", "some PrivateCloud");
|
final var givenBookingItem = givenSomeNewBookingItem("D-1000111 default project",
|
||||||
|
HsBookingItemType.MANAGED_SERVER,
|
||||||
|
"some PrivateCloud");
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -558,10 +559,22 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
}).assertSuccessful().returnedValue();
|
}).assertSuccessful().returnedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
HsBookingItemEntity givenBookingItem(final String projectCaption, final String bookingItemCaption) {
|
HsBookingItemEntity givenSomeNewBookingItem(final String projectCaption, final HsBookingItemType bookingItemType, final String bookingItemCaption) {
|
||||||
return bookingItemRepo.findByCaption(bookingItemCaption).stream()
|
return jpaAttempt.transacted(() -> {
|
||||||
.filter(bi -> bi.getRelatedProject().getCaption().contains(projectCaption))
|
context.define("superuser-alex@hostsharing.net");
|
||||||
.findAny().orElseThrow();
|
final var project = projectRepo.findByCaption(projectCaption).getFirst();
|
||||||
|
final var resources = switch (bookingItemType) {
|
||||||
|
case MANAGED_SERVER -> Map.<String, Object>ofEntries(entry("CPUs", 1), entry("RAM", 20), entry("SSD", 25), entry("Traffic", 250));
|
||||||
|
default -> new HashMap<String, Object>();
|
||||||
|
};
|
||||||
|
final var newBookingItem = HsBookingItemEntity.builder()
|
||||||
|
.project(project)
|
||||||
|
.type(bookingItemType)
|
||||||
|
.caption(bookingItemCaption)
|
||||||
|
.resources(resources)
|
||||||
|
.build();
|
||||||
|
return toCleanup(bookingItemRepo.save(newBookingItem));
|
||||||
|
}).assertSuccessful().returnedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
HsHostingAssetEntity givenParentAsset(final HsHostingAssetType assetType, final String assetIdentifier) {
|
HsHostingAssetEntity givenParentAsset(final HsHostingAssetType assetType, final String assetIdentifier) {
|
||||||
@ -574,16 +587,23 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
private HsHostingAssetEntity givenSomeTemporaryHostingAsset(final String identifierSuffix,
|
private HsHostingAssetEntity givenSomeTemporaryHostingAsset(final String identifierSuffix,
|
||||||
final HsHostingAssetType hostingAssetType,
|
final HsHostingAssetType hostingAssetType,
|
||||||
final Map.Entry<String, Object>... resources) {
|
final Map.Entry<String, Object>... config) {
|
||||||
return jpaAttempt.transacted(() -> {
|
return jpaAttempt.transacted(() -> {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
|
final var bookingItemType = switch (hostingAssetType) {
|
||||||
|
case CLOUD_SERVER -> HsBookingItemType.CLOUD_SERVER;
|
||||||
|
case MANAGED_SERVER -> HsBookingItemType.MANAGED_SERVER;
|
||||||
|
case MANAGED_WEBSPACE -> HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
final var newBookingItem = givenSomeNewBookingItem("D-1000111 default project", bookingItemType, "temp ManagedServer");
|
||||||
final var newAsset = HsHostingAssetEntity.builder()
|
final var newAsset = HsHostingAssetEntity.builder()
|
||||||
.uuid(UUID.randomUUID())
|
.uuid(UUID.randomUUID())
|
||||||
.bookingItem(givenBookingItem("D-1000111 default project", "some ManagedServer"))
|
.bookingItem(newBookingItem)
|
||||||
.type(hostingAssetType)
|
.type(hostingAssetType)
|
||||||
.identifier("vm" + identifierSuffix)
|
.identifier("vm" + identifierSuffix)
|
||||||
.caption("some test-asset")
|
.caption("some test-asset")
|
||||||
.config(Map.ofEntries(resources))
|
.config(Map.ofEntries(config))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return assetRepo.save(newAsset);
|
return assetRepo.save(newAsset);
|
||||||
|
@ -15,7 +15,7 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.patchMap;
|
import static net.hostsharing.hsadminng.mapper.PatchMap.patchMap;
|
||||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||||
@ -70,7 +70,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
protected HsHostingAssetEntity newInitialEntity() {
|
protected HsHostingAssetEntity newInitialEntity() {
|
||||||
final var entity = new HsHostingAssetEntity();
|
final var entity = new HsHostingAssetEntity();
|
||||||
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
||||||
entity.setBookingItem(TEST_BOOKING_ITEM);
|
entity.setBookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM);
|
||||||
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
|
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
|
||||||
entity.setCaption(INITIAL_CAPTION);
|
entity.setCaption(INITIAL_CAPTION);
|
||||||
entity.setAlarmContact(givenInitialContact);
|
entity.setAlarmContact(givenInitialContact);
|
||||||
|
@ -5,13 +5,13 @@ import org.junit.jupiter.api.Test;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class HsHostingAssetEntityUnitTest {
|
class HsHostingAssetEntityUnitTest {
|
||||||
|
|
||||||
final HsHostingAssetEntity givenParentAsset = HsHostingAssetEntity.builder()
|
final HsHostingAssetEntity givenParentAsset = HsHostingAssetEntity.builder()
|
||||||
.bookingItem(TEST_BOOKING_ITEM)
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
.type(HsHostingAssetType.MANAGED_SERVER)
|
.type(HsHostingAssetType.MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
.caption("some managed asset")
|
.caption("some managed asset")
|
||||||
@ -21,7 +21,7 @@ class HsHostingAssetEntityUnitTest {
|
|||||||
entry("HDD-storage", 2048)))
|
entry("HDD-storage", 2048)))
|
||||||
.build();
|
.build();
|
||||||
final HsHostingAssetEntity givenWebspace = HsHostingAssetEntity.builder()
|
final HsHostingAssetEntity givenWebspace = HsHostingAssetEntity.builder()
|
||||||
.bookingItem(TEST_BOOKING_ITEM)
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
.type(HsHostingAssetType.MANAGED_WEBSPACE)
|
.type(HsHostingAssetType.MANAGED_WEBSPACE)
|
||||||
.parentAsset(givenParentAsset)
|
.parentAsset(givenParentAsset)
|
||||||
.identifier("xyz00")
|
.identifier("xyz00")
|
||||||
@ -58,7 +58,7 @@ class HsHostingAssetEntityUnitTest {
|
|||||||
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
||||||
|
|
||||||
assertThat(givenWebspace.toString()).isEqualTo(
|
assertThat(givenWebspace.toString()).isEqualTo(
|
||||||
"HsHostingAssetEntity(MANAGED_WEBSPACE, xyz00, some managed webspace, MANAGED_SERVER:vm1234, D-1234500:test project:test booking item, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })");
|
"HsHostingAssetEntity(MANAGED_WEBSPACE, xyz00, some managed webspace, MANAGED_SERVER:vm1234, D-1234500:test project:test cloud server booking item, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })");
|
||||||
|
|
||||||
assertThat(givenUnixUser.toString()).isEqualTo(
|
assertThat(givenUnixUser.toString()).isEqualTo(
|
||||||
"HsHostingAssetEntity(UNIX_USER, xyz00-web, some unix-user, MANAGED_WEBSPACE:xyz00, { HDD-hard-quota: 512, HDD-soft-quota: 256, SSD-hard-quota: 256, SSD-soft-quota: 128 })");
|
"HsHostingAssetEntity(UNIX_USER, xyz00-web, some unix-user, MANAGED_WEBSPACE:xyz00, { HDD-hard-quota: 512, HDD-soft-quota: 256, SSD-hard-quota: 256, SSD-soft-quota: 128 })");
|
||||||
|
@ -90,6 +90,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
|||||||
result.assertSuccessful();
|
result.assertSuccessful();
|
||||||
assertThat(result.returnedValue()).isNotNull().extracting(HsHostingAssetEntity::getUuid).isNotNull();
|
assertThat(result.returnedValue()).isNotNull().extracting(HsHostingAssetEntity::getUuid).isNotNull();
|
||||||
assertThatAssetIsPersisted(result.returnedValue());
|
assertThatAssetIsPersisted(result.returnedValue());
|
||||||
|
assertThat(result.returnedValue().isLoaded()).isFalse();
|
||||||
assertThat(assetRepo.count()).isEqualTo(count + 1);
|
assertThat(assetRepo.count()).isEqualTo(count + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,5 +414,6 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
|||||||
assertThat(actualResult)
|
assertThat(actualResult)
|
||||||
.extracting(HsHostingAssetEntity::toString)
|
.extracting(HsHostingAssetEntity::toString)
|
||||||
.contains(serverNames);
|
.contains(serverNames);
|
||||||
|
actualResult.forEach(loadedEntity -> assertThat(loadedEntity.isLoaded()).isTrue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
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.MANAGED_SERVER;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class HsCloudServerHostingAssetValidatorUnitTest {
|
class HsCloudServerHostingAssetValidatorUnitTest {
|
||||||
@ -28,7 +32,28 @@ class HsCloudServerHostingAssetValidatorUnitTest {
|
|||||||
final var result = validator.validate(cloudServerHostingAssetEntity);
|
final var result = validator.validate(cloudServerHostingAssetEntity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).containsExactly("'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'CLOUD_SERVER:vm1234.bookingItem' must not be null but is null",
|
||||||
|
"'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var cloudServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.identifier("xyz99")
|
||||||
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(cloudServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(cloudServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz99'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -39,4 +64,43 @@ class HsCloudServerHostingAssetValidatorUnitTest {
|
|||||||
// then
|
// then
|
||||||
assertThat(validator.properties()).map(Map::toString).isEmpty();
|
assertThat(validator.properties()).map(Map::toString).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesBookingItemType() {
|
||||||
|
// given
|
||||||
|
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'MANAGED_SERVER:xyz00.bookingItem' must be of type MANAGED_SERVER but is of type CLOUD_SERVER");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesParentAndAssignedToAssetMustNotBeSet() {
|
||||||
|
// given
|
||||||
|
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.assignedToAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'CLOUD_SERVER:xyz00.parentAsset' must be null but is set to D-???????-?:null",
|
||||||
|
"'CLOUD_SERVER:xyz00.assignedToAsset' must be null but is set to D-???????-?:null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
|
import static org.assertj.core.api.Assertions.entry;
|
||||||
|
|
||||||
|
class HsHostingAssetEntityValidatorRegistryUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void forTypeWithUnknownTypeThrowsException() {
|
||||||
|
// when
|
||||||
|
final var thrown = catchThrowable(() -> {
|
||||||
|
HsHostingAssetEntityValidatorRegistry.forType(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(thrown).hasMessage("no validator found for type null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void typesReturnsAllImplementedTypes() {
|
||||||
|
// when
|
||||||
|
final var types = HsHostingAssetEntityValidatorRegistry.types();
|
||||||
|
|
||||||
|
// then
|
||||||
|
// TODO.test: when all types are implemented, replace with set of all types:
|
||||||
|
// assertThat(types).isEqualTo(EnumSet.allOf(HsHostingAssetType.class));
|
||||||
|
// also remove "Implemented" from the test method name.
|
||||||
|
assertThat(types).containsExactlyInAnyOrder(
|
||||||
|
HsHostingAssetType.CLOUD_SERVER,
|
||||||
|
HsHostingAssetType.MANAGED_SERVER,
|
||||||
|
HsHostingAssetType.MANAGED_WEBSPACE,
|
||||||
|
HsHostingAssetType.UNIX_USER
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatedDoesNotThrowAnExceptionForValidEntity() {
|
||||||
|
final var givenBookingItem = HsBookingItemEntity.builder()
|
||||||
|
.type(HsBookingItemType.CLOUD_SERVER)
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("CPUs", 4),
|
||||||
|
entry("RAM", 20),
|
||||||
|
entry("SSD", 50),
|
||||||
|
entry("Traffic", 250)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
final var validEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(HsHostingAssetType.CLOUD_SERVER)
|
||||||
|
.bookingItem(givenBookingItem)
|
||||||
|
.identifier("vm1234")
|
||||||
|
.caption("some valid cloud server")
|
||||||
|
.build();
|
||||||
|
HsHostingAssetEntityValidatorRegistry.validated(validEntity);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -16,12 +18,18 @@ class HsHostingAssetEntityValidatorUnitTest {
|
|||||||
final var managedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var managedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_SERVER)
|
.type(MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build())
|
||||||
|
.assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = catchThrowable( ()-> HsHostingAssetEntityValidatorRegistry.validated(managedServerHostingAssetEntity));
|
final var result = catchThrowable( ()-> HsHostingAssetEntityValidatorRegistry.validated(managedServerHostingAssetEntity));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).isNull(); // all required properties have defaults
|
assertThat(result.getMessage()).contains(
|
||||||
|
"'MANAGED_SERVER:vm1234.parentAsset' must be null but is set to D-???????-?:null",
|
||||||
|
"'MANAGED_SERVER:vm1234.assignedToAsset' must be null but is set to D-???????-?:null"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -17,6 +19,9 @@ class HsManagedServerHostingAssetValidatorUnitTest {
|
|||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_SERVER)
|
.type(MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.assignedToAsset(HsHostingAssetEntity.builder().build())
|
||||||
.config(Map.ofEntries(
|
.config(Map.ofEntries(
|
||||||
entry("monit_max_hdd_usage", "90"),
|
entry("monit_max_hdd_usage", "90"),
|
||||||
entry("monit_max_cpu_usage", 2),
|
entry("monit_max_cpu_usage", 2),
|
||||||
@ -30,8 +35,50 @@ class HsManagedServerHostingAssetValidatorUnitTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'MANAGED_SERVER:vm1234.parentAsset' must be null but is set to D-???????-?:null",
|
||||||
|
"'MANAGED_SERVER:vm1234.assignedToAsset' must be null but is set to D-???????-?:null",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be >= 10 but is 2",
|
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be >= 10 but is 2",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be <= 100 but is 101",
|
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be <= 100 but is 101",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
"'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz00'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesParentAndAssignedToAssetMustNotBeSet() {
|
||||||
|
// given
|
||||||
|
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.assignedToAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'MANAGED_SERVER:xyz00.bookingItem' must be of type MANAGED_SERVER but is of type CLOUD_SERVER",
|
||||||
|
"'MANAGED_SERVER:xyz00.parentAsset' must be null but is set to D-???????-?:null",
|
||||||
|
"'MANAGED_SERVER:xyz00.assignedToAsset' must be null but is set to D-???????-?:null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,11 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
entry("SLA-EMail", true)
|
entry("SLA-EMail", true)
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
final HsBookingItemEntity cloudServerBookingItem = managedServerBookingItem.toBuilder()
|
||||||
|
.type(HsBookingItemType.CLOUD_SERVER)
|
||||||
|
.caption("Test Cloud-Server")
|
||||||
|
.build();
|
||||||
|
|
||||||
final HsHostingAssetEntity mangedServerAssetEntity = HsHostingAssetEntity.builder()
|
final HsHostingAssetEntity mangedServerAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(HsHostingAssetType.MANAGED_SERVER)
|
.type(HsHostingAssetType.MANAGED_SERVER)
|
||||||
.bookingItem(managedServerBookingItem)
|
.bookingItem(managedServerBookingItem)
|
||||||
@ -38,13 +43,46 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
entry("monit_max_ram_usage", 90)
|
entry("monit_max_ram_usage", 90)
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
final HsHostingAssetEntity cloudServerAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(HsHostingAssetType.CLOUD_SERVER)
|
||||||
|
.bookingItem(cloudServerBookingItem)
|
||||||
|
.identifier("vm1234")
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("monit_max_ssd_usage", 70),
|
||||||
|
entry("monit_max_cpu_usage", 80),
|
||||||
|
entry("monit_max_ram_usage", 90)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validatesIdentifier() {
|
void acceptsAlienIdentifierPrefixForPreExistingEntity() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder()
|
||||||
|
.type(HsBookingItemType.MANAGED_WEBSPACE)
|
||||||
|
.resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250)))
|
||||||
|
.build())
|
||||||
|
.parentAsset(mangedServerAssetEntity)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.isLoaded(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesIdentifierAndReferencedEntities() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("xyz00")
|
.identifier("xyz00")
|
||||||
.build();
|
.build();
|
||||||
@ -62,6 +100,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("abc00")
|
.identifier("abc00")
|
||||||
.config(Map.ofEntries(
|
.config(Map.ofEntries(
|
||||||
@ -82,6 +121,11 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder()
|
||||||
|
.type(HsBookingItemType.MANAGED_WEBSPACE)
|
||||||
|
.caption("some ManagedWebspace")
|
||||||
|
.resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250)))
|
||||||
|
.build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("abc00")
|
.identifier("abc00")
|
||||||
.build();
|
.build();
|
||||||
@ -92,4 +136,30 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
// then
|
// then
|
||||||
assertThat(result).isEmpty();
|
assertThat(result).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesEntityReferences() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder()
|
||||||
|
.type(HsBookingItemType.MANAGED_SERVER)
|
||||||
|
.caption("some ManagedServer")
|
||||||
|
.resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250)))
|
||||||
|
.build())
|
||||||
|
.parentAsset(cloudServerAssetEntity)
|
||||||
|
.assignedToAsset(HsHostingAssetEntity.builder().build())
|
||||||
|
.identifier("abc00")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly(
|
||||||
|
"'MANAGED_WEBSPACE:abc00.bookingItem' must be of type MANAGED_WEBSPACE but is of type MANAGED_SERVER",
|
||||||
|
"'MANAGED_WEBSPACE:abc00.parentAsset' must be of type MANAGED_SERVER but is of type CLOUD_SERVER",
|
||||||
|
"'MANAGED_WEBSPACE:abc00.assignedToAsset' must be null but is set to D-???????-?:some ManagedServer");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsUnixUserHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var unixUserHostingAsset = HsHostingAssetEntity.builder()
|
||||||
|
.type(UNIX_USER)
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).identifier("abc00").build())
|
||||||
|
.identifier("xyz99-temp")
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(unixUserHostingAsset);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly(
|
||||||
|
"'identifier' expected to match '^abc00$|^abc00-[a-z0-9]+$', but is 'xyz99-temp'");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user
nur bei neu angelegten Objekten prüfen