hosting-asset-validation-beyond-property-validators (#65)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/65 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Transient;
|
||||
import jakarta.persistence.Version;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
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.stringify.Stringify.stringify;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@Builder(toBuilder = true)
|
||||
@Table(name = "hs_booking_item_rv")
|
||||
@Getter
|
||||
@Setter
|
||||
@ -92,6 +93,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
@JoinColumn(name = "parentitemuuid")
|
||||
private HsBookingItemEntity parentItem;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private HsBookingItemType type;
|
||||
|
@ -29,6 +29,7 @@ import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Transient;
|
||||
import jakarta.persistence.Version;
|
||||
@ -120,12 +121,20 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject {
|
||||
@Transient
|
||||
private PatchableMapWrapper<Object> configWrapper;
|
||||
|
||||
@Transient
|
||||
private boolean isLoaded = false;
|
||||
|
||||
@PostLoad
|
||||
public void markAsLoaded() {
|
||||
this.isLoaded = true;
|
||||
}
|
||||
|
||||
public PatchableMapWrapper<Object> getConfig() {
|
||||
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config );
|
||||
}
|
||||
|
||||
public void putConfig(Map<String, Object> newConfg) {
|
||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg);
|
||||
public void putConfig(Map<String, Object> newConfig) {
|
||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfig);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
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.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.ValidatableProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.Collections.emptyList;
|
||||
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);
|
||||
this.bookingItemValidation = bookingItemValidation;
|
||||
this.parentAssetValidation = parentAssetValidation;
|
||||
this.assignedToAssetValidation = assignedToAssetValidation;
|
||||
this.alarmContactValidation = alarmContactValidation;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||
return sequentiallyValidate(
|
||||
() -> validateProperties(assetEntity),
|
||||
() -> validateEntityReferences(assetEntity),
|
||||
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
|
||||
() -> optionallyValidate(assetEntity.getBookingItem()),
|
||||
() -> optionallyValidate(assetEntity.getParentAsset()),
|
||||
() -> 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) {
|
||||
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
||||
}
|
||||
@ -57,6 +102,7 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
||||
.toList());
|
||||
}
|
||||
|
||||
// TODO.test: check, if there are any hosting assets which need this validation at all
|
||||
private String validateMaxTotalValue(
|
||||
final HsHostingAssetEntity hostingAsset,
|
||||
final ValidatableProperty<?> propDef) {
|
||||
@ -73,4 +119,120 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
||||
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||
: 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<>();
|
||||
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_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) {
|
||||
|
@ -1,5 +1,10 @@
|
||||
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.EnumerationProperty.enumerationProperty;
|
||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||
@ -8,6 +13,11 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
||||
|
||||
public HsManagedServerHostingAssetValidator() {
|
||||
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
|
||||
integerProperty("monit_max_cpu_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_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
||||
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
||||
// stringProperty("monit_alarm_email").unit("GB").optional() TODO.impl: via Contact?
|
||||
|
||||
// other settings
|
||||
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
||||
@ -45,4 +54,9 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
||||
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;
|
||||
|
||||
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 java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
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
|
||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||
return Stream.of(validateIdentifierPattern(assetEntity), super.validate(assetEntity))
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
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();
|
||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||
final var prefixPattern =
|
||||
!assetEntity.isLoaded()
|
||||
? assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix()
|
||||
: "[a-z][a-z0-9][a-z0-9]";
|
||||
return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$");
|
||||
}
|
||||
}
|
||||
|
@ -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`.
|
Reference in New Issue
Block a user