hosting-asset-validation-baseline (#56)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #56 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
parent
2e9e5d6ef0
commit
23a6f89943
@ -17,6 +17,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.valid;
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -56,7 +57,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
|
|
||||||
final var entityToSave = mapper.map(body, HsBookingItemEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
final var entityToSave = mapper.map(body, HsBookingItemEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||||
|
|
||||||
final var saved = bookingItemRepo.save(entityToSave);
|
final var saved = bookingItemRepo.save(valid(entityToSave));
|
||||||
|
|
||||||
final var uri =
|
final var uri =
|
||||||
MvcUriComponentsBuilder.fromController(getClass())
|
MvcUriComponentsBuilder.fromController(getClass())
|
||||||
@ -111,7 +112,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
|||||||
|
|
||||||
new HsBookingItemEntityPatcher(current).apply(body);
|
new HsBookingItemEntityPatcher(current).apply(body);
|
||||||
|
|
||||||
final var saved = bookingItemRepo.save(current);
|
final var saved = bookingItemRepo.save(valid(current));
|
||||||
final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var mapped = mapper.map(saved, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.Validatable;
|
||||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||||
@ -65,7 +66,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
|||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
public class HsBookingItemEntity implements Stringifyable, RbacObject, Validatable<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
private static Stringify<HsBookingItemEntity> stringify = stringify(HsBookingItemEntity.class)
|
private static Stringify<HsBookingItemEntity> stringify = stringify(HsBookingItemEntity.class)
|
||||||
.withProp(HsBookingItemEntity::getDebitor)
|
.withProp(HsBookingItemEntity::getDebitor)
|
||||||
@ -142,6 +143,16 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
|||||||
":" + caption;
|
":" + caption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPropertiesName() {
|
||||||
|
return "resources";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getProperties() {
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("bookingItem", HsBookingItemEntity.class)
|
return rbacViewFor("bookingItem", HsBookingItemEntity.class)
|
||||||
.withIdentityView(SQL.query("""
|
.withIdentityView(SQL.query("""
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class HsBookingItemEntityValidators {
|
||||||
|
|
||||||
|
private static final Map<Enum<HsBookingItemType>, HsEntityValidator<HsBookingItemEntity, HsBookingItemType>> validators = new HashMap<>();
|
||||||
|
static {
|
||||||
|
register(CLOUD_SERVER, new HsCloudServerBookingItemValidator());
|
||||||
|
register(MANAGED_SERVER, new HsManagedServerBookingItemValidator());
|
||||||
|
register(MANAGED_WEBSPACE, new HsManagedWebspaceBookingItemValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void register(final Enum<HsBookingItemType> type, final HsEntityValidator<HsBookingItemEntity, HsBookingItemType> validator) {
|
||||||
|
stream(validator.propertyValidators).forEach( entry -> {
|
||||||
|
entry.verifyConsistency(Map.entry(type, validator));
|
||||||
|
});
|
||||||
|
validators.put(type, validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HsEntityValidator<HsBookingItemEntity, HsBookingItemType> forType(final Enum<HsBookingItemType> type) {
|
||||||
|
return validators.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Enum<HsBookingItemType>> types() {
|
||||||
|
return validators.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HsBookingItemEntity valid(final HsBookingItemEntity entityToSave) {
|
||||||
|
final var violations = HsBookingItemEntityValidators.forType(entityToSave.getType()).validate(entityToSave);
|
||||||
|
if (!violations.isEmpty()) {
|
||||||
|
throw new ValidationException(violations.toString());
|
||||||
|
}
|
||||||
|
return entityToSave;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsCloudServerBookingItemValidator extends HsEntityValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
|
HsCloudServerBookingItemValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("CPUs").min(1).max(32).required(),
|
||||||
|
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
||||||
|
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(),
|
||||||
|
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.BooleanPropertyValidator.booleanProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsManagedServerBookingItemValidator extends HsEntityValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
|
HsManagedServerBookingItemValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("CPUs").min(1).max(32).required(),
|
||||||
|
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
||||||
|
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(),
|
||||||
|
enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional(),
|
||||||
|
booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").optional(),
|
||||||
|
booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(),
|
||||||
|
booleanProperty("SLA-PgSQL").falseIf("SLA-Platform", "BASIC").optional(),
|
||||||
|
booleanProperty("SLA-Office").falseIf("SLA-Platform", "BASIC").optional(),
|
||||||
|
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.BooleanPropertyValidator.booleanProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationPropertyValidator.enumerationProperty;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsManagedWebspaceBookingItemValidator extends HsEntityValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
|
public HsManagedWebspaceBookingItemValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(),
|
||||||
|
enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional(),
|
||||||
|
integerProperty("Daemons").min(0).max(10).optional(),
|
||||||
|
booleanProperty("Online Office Server").optional()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
@ -16,11 +15,12 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||||
|
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.valid;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class HsHostingAssetController implements HsHostingAssetsApi {
|
public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||||
@ -117,21 +117,17 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
|
|
||||||
new HsHostingAssetEntityPatcher(current).apply(body);
|
new HsHostingAssetEntityPatcher(current).apply(body);
|
||||||
|
|
||||||
final var saved = assetRepo.save(current);
|
final var saved = assetRepo.save(valid(current));
|
||||||
final var mapped = mapper.map(saved, HsHostingAssetResource.class);
|
final var mapped = mapper.map(saved, HsHostingAssetResource.class);
|
||||||
return ResponseEntity.ok(mapped);
|
return ResponseEntity.ok(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) {
|
|
||||||
final var violations = HsHostingAssetValidator.forType(entityToSave.getType()).validate(entityToSave);
|
|
||||||
if (!violations.isEmpty()) {
|
|
||||||
throw new ValidationException(violations.toString());
|
|
||||||
}
|
|
||||||
return entityToSave;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final BiConsumer<HsHostingAssetInsertResource, HsHostingAssetEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
final BiConsumer<HsHostingAssetInsertResource, HsHostingAssetEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||||
entity.putConfig(KeyValueMap.from(resource.getConfig()));
|
entity.putConfig(KeyValueMap.from(resource.getConfig()));
|
||||||
|
if (resource.getParentAssetUuid() != null) {
|
||||||
|
entity.setParentAsset(assetRepo.findByUuid(resource.getParentAssetUuid())
|
||||||
|
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] parentAssetUuid %s not found".formatted(
|
||||||
|
resource.getParentAssetUuid()))));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.Validatable;
|
||||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
||||||
@ -40,7 +41,6 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inOtherCas
|
|||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
||||||
@ -61,7 +61,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
|||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class HsHostingAssetEntity implements Stringifyable, RbacObject {
|
public class HsHostingAssetEntity implements Stringifyable, RbacObject, Validatable<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
private static Stringify<HsHostingAssetEntity> stringify = stringify(HsHostingAssetEntity.class)
|
private static Stringify<HsHostingAssetEntity> stringify = stringify(HsHostingAssetEntity.class)
|
||||||
.withProp(HsHostingAssetEntity::getType)
|
.withProp(HsHostingAssetEntity::getType)
|
||||||
@ -114,6 +114,16 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject {
|
|||||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg);
|
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPropertiesName() {
|
||||||
|
return "config";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getProperties() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return stringify.apply(this);
|
return stringify.apply(this);
|
||||||
@ -137,7 +147,7 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject {
|
|||||||
.importEntityAlias("bookingItem", HsBookingItemEntity.class, usingDefaultCase(),
|
.importEntityAlias("bookingItem", HsBookingItemEntity.class, usingDefaultCase(),
|
||||||
dependsOnColumn("bookingItemUuid"),
|
dependsOnColumn("bookingItemUuid"),
|
||||||
directlyFetchedByDependsOnColumn(),
|
directlyFetchedByDependsOnColumn(),
|
||||||
NOT_NULL)
|
NULLABLE)
|
||||||
|
|
||||||
.switchOnColumn("type",
|
.switchOnColumn("type",
|
||||||
inCaseOf(CLOUD_SERVER.name(),
|
inCaseOf(CLOUD_SERVER.name(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator;
|
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -15,7 +15,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<List<String>> listAssetTypes() {
|
public ResponseEntity<List<String>> listAssetTypes() {
|
||||||
final var resource = HsHostingAssetValidator.types().stream()
|
final var resource = HsHostingAssetEntityValidators.types().stream()
|
||||||
.map(Enum::name)
|
.map(Enum::name)
|
||||||
.toList();
|
.toList();
|
||||||
return ResponseEntity.ok(resource);
|
return ResponseEntity.ok(resource);
|
||||||
@ -25,7 +25,7 @@ public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
|
|||||||
public ResponseEntity<List<Object>> listAssetTypeProps(
|
public ResponseEntity<List<Object>> listAssetTypeProps(
|
||||||
final HsHostingAssetTypeResource assetType) {
|
final HsHostingAssetTypeResource assetType) {
|
||||||
|
|
||||||
final var propValidators = HsHostingAssetValidator.forType(HsHostingAssetType.of(assetType));
|
final var propValidators = HsHostingAssetEntityValidators.forType(HsHostingAssetType.of(assetType));
|
||||||
final List<Map<String, Object>> resource = propValidators.properties();
|
final List<Map<String, Object>> resource = propValidators.properties();
|
||||||
return ResponseEntity.ok(toListOfObjects(resource));
|
return ResponseEntity.ok(toListOfObjects(resource));
|
||||||
}
|
}
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validator;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
|
||||||
|
|
||||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public abstract class HsHostingAssetPropertyValidator<T> {
|
|
||||||
|
|
||||||
final Class<T> type;
|
|
||||||
final String propertyName;
|
|
||||||
private Boolean required;
|
|
||||||
|
|
||||||
public static <K, V> Map.Entry<K, V> defType(K k, V v) {
|
|
||||||
return new SimpleImmutableEntry<>(k, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HsHostingAssetPropertyValidator<T> required() {
|
|
||||||
required = Boolean.TRUE;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HsHostingAssetPropertyValidator<T> optional() {
|
|
||||||
required = Boolean.FALSE;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<String> validate(final Map<String, Object> props) {
|
|
||||||
final var result = new ArrayList<String>();
|
|
||||||
final var propValue = props.get(propertyName);
|
|
||||||
if (propValue == null) {
|
|
||||||
if (required) {
|
|
||||||
result.add("'" + propertyName + "' is required but missing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (propValue != null){
|
|
||||||
if ( type.isInstance(propValue)) {
|
|
||||||
//noinspection unchecked
|
|
||||||
validate(result, (T) propValue, props);
|
|
||||||
} else {
|
|
||||||
result.add("'" + propertyName + "' is expected to be of type " + type + ", " +
|
|
||||||
"but is of type '" + propValue.getClass().getSimpleName() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void validate(final ArrayList<String> result, final T propValue, final Map<String, Object> props);
|
|
||||||
|
|
||||||
public void verifyConsistency(final Map.Entry<HsHostingAssetType, HsHostingAssetValidator> typeDef) {
|
|
||||||
if (required == null ) {
|
|
||||||
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> toMap(final ObjectMapper mapper) {
|
|
||||||
final Map<String, Object> map = mapper.convertValue(this, Map.class);
|
|
||||||
map.put("type", simpleTypeName());
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String simpleTypeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
class IntegerPropertyValidator extends HsHostingAssetPropertyValidator<Integer>{
|
|
||||||
|
|
||||||
private String unit;
|
|
||||||
private Integer min;
|
|
||||||
private Integer max;
|
|
||||||
private Integer step;
|
|
||||||
|
|
||||||
public static IntegerPropertyValidator integerProperty(final String propertyName) {
|
|
||||||
return new IntegerPropertyValidator(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntegerPropertyValidator(final String propertyName) {
|
|
||||||
super(Integer.class, propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validate(final ArrayList<String> result, final Integer propValue, final Map<String, Object> props) {
|
|
||||||
if (min != null && propValue < min) {
|
|
||||||
result.add("'" + propertyName + "' is expected to be >= " + min + " but is " + propValue);
|
|
||||||
}
|
|
||||||
if (max != null && propValue > max) {
|
|
||||||
result.add("'" + propertyName + "' is expected to be <= " + max + " but is " + propValue);
|
|
||||||
}
|
|
||||||
if (step != null && propValue % step != 0) {
|
|
||||||
result.add("'" + propertyName + "' is expected to be multiple of " + step + " but is " + propValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String simpleTypeName() {
|
|
||||||
return "integer";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
class EnumPropertyValidator extends HsHostingAssetPropertyValidator<String> {
|
|
||||||
|
|
||||||
private String[] values;
|
|
||||||
|
|
||||||
private EnumPropertyValidator(final String propertyName) {
|
|
||||||
super(String.class, propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EnumPropertyValidator enumerationProperty(final String propertyName) {
|
|
||||||
return new EnumPropertyValidator(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HsHostingAssetPropertyValidator<String> values(final String... values) {
|
|
||||||
this.values = values;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validate(final ArrayList<String> result, final String propValue, final Map<String, Object> props) {
|
|
||||||
if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) {
|
|
||||||
result.add("'" + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String simpleTypeName() {
|
|
||||||
return "enumeration";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
class BooleanPropertyValidator extends HsHostingAssetPropertyValidator<Boolean> {
|
|
||||||
|
|
||||||
private Map.Entry<String, String> falseIf;
|
|
||||||
|
|
||||||
private BooleanPropertyValidator(final String propertyName) {
|
|
||||||
super(Boolean.class, propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BooleanPropertyValidator booleanProperty(final String propertyName) {
|
|
||||||
return new BooleanPropertyValidator(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
HsHostingAssetPropertyValidator<Boolean> falseIf(final String refPropertyName, final String refPropertyValue) {
|
|
||||||
this.falseIf = new SimpleImmutableEntry<>(refPropertyName, refPropertyValue);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validate(final ArrayList<String> result, final Boolean propValue, final Map<String, Object> props) {
|
|
||||||
if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) {
|
|
||||||
if (propValue) {
|
|
||||||
result.add("'" + propertyName + "' is expected to be false because " +
|
|
||||||
falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String simpleTypeName() {
|
|
||||||
return "boolean";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validator;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
|
||||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.EnumPropertyValidator.enumerationProperty;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetPropertyValidator.defType;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.BooleanPropertyValidator.booleanProperty;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerPropertyValidator.integerProperty;
|
|
||||||
|
|
||||||
public class HsHostingAssetValidator {
|
|
||||||
|
|
||||||
private static final Map<HsHostingAssetType, HsHostingAssetValidator> validators = Map.ofEntries(
|
|
||||||
defType(HsHostingAssetType.CLOUD_SERVER, new HsHostingAssetValidator(
|
|
||||||
integerProperty("CPUs").min(1).max(32).required(),
|
|
||||||
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
|
||||||
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
|
||||||
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
|
||||||
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(),
|
|
||||||
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional())),
|
|
||||||
defType(HsHostingAssetType.MANAGED_SERVER, new HsHostingAssetValidator(
|
|
||||||
integerProperty("CPUs").min(1).max(32).required(),
|
|
||||||
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
|
||||||
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
|
||||||
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
|
||||||
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(),
|
|
||||||
enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional(),
|
|
||||||
booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").optional(),
|
|
||||||
booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(),
|
|
||||||
booleanProperty("SLA-PgSQL").falseIf("SLA-Platform", "BASIC").optional(),
|
|
||||||
booleanProperty("SLA-Office").falseIf("SLA-Platform", "BASIC").optional(),
|
|
||||||
booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional())),
|
|
||||||
defType(HsHostingAssetType.MANAGED_WEBSPACE, new HsHostingAssetValidator(
|
|
||||||
integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(),
|
|
||||||
integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(),
|
|
||||||
integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(),
|
|
||||||
enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional(),
|
|
||||||
integerProperty("Daemons").min(0).max(10).optional(),
|
|
||||||
booleanProperty("Online Office Server").optional())
|
|
||||||
));
|
|
||||||
static {
|
|
||||||
validators.entrySet().forEach(typeDef -> {
|
|
||||||
stream(typeDef.getValue().propertyValidators).forEach( entry -> {
|
|
||||||
entry.verifyConsistency(typeDef);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private final HsHostingAssetPropertyValidator<?>[] propertyValidators;
|
|
||||||
|
|
||||||
public static HsHostingAssetValidator forType(final HsHostingAssetType type) {
|
|
||||||
return validators.get(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
HsHostingAssetValidator(final HsHostingAssetPropertyValidator<?>... validators) {
|
|
||||||
propertyValidators = validators;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set<HsHostingAssetType> types() {
|
|
||||||
return validators.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var result = new ArrayList<String>();
|
|
||||||
assetEntity.getConfig().keySet().forEach( givenPropName -> {
|
|
||||||
if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) {
|
|
||||||
result.add("'" + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stream(propertyValidators).forEach(pv -> {
|
|
||||||
result.addAll(pv.validate(assetEntity.getConfig()));
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Map<String, Object>> properties() {
|
|
||||||
final var mapper = new ObjectMapper();
|
|
||||||
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
|
||||||
return Arrays.stream(propertyValidators)
|
|
||||||
.map(propertyValidator -> propertyValidator.toMap(mapper))
|
|
||||||
.map(HsHostingAssetValidator::asKeyValueMap)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
private static Map<String, Object> asKeyValueMap(final Map map) {
|
|
||||||
return (Map<String, Object>) map;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
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 net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsCloudServerHostingAssetValidator extends HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
public HsCloudServerHostingAssetValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("CPUs").min(1).max(32).required(),
|
||||||
|
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
||||||
|
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class HsHostingAssetEntityValidators {
|
||||||
|
|
||||||
|
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType>> validators = new HashMap<>();
|
||||||
|
static {
|
||||||
|
register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator());
|
||||||
|
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
|
||||||
|
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> validator) {
|
||||||
|
stream(validator.propertyValidators).forEach( entry -> {
|
||||||
|
entry.verifyConsistency(Map.entry(type, validator));
|
||||||
|
});
|
||||||
|
validators.put(type, validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> forType(final Enum<HsHostingAssetType> type) {
|
||||||
|
return validators.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Enum<HsHostingAssetType>> types() {
|
||||||
|
return validators.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) {
|
||||||
|
final var violations = HsHostingAssetEntityValidators.forType(entityToSave.getType()).validate(entityToSave);
|
||||||
|
if (!violations.isEmpty()) {
|
||||||
|
throw new ValidationException(violations.toString());
|
||||||
|
}
|
||||||
|
return entityToSave;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
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 net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsManagedServerHostingAssetValidator extends HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
public HsManagedServerHostingAssetValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("CPUs").min(1).max(32).required(),
|
||||||
|
integerProperty("RAM").unit("GB").min(1).max(128).required(),
|
||||||
|
integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
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 net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerPropertyValidator.integerProperty;
|
||||||
|
|
||||||
|
class HsManagedWebspaceHostingAssetValidator extends HsEntityValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
public HsManagedWebspaceHostingAssetValidator() {
|
||||||
|
super(
|
||||||
|
integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(),
|
||||||
|
integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(),
|
||||||
|
integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var result = super.validate(assetEntity);
|
||||||
|
validateIdentifierPattern(result, assetEntity);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateIdentifierPattern(final List<String> result, final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var expectedIdentifierPattern = "^" + assetEntity.getParentAsset().getBookingItem().getDebitor().getDefaultPrefix() + "[0-9][0-9]$";
|
||||||
|
if ( !assetEntity.getIdentifier().matches(expectedIdentifierPattern)) {
|
||||||
|
result.add("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class BooleanPropertyValidator extends HsPropertyValidator<Boolean> {
|
||||||
|
|
||||||
|
private Map.Entry<String, String> falseIf;
|
||||||
|
|
||||||
|
private BooleanPropertyValidator(final String propertyName) {
|
||||||
|
super(Boolean.class, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BooleanPropertyValidator booleanProperty(final String propertyName) {
|
||||||
|
return new BooleanPropertyValidator(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<Boolean> falseIf(final String refPropertyName, final String refPropertyValue) {
|
||||||
|
this.falseIf = new AbstractMap.SimpleImmutableEntry<>(refPropertyName, refPropertyValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validate(final ArrayList<String> result, final String propertiesName, final Boolean propValue, final Map<String, Object> props) {
|
||||||
|
if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) {
|
||||||
|
if (propValue) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be false because " +
|
||||||
|
propertiesName+"." + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "boolean";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class EnumerationPropertyValidator extends HsPropertyValidator<String> {
|
||||||
|
|
||||||
|
private String[] values;
|
||||||
|
|
||||||
|
private EnumerationPropertyValidator(final String propertyName) {
|
||||||
|
super(String.class, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumerationPropertyValidator enumerationProperty(final String propertyName) {
|
||||||
|
return new EnumerationPropertyValidator(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<String> values(final String... values) {
|
||||||
|
this.values = values;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validate(final ArrayList<String> result, final String propertiesName, final String propValue, final Map<String, Object> props) {
|
||||||
|
if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "enumeration";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
|
|
||||||
|
public class HsEntityValidator<E extends Validatable<E, T>, T extends Enum<T>> {
|
||||||
|
|
||||||
|
public final HsPropertyValidator<?>[] propertyValidators;
|
||||||
|
|
||||||
|
public HsEntityValidator(final HsPropertyValidator<?>... validators) {
|
||||||
|
propertyValidators = validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> validate(final E assetEntity) {
|
||||||
|
final var result = new ArrayList<String>();
|
||||||
|
assetEntity.getProperties().keySet().forEach( givenPropName -> {
|
||||||
|
if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) {
|
||||||
|
result.add("'"+assetEntity.getPropertiesName()+"." + givenPropName + "' is not expected but is set to '" +assetEntity.getProperties().get(givenPropName) + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stream(propertyValidators).forEach(pv -> {
|
||||||
|
result.addAll(pv.validate(assetEntity.getPropertiesName(), assetEntity.getProperties()));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> properties() {
|
||||||
|
final var mapper = new ObjectMapper();
|
||||||
|
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||||
|
return Arrays.stream(propertyValidators)
|
||||||
|
.map(propertyValidator -> propertyValidator.toMap(mapper))
|
||||||
|
.map(HsEntityValidator::asKeyValueMap)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
private static Map<String, Object> asKeyValueMap(final Map map) {
|
||||||
|
return (Map<String, Object>) map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public abstract class HsPropertyValidator<T> {
|
||||||
|
|
||||||
|
final Class<T> type;
|
||||||
|
final String propertyName;
|
||||||
|
private Boolean required;
|
||||||
|
|
||||||
|
public static <K, V> Map.Entry<K, V> defType(K k, V v) {
|
||||||
|
return new SimpleImmutableEntry<>(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<T> required() {
|
||||||
|
required = Boolean.TRUE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HsPropertyValidator<T> optional() {
|
||||||
|
required = Boolean.FALSE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<String> validate(final String propertiesName, final Map<String, Object> props) {
|
||||||
|
final var result = new ArrayList<String>();
|
||||||
|
final var propValue = props.get(propertyName);
|
||||||
|
if (propValue == null) {
|
||||||
|
if (required) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is required but missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (propValue != null){
|
||||||
|
if ( type.isInstance(propValue)) {
|
||||||
|
//noinspection unchecked
|
||||||
|
validate(result, propertiesName, (T) propValue, props);
|
||||||
|
} else {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be of type " + type + ", " +
|
||||||
|
"but is of type '" + propValue.getClass().getSimpleName() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void validate(final ArrayList<String> result, final String propertiesName, final T propValue, final Map<String, Object> props);
|
||||||
|
|
||||||
|
public void verifyConsistency(final Map.Entry<? extends Enum<?>, ?> typeDef) {
|
||||||
|
if (required == null ) {
|
||||||
|
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap(final ObjectMapper mapper) {
|
||||||
|
final Map<String, Object> map = mapper.convertValue(this, Map.class);
|
||||||
|
map.put("type", simpleTypeName());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String simpleTypeName();
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class IntegerPropertyValidator extends HsPropertyValidator<Integer> {
|
||||||
|
|
||||||
|
private String unit;
|
||||||
|
private Integer min;
|
||||||
|
private Integer max;
|
||||||
|
private Integer step;
|
||||||
|
|
||||||
|
public static IntegerPropertyValidator integerProperty(final String propertyName) {
|
||||||
|
return new IntegerPropertyValidator(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntegerPropertyValidator(final String propertyName) {
|
||||||
|
super(Integer.class, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validate(final ArrayList<String> result, final String propertiesName, final Integer propValue, final Map<String, Object> props) {
|
||||||
|
if (min != null && propValue < min) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be >= " + min + " but is " + propValue);
|
||||||
|
}
|
||||||
|
if (max != null && propValue > max) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be <= " + max + " but is " + propValue);
|
||||||
|
}
|
||||||
|
if (step != null && propValue % step != 0) {
|
||||||
|
result.add("'"+propertiesName+"." + propertyName + "' is expected to be multiple of " + step + " but is " + propValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String simpleTypeName() {
|
||||||
|
return "integer";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface Validatable<E, T extends Enum<T>> {
|
||||||
|
|
||||||
|
|
||||||
|
Enum<T> getType();
|
||||||
|
|
||||||
|
String getPropertiesName();
|
||||||
|
Map<String, Object> getProperties();
|
||||||
|
}
|
@ -8,6 +8,7 @@ import jakarta.persistence.EntityManager;
|
|||||||
import jakarta.persistence.PersistenceContext;
|
import jakarta.persistence.PersistenceContext;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -25,6 +26,7 @@ public class RbacGrantsDiagramService {
|
|||||||
|
|
||||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||||
|
|
||||||
|
new File("doc/temp").mkdirs();
|
||||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
||||||
writer.write("""
|
writer.write("""
|
||||||
### all grants to %s
|
### all grants to %s
|
||||||
@ -192,8 +194,9 @@ public class RbacGrantsDiagramService {
|
|||||||
return "[" + roleType + "\nref:" + uuid + "]";
|
return "[" + roleType + "\nref:" + uuid + "]";
|
||||||
}
|
}
|
||||||
if (refType.equals("perm")) {
|
if (refType.equals("perm")) {
|
||||||
final var roleType = idName.split(":")[1];
|
final var parts = idName.split(":");
|
||||||
return "{{" + roleType + "\nref:" + uuid + "}}";
|
final var permType = parts[2];
|
||||||
|
return "{{" + permType + "\nref:" + uuid + "}}";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -205,7 +208,7 @@ public class RbacGrantsDiagramService {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static String cleanId(final String idName) {
|
private static String cleanId(final String idName) {
|
||||||
return idName.replaceAll("@.*", "")
|
return idName.replaceAll("@.*", "")
|
||||||
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
|
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "").replace(">", ":");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,7 +53,11 @@ components:
|
|||||||
bookingItemUuid:
|
bookingItemUuid:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: false
|
nullable: true
|
||||||
|
parentAssetUuid:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
type:
|
type:
|
||||||
$ref: '#/components/schemas/HsHostingAssetType'
|
$ref: '#/components/schemas/HsHostingAssetType'
|
||||||
identifier:
|
identifier:
|
||||||
@ -72,7 +76,6 @@ components:
|
|||||||
- type
|
- type
|
||||||
- identifier
|
- identifier
|
||||||
- caption
|
- caption
|
||||||
- debitorUuid
|
|
||||||
- config
|
- config
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ begin
|
|||||||
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
||||||
insert
|
insert
|
||||||
into hs_booking_item (uuid, debitoruuid, type, caption, validity, resources)
|
into hs_booking_item (uuid, debitoruuid, type, caption, validity, resources)
|
||||||
values (uuid_generate_v4(), relatedDebitor.uuid, 'MANAGED_SERVER', 'some ManagedServer', daterange('20221001', null, '[]'), '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
|
values (uuid_generate_v4(), relatedDebitor.uuid, 'MANAGED_SERVER', 'some ManagedServer', daterange('20221001', null, '[]'), '{ "CPUs": 2, "RAM": 8, "SDD": 512, "Traffic": 42 }'::jsonb),
|
||||||
(uuid_generate_v4(), relatedDebitor.uuid, 'CLOUD_SERVER', 'some CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
|
(uuid_generate_v4(), relatedDebitor.uuid, 'CLOUD_SERVER', 'some CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPUs": 2, "RAM": 4, "HDD": 1024, "Traffic": 42 }'::jsonb),
|
||||||
(uuid_generate_v4(), relatedDebitor.uuid, 'PRIVATE_CLOUD', 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPU": 10, "SDD": 10240, "HDD": 10240, "extra": 42 }'::jsonb);
|
(uuid_generate_v4(), relatedDebitor.uuid, 'PRIVATE_CLOUD', 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPUs": 10, "SDD": 10240, "HDD": 10240, "Traffic": 42 }'::jsonb);
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -24,12 +24,14 @@ create table if not exists hs_hosting_asset
|
|||||||
(
|
(
|
||||||
uuid uuid unique references RbacObject (uuid),
|
uuid uuid unique references RbacObject (uuid),
|
||||||
version int not null default 0,
|
version int not null default 0,
|
||||||
bookingItemUuid uuid not null references hs_booking_item(uuid),
|
bookingItemUuid uuid null references hs_booking_item(uuid),
|
||||||
type HsHostingAssetType not null,
|
type HsHostingAssetType not null,
|
||||||
parentAssetUuid uuid null references hs_hosting_asset(uuid),
|
parentAssetUuid uuid null references hs_hosting_asset(uuid),
|
||||||
identifier varchar(80) not null,
|
identifier varchar(80) not null,
|
||||||
caption varchar(80) not null,
|
caption varchar(80) not null,
|
||||||
config jsonb not null
|
config jsonb not null,
|
||||||
|
|
||||||
|
constraint chk_hs_hosting_asset_has_booking_item_or_parent_asset check (bookingItemUuid is not null or parentAssetUuid is not null)
|
||||||
);
|
);
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -39,8 +39,6 @@ begin
|
|||||||
SELECT * FROM hs_hosting_asset WHERE uuid = NEW.parentAssetUuid INTO newParentServer;
|
SELECT * FROM hs_hosting_asset WHERE uuid = NEW.parentAssetUuid INTO newParentServer;
|
||||||
|
|
||||||
SELECT * FROM hs_booking_item WHERE uuid = NEW.bookingItemUuid INTO newBookingItem;
|
SELECT * FROM hs_booking_item WHERE uuid = NEW.bookingItemUuid INTO newBookingItem;
|
||||||
assert newBookingItem.uuid is not null, format('newBookingItem must not be null for NEW.bookingItemUuid = %s', NEW.bookingItemUuid);
|
|
||||||
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsHostingAssetOWNER(NEW),
|
hsHostingAssetOWNER(NEW),
|
||||||
|
@ -44,10 +44,10 @@ begin
|
|||||||
raise notice 'creating test hosting-asset: %', givenPartnerNumber::text || givenDebitorSuffix::text;
|
raise notice 'creating test hosting-asset: %', givenPartnerNumber::text || givenDebitorSuffix::text;
|
||||||
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
||||||
insert into hs_hosting_asset
|
insert into hs_hosting_asset
|
||||||
(uuid, bookingitemuuid, type, parentAssetUuid, identifier, caption, config)
|
(uuid, bookingitemuuid, type, parentAssetUuid, identifier, caption, config)
|
||||||
values (managedServerUuid, relatedPrivateCloudBookingItem.uuid, 'MANAGED_SERVER', null, 'vm10' || givenDebitorSuffix, 'some ManagedServer', '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
|
values (managedServerUuid, relatedPrivateCloudBookingItem.uuid, 'MANAGED_SERVER', null, 'vm10' || givenDebitorSuffix, 'some ManagedServer', '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb),
|
||||||
(uuid_generate_v4(), relatedPrivateCloudBookingItem.uuid, 'CLOUD_SERVER', null, 'vm20' || givenDebitorSuffix, 'another CloudServer', '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
|
(uuid_generate_v4(), relatedPrivateCloudBookingItem.uuid, 'CLOUD_SERVER', null, 'vm20' || givenDebitorSuffix, 'another CloudServer', '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb),
|
||||||
(uuid_generate_v4(), relatedManagedServerBookingItem.uuid, 'MANAGED_WEBSPACE', managedServerUuid, givenWebspacePrefix || '01', 'some Webspace', '{ "RAM": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb);
|
(uuid_generate_v4(), relatedManagedServerBookingItem.uuid, 'MANAGED_WEBSPACE', managedServerUuid, givenWebspacePrefix || '01', 'some Webspace', '{ "RAM": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb);
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.matchesRegex;
|
import static org.hamcrest.Matchers.matchesRegex;
|
||||||
@ -69,37 +70,42 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"caption": "some ManagedServer",
|
"type": "MANAGED_SERVER",
|
||||||
"validFrom": "2022-10-01",
|
"caption": "some ManagedServer",
|
||||||
"validTo": null,
|
"validFrom": "2022-10-01",
|
||||||
"resources": {
|
"validTo": null,
|
||||||
"CPU": 2,
|
"resources": {
|
||||||
"SDD": 512,
|
"RAM": 8,
|
||||||
"extra": 42
|
"SDD": 512,
|
||||||
}
|
"CPUs": 2,
|
||||||
},
|
"Traffic": 42
|
||||||
{
|
}
|
||||||
"caption": "some CloudServer",
|
},
|
||||||
"validFrom": "2023-01-15",
|
{
|
||||||
"validTo": "2024-04-14",
|
"type": "CLOUD_SERVER",
|
||||||
"resources": {
|
"caption": "some CloudServer",
|
||||||
"CPU": 2,
|
"validFrom": "2023-01-15",
|
||||||
"HDD": 1024,
|
"validTo": "2024-04-14",
|
||||||
"extra": 42
|
"resources": {
|
||||||
}
|
"HDD": 1024,
|
||||||
},
|
"RAM": 4,
|
||||||
{
|
"CPUs": 2,
|
||||||
"caption": "some PrivateCloud",
|
"Traffic": 42
|
||||||
"validFrom": "2024-04-01",
|
}
|
||||||
"validTo": null,
|
},
|
||||||
"resources": {
|
{
|
||||||
"CPU": 10,
|
"type": "PRIVATE_CLOUD",
|
||||||
"HDD": 10240,
|
"caption": "some PrivateCloud",
|
||||||
"SDD": 10240,
|
"validFrom": "2024-04-01",
|
||||||
"extra": 42
|
"validTo": null,
|
||||||
}
|
"resources": {
|
||||||
}
|
"HDD": 10240,
|
||||||
]
|
"SDD": 10240,
|
||||||
|
"CPUs": 10,
|
||||||
|
"Traffic": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
"""));
|
"""));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@ -123,7 +129,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"debitorUuid": "%s",
|
"debitorUuid": "%s",
|
||||||
"type": "MANAGED_SERVER",
|
"type": "MANAGED_SERVER",
|
||||||
"caption": "some new booking",
|
"caption": "some new booking",
|
||||||
"resources": { "CPU": 12, "extra": 42 },
|
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 },
|
||||||
"validFrom": "2022-10-13"
|
"validFrom": "2022-10-13"
|
||||||
}
|
}
|
||||||
""".formatted(givenDebitor.getUuid()))
|
""".formatted(givenDebitor.getUuid()))
|
||||||
@ -139,7 +145,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"caption": "some new booking",
|
"caption": "some new booking",
|
||||||
"validFrom": "2022-10-13",
|
"validFrom": "2022-10-13",
|
||||||
"validTo": null,
|
"validTo": null,
|
||||||
"resources": { "CPU": 12 }
|
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*"))
|
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*"))
|
||||||
@ -177,7 +183,12 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"caption": "some CloudServer",
|
"caption": "some CloudServer",
|
||||||
"validFrom": "2023-01-15",
|
"validFrom": "2023-01-15",
|
||||||
"validTo": "2024-04-14",
|
"validTo": "2024-04-14",
|
||||||
"resources": { CPU: 2, HDD: 1024 }
|
"resources": {
|
||||||
|
"HDD": 1024,
|
||||||
|
"RAM": 4,
|
||||||
|
"CPUs": 2,
|
||||||
|
"Traffic": 42
|
||||||
|
}
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
}
|
}
|
||||||
@ -222,7 +233,12 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"caption": "some CloudServer",
|
"caption": "some CloudServer",
|
||||||
"validFrom": "2023-01-15",
|
"validFrom": "2023-01-15",
|
||||||
"validTo": "2024-04-14",
|
"validTo": "2024-04-14",
|
||||||
"resources": { CPU: 2, HDD: 1024 }
|
"resources": {
|
||||||
|
"HDD": 1024,
|
||||||
|
"RAM": 4,
|
||||||
|
"CPUs": 2,
|
||||||
|
"Traffic": 42
|
||||||
|
}
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
}
|
}
|
||||||
@ -234,7 +250,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void globalAdmin_canPatchAllUpdatablePropertiesOfBookingItem() {
|
void globalAdmin_canPatchAllUpdatablePropertiesOfBookingItem() {
|
||||||
|
|
||||||
final var givenBookingItem = givenSomeTemporaryBookingItemForDebitorNumber(1000111, entry("something", 1));
|
final var givenBookingItem = givenSomeBookingItem(1000111, MANAGED_WEBSPACE,
|
||||||
|
resource("HDD", 100), resource("SSD", 50), resource("Traffic", 250));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -245,9 +262,9 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"validFrom": "2020-06-05",
|
"validFrom": "2020-06-05",
|
||||||
"validTo": "2022-12-31",
|
"validTo": "2022-12-31",
|
||||||
"resources": {
|
"resources": {
|
||||||
"CPU": "4",
|
"Traffic": 500,
|
||||||
"HDD": null,
|
"HDD": null,
|
||||||
"SSD": "4096"
|
"SSD": 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@ -263,9 +280,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"validFrom": "2022-11-01",
|
"validFrom": "2022-11-01",
|
||||||
"validTo": "2022-12-31",
|
"validTo": "2022-12-31",
|
||||||
"resources": {
|
"resources": {
|
||||||
"CPU": "4",
|
"Traffic": 500,
|
||||||
"SSD": "4096",
|
"SSD": 100
|
||||||
"something": 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
@ -288,7 +304,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void globalAdmin_canDeleteArbitraryBookingItem() {
|
void globalAdmin_canDeleteArbitraryBookingItem() {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItem = givenSomeTemporaryBookingItemForDebitorNumber(1000111, entry("something", 1));
|
final var givenBookingItem = givenSomeBookingItem(1000111, MANAGED_WEBSPACE,
|
||||||
|
resource("HDD", 100), resource("SSD", 50), resource("Traffic", 250));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -306,7 +323,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void normalUser_canNotDeleteUnrelatedBookingItem() {
|
void normalUser_canNotDeleteUnrelatedBookingItem() {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItem = givenSomeTemporaryBookingItemForDebitorNumber(1000111, entry("something", 1));
|
final var givenBookingItem = givenSomeBookingItem(1000111, MANAGED_WEBSPACE,
|
||||||
|
resource("HDD", 100), resource("SSD", 50), resource("Traffic", 250));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -322,15 +340,16 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HsBookingItemEntity givenSomeTemporaryBookingItemForDebitorNumber(final int debitorNumber,
|
@SafeVarargs
|
||||||
final Map.Entry<String, Integer> resources) {
|
private HsBookingItemEntity givenSomeBookingItem(final int debitorNumber,
|
||||||
|
final HsBookingItemType hsBookingItemType, final Map.Entry<String, Object>... resources) {
|
||||||
return jpaAttempt.transacted(() -> {
|
return jpaAttempt.transacted(() -> {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenDebitor = debitorRepo.findDebitorByDebitorNumber(debitorNumber).get(0);
|
final var givenDebitor = debitorRepo.findDebitorByDebitorNumber(debitorNumber).get(0);
|
||||||
final var newBookingItem = HsBookingItemEntity.builder()
|
final var newBookingItem = HsBookingItemEntity.builder()
|
||||||
.uuid(UUID.randomUUID())
|
.uuid(UUID.randomUUID())
|
||||||
.debitor(givenDebitor)
|
.debitor(givenDebitor)
|
||||||
.type(HsBookingItemType.MANAGED_WEBSPACE)
|
.type(hsBookingItemType)
|
||||||
.caption("some test-booking")
|
.caption("some test-booking")
|
||||||
.resources(Map.ofEntries(resources))
|
.resources(Map.ofEntries(resources))
|
||||||
.validity(Range.closedOpen(
|
.validity(Range.closedOpen(
|
||||||
@ -340,4 +359,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
return bookingItemRepo.save(newBookingItem);
|
return bookingItemRepo.save(newBookingItem);
|
||||||
}).assertSuccessful().returnedValue();
|
}).assertSuccessful().returnedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map.Entry<String, Object> resource(final String key, final Object value) {
|
||||||
|
return entry(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,9 +167,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
// then
|
// then
|
||||||
allTheseBookingItemsAreReturned(
|
allTheseBookingItemsAreReturned(
|
||||||
result,
|
result,
|
||||||
"HsBookingItemEntity(D-1000212, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
|
"HsBookingItemEntity(D-1000212, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })",
|
||||||
"HsBookingItemEntity(D-1000212, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
|
"HsBookingItemEntity(D-1000212, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD: 1024, RAM: 4, Traffic: 42 })",
|
||||||
"HsBookingItemEntity(D-1000212, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
|
"HsBookingItemEntity(D-1000212, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -184,9 +184,9 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
|||||||
// then:
|
// then:
|
||||||
exactlyTheseBookingItemsAreReturned(
|
exactlyTheseBookingItemsAreReturned(
|
||||||
result,
|
result,
|
||||||
"HsBookingItemEntity(D-1000111, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })",
|
"HsBookingItemEntity(D-1000111, MANAGED_SERVER, [2022-10-01,), some ManagedServer, { CPUs: 2, RAM: 8, SDD: 512, Traffic: 42 })",
|
||||||
"HsBookingItemEntity(D-1000111, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPU: 2, HDD: 1024, extra: 42 })",
|
"HsBookingItemEntity(D-1000111, CLOUD_SERVER, [2023-01-15,2024-04-15), some CloudServer, { CPUs: 2, HDD: 1024, RAM: 4, Traffic: 42 })",
|
||||||
"HsBookingItemEntity(D-1000111, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10240, SDD: 10240, extra: 42 })");
|
"HsBookingItemEntity(D-1000111, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10240, SDD: 10240, Traffic: 42 })");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.valid;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
|
|
||||||
|
class HsBookingItemEntityValidatorsUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validThrowsException() {
|
||||||
|
// given
|
||||||
|
final var cloudServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = catchThrowable( ()-> valid(cloudServerBookingItemEntity) );
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isInstanceOf(ValidationException.class)
|
||||||
|
.hasMessageContaining(
|
||||||
|
"'resources.CPUs' is required but missing",
|
||||||
|
"'resources.RAM' is required but missing",
|
||||||
|
"'resources.SSD' is required but missing",
|
||||||
|
"'resources.Traffic' is required but missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listsTypes() {
|
||||||
|
// when
|
||||||
|
final var result = HsBookingItemEntityValidators.types();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsCloudServerBookingItemValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesProperties() {
|
||||||
|
// given
|
||||||
|
final var validator = HsBookingItemEntityValidators.forType(CLOUD_SERVER);
|
||||||
|
final var cloudServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("CPUs", 2),
|
||||||
|
entry("RAM", 25),
|
||||||
|
entry("SSD", 25),
|
||||||
|
entry("Traffic", 250),
|
||||||
|
entry("SLA-EMail", true)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(cloudServerBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly("'resources.SLA-EMail' is not expected but is set to 'true'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsAllValidations() {
|
||||||
|
// when
|
||||||
|
final var validator = forType(CLOUD_SERVER);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
|
"{type=integer, propertyName=CPUs, required=true, unit=null, min=1, max=32, step=null}",
|
||||||
|
"{type=integer, propertyName=RAM, required=true, unit=GB, min=1, max=128, step=null}",
|
||||||
|
"{type=integer, propertyName=SSD, required=true, unit=GB, min=25, max=1000, step=25}",
|
||||||
|
"{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=4000, step=250}",
|
||||||
|
"{type=integer, propertyName=Traffic, required=true, unit=GB, min=250, max=10000, step=250}",
|
||||||
|
"{type=enumeration, propertyName=SLA-Infrastructure, required=false, values=[BASIC, EXT8H, EXT4H, EXT2H]}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsManagedServerBookingItemValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesProperties() {
|
||||||
|
// given
|
||||||
|
final var validator = HsBookingItemEntityValidators.forType(MANAGED_SERVER);
|
||||||
|
final var mangedServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("CPUs", 2),
|
||||||
|
entry("RAM", 25),
|
||||||
|
entry("SSD", 25),
|
||||||
|
entry("Traffic", 250),
|
||||||
|
entry("SLA-EMail", true)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly("'resources.SLA-EMail' is expected to be false because resources.SLA-Platform=BASIC but is true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsAllValidations() {
|
||||||
|
// when
|
||||||
|
final var validator = forType(MANAGED_SERVER);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
|
"{type=integer, propertyName=CPUs, required=true, unit=null, min=1, max=32, step=null}",
|
||||||
|
"{type=integer, propertyName=RAM, required=true, unit=GB, min=1, max=128, step=null}",
|
||||||
|
"{type=integer, propertyName=SSD, required=true, unit=GB, min=25, max=1000, step=25}",
|
||||||
|
"{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=4000, step=250}",
|
||||||
|
"{type=integer, propertyName=Traffic, required=true, unit=GB, min=250, max=10000, step=250}",
|
||||||
|
"{type=enumeration, propertyName=SLA-Platform, required=false, values=[BASIC, EXT8H, EXT4H, EXT2H]}",
|
||||||
|
"{type=boolean, propertyName=SLA-EMail, required=false, falseIf={SLA-Platform=BASIC}}",
|
||||||
|
"{type=boolean, propertyName=SLA-Maria, required=false, falseIf={SLA-Platform=BASIC}}",
|
||||||
|
"{type=boolean, propertyName=SLA-PgSQL, required=false, falseIf={SLA-Platform=BASIC}}",
|
||||||
|
"{type=boolean, propertyName=SLA-Office, required=false, falseIf={SLA-Platform=BASIC}}",
|
||||||
|
"{type=boolean, propertyName=SLA-Web, required=false, falseIf={SLA-Platform=BASIC}}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidators.forType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsManagedWebspaceBookingItemValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesProperties() {
|
||||||
|
// given
|
||||||
|
final var mangedServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.resources(Map.ofEntries(
|
||||||
|
entry("CPUs", 2),
|
||||||
|
entry("RAM", 25),
|
||||||
|
entry("SSD", 25),
|
||||||
|
entry("Traffic", 250),
|
||||||
|
entry("SLA-EMail", true)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
final var validator = forType(mangedServerBookingItemEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerBookingItemEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'resources.CPUs' is not expected but is set to '2'",
|
||||||
|
"'resources.SLA-EMail' is not expected but is set to 'true'",
|
||||||
|
"'resources.RAM' is not expected but is set to '25'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsAllValidations() {
|
||||||
|
// when
|
||||||
|
final var validator = forType(MANAGED_WEBSPACE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
|
"{type=integer, propertyName=SSD, required=true, unit=GB, min=1, max=100, step=1}",
|
||||||
|
"{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=250, step=10}",
|
||||||
|
"{type=integer, propertyName=Traffic, required=true, unit=GB, min=10, max=1000, step=10}",
|
||||||
|
"{type=enumeration, propertyName=SLA-Platform, required=false, values=[BASIC, EXT24H]}",
|
||||||
|
"{type=integer, propertyName=Daemons, required=false, unit=null, min=0, max=10, step=null}",
|
||||||
|
"{type=boolean, propertyName=Online Office Server, required=false, falseIf=null}");
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,8 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.matchesRegex;
|
import static org.hamcrest.Matchers.matchesRegex;
|
||||||
@ -113,7 +115,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.header("current-user", "superuser-alex@hostsharing.net")
|
.header("current-user", "superuser-alex@hostsharing.net")
|
||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.get("http://localhost/api/hs/hosting/assets?type=" + HsHostingAssetType.MANAGED_SERVER)
|
.get("http://localhost/api/hs/hosting/assets?type=" + MANAGED_SERVER)
|
||||||
.then().log().all().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
@ -159,7 +161,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
class AddServer {
|
class AddServer {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canAddAsset() {
|
void globalAdmin_canAddBookedAsset() {
|
||||||
|
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenBookingItem = givenBookingItem("First", "some PrivateCloud");
|
final var givenBookingItem = givenBookingItem("First", "some PrivateCloud");
|
||||||
@ -173,7 +175,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"bookingItemUuid": "%s",
|
"bookingItemUuid": "%s",
|
||||||
"type": "MANAGED_SERVER",
|
"type": "MANAGED_SERVER",
|
||||||
"identifier": "vm1400",
|
"identifier": "vm1400",
|
||||||
"caption": "some new CloudServer",
|
"caption": "some new ManagedServer",
|
||||||
"config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 }
|
"config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 }
|
||||||
}
|
}
|
||||||
""".formatted(givenBookingItem.getUuid()))
|
""".formatted(givenBookingItem.getUuid()))
|
||||||
@ -187,7 +189,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
{
|
{
|
||||||
"type": "MANAGED_SERVER",
|
"type": "MANAGED_SERVER",
|
||||||
"identifier": "vm1400",
|
"identifier": "vm1400",
|
||||||
"caption": "some new CloudServer",
|
"caption": "some new ManagedServer",
|
||||||
"config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 }
|
"config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 }
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
@ -200,6 +202,48 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
assertThat(newUserUuid).isNotNull();
|
assertThat(newUserUuid).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parentAssetAgent_canAddSubAsset() {
|
||||||
|
|
||||||
|
context.define("superuser-alex@hostsharing.net");
|
||||||
|
final var givenParentAsset = givenParentAsset("First", MANAGED_SERVER);
|
||||||
|
|
||||||
|
final var location = RestAssured // @formatter:off
|
||||||
|
.given()
|
||||||
|
.header("current-user", "person-FirbySusan@example.com")
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.body("""
|
||||||
|
{
|
||||||
|
"parentAssetUuid": "%s",
|
||||||
|
"type": "MANAGED_WEBSPACE",
|
||||||
|
"identifier": "fir90",
|
||||||
|
"caption": "some new ManagedWebspace in client's ManagedServer",
|
||||||
|
"config": { "SSD": 100, "Traffic": 250 }
|
||||||
|
}
|
||||||
|
""".formatted(givenParentAsset.getUuid()))
|
||||||
|
.port(port)
|
||||||
|
.when()
|
||||||
|
.post("http://localhost/api/hs/hosting/assets")
|
||||||
|
.then().log().all().assertThat()
|
||||||
|
.statusCode(201)
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.body("", lenientlyEquals("""
|
||||||
|
{
|
||||||
|
"type": "MANAGED_WEBSPACE",
|
||||||
|
"identifier": "fir90",
|
||||||
|
"caption": "some new ManagedWebspace in client's ManagedServer",
|
||||||
|
"config": { "SSD": 100, "Traffic": 250 }
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*"))
|
||||||
|
.extract().header("Location"); // @formatter:on
|
||||||
|
|
||||||
|
// finally, the new asset can be accessed under the generated UUID
|
||||||
|
final var newUserUuid = UUID.fromString(
|
||||||
|
location.substring(location.lastIndexOf('/') + 1));
|
||||||
|
assertThat(newUserUuid).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void additionalValidationsArePerformend_whenAddingAsset() {
|
void additionalValidationsArePerformend_whenAddingAsset() {
|
||||||
|
|
||||||
@ -215,7 +259,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"bookingItemUuid": "%s",
|
"bookingItemUuid": "%s",
|
||||||
"type": "MANAGED_SERVER",
|
"type": "MANAGED_SERVER",
|
||||||
"identifier": "vm1400",
|
"identifier": "vm1400",
|
||||||
"caption": "some new CloudServer",
|
"caption": "some new ManagedServer",
|
||||||
"config": { "CPUs": 0, "extra": 42 }
|
"config": { "CPUs": 0, "extra": 42 }
|
||||||
}
|
}
|
||||||
""".formatted(givenBookingItem.getUuid()))
|
""".formatted(givenBookingItem.getUuid()))
|
||||||
@ -228,14 +272,14 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
{
|
{
|
||||||
"statusPhrase": "Bad Request",
|
"statusPhrase": "Bad Request",
|
||||||
"message": "['extra' is not expected but is '42', 'CPUs' is expected to be >= 1 but is 0, 'RAM' is required but missing, 'SSD' is required but missing, 'Traffic' is required but missing]"
|
"message": "['config.extra' is not expected but is set to '42', 'config.CPUs' is expected to be >= 1 but is 0, 'config.RAM' is required but missing, 'config.SSD' is required but missing, 'config.Traffic' is required but missing]"
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class GetASset {
|
class GetAsset {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canGetArbitraryAsset() {
|
void globalAdmin_canGetArbitraryAsset() {
|
||||||
@ -321,7 +365,8 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void globalAdmin_canPatchAllUpdatablePropertiesOfAsset() {
|
void globalAdmin_canPatchAllUpdatablePropertiesOfAsset() {
|
||||||
|
|
||||||
final var givenAsset = givenSomeTemporaryAssetForDebitorNumber("2001", entry("something", 1));
|
final var givenAsset = givenSomeTemporaryHostingAsset("2001", CLOUD_SERVER,
|
||||||
|
config("CPUs", 4), config("RAM", 100), config("HDD", 100), config("Traffic", 2000));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -330,9 +375,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"CPU": "4",
|
"CPUs": 2,
|
||||||
"HDD": null,
|
"HDD": null,
|
||||||
"SSD": "4096"
|
"SSD": 250
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@ -348,9 +393,9 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"identifier": "vm2001",
|
"identifier": "vm2001",
|
||||||
"caption": "some test-asset",
|
"caption": "some test-asset",
|
||||||
"config": {
|
"config": {
|
||||||
"CPU": "4",
|
"CPUs": 2,
|
||||||
"SSD": "4096",
|
"RAM": 100,
|
||||||
"something": 1
|
"SSD": 250
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")); // @formatter:on
|
""")); // @formatter:on
|
||||||
@ -359,7 +404,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
assertThat(assetRepo.findByUuid(givenAsset.getUuid())).isPresent().get()
|
assertThat(assetRepo.findByUuid(givenAsset.getUuid())).isPresent().get()
|
||||||
.matches(asset -> {
|
.matches(asset -> {
|
||||||
assertThat(asset.toString()).isEqualTo("HsHostingAssetEntity(CLOUD_SERVER, vm2001, some test-asset, D-1000111:some CloudServer, { CPU: 4, SSD: 4096, something: 1 })");
|
assertThat(asset.toString()).isEqualTo("HsHostingAssetEntity(CLOUD_SERVER, vm2001, some test-asset, D-1000111:some CloudServer, { CPUs: 2, RAM: 100, SSD: 250, Traffic: 2000 })");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -371,7 +416,8 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void globalAdmin_canDeleteArbitraryAsset() {
|
void globalAdmin_canDeleteArbitraryAsset() {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenAsset = givenSomeTemporaryAssetForDebitorNumber("2002", entry("something", 1));
|
final var givenAsset = givenSomeTemporaryHostingAsset("2002", CLOUD_SERVER,
|
||||||
|
config("CPUs", 4), config("RAM", 100), config("HDD", 100), config("Traffic", 2000));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -389,7 +435,8 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
@Test
|
@Test
|
||||||
void normalUser_canNotDeleteUnrelatedAsset() {
|
void normalUser_canNotDeleteUnrelatedAsset() {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var givenAsset = givenSomeTemporaryAssetForDebitorNumber("2003", entry("something", 1));
|
final var givenAsset = givenSomeTemporaryHostingAsset("2003", CLOUD_SERVER,
|
||||||
|
config("CPUs", 4), config("RAM", 100), config("HDD", 100), config("Traffic", 2000));
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -412,14 +459,22 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.findAny().orElseThrow();
|
.findAny().orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private HsHostingAssetEntity givenSomeTemporaryAssetForDebitorNumber(final String identifierSuffix,
|
HsHostingAssetEntity givenParentAsset(final String debitorName, final HsHostingAssetType assetType) {
|
||||||
final Map.Entry<String, Integer> resources) {
|
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike(debitorName).stream().findAny().orElseThrow();
|
||||||
|
final var givenAsset = assetRepo.findAllByCriteria(givenDebitor.getUuid(), null, assetType).stream().findAny().orElseThrow();
|
||||||
|
return givenAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
private HsHostingAssetEntity givenSomeTemporaryHostingAsset(final String identifierSuffix,
|
||||||
|
final HsHostingAssetType hostingAssetType,
|
||||||
|
final Map.Entry<String, Object>... resources) {
|
||||||
return jpaAttempt.transacted(() -> {
|
return jpaAttempt.transacted(() -> {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
final var newAsset = HsHostingAssetEntity.builder()
|
final var newAsset = HsHostingAssetEntity.builder()
|
||||||
.uuid(UUID.randomUUID())
|
.uuid(UUID.randomUUID())
|
||||||
.bookingItem(givenBookingItem("First", "some CloudServer"))
|
.bookingItem(givenBookingItem("First", "some CloudServer"))
|
||||||
.type(HsHostingAssetType.CLOUD_SERVER)
|
.type(hostingAssetType)
|
||||||
.identifier("vm" + identifierSuffix)
|
.identifier("vm" + identifierSuffix)
|
||||||
.caption("some test-asset")
|
.caption("some test-asset")
|
||||||
.config(Map.ofEntries(resources))
|
.config(Map.ofEntries(resources))
|
||||||
@ -428,4 +483,8 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
return assetRepo.save(newAsset);
|
return assetRepo.save(newAsset);
|
||||||
}).assertSuccessful().returnedValue();
|
}).assertSuccessful().returnedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map.Entry<String, Object> config(final String key, final Object value) {
|
||||||
|
return entry(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class HsHostingAssetPropsControllerAcceptanceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_canListPropertiesOfGivenAssetType() {
|
void anyone_canListPropertiesOfGivenAssetType() {
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -52,101 +52,50 @@ class HsHostingAssetPropsControllerAcceptanceTest {
|
|||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body("", lenientlyEquals("""
|
.body("", lenientlyEquals("""
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"propertyName": "CPUs",
|
"propertyName": "CPUs",
|
||||||
"required": true,
|
"required": true,
|
||||||
"unit": null,
|
"unit": null,
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 32,
|
"max": 32,
|
||||||
"step": null
|
"step": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"propertyName": "RAM",
|
"propertyName": "RAM",
|
||||||
"required": true,
|
"required": true,
|
||||||
"unit": "GB",
|
"unit": "GB",
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 128,
|
"max": 128,
|
||||||
"step": null
|
"step": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"propertyName": "SSD",
|
"propertyName": "SSD",
|
||||||
"required": true,
|
"required": true,
|
||||||
"unit": "GB",
|
"unit": "GB",
|
||||||
"min": 25,
|
"min": 25,
|
||||||
"max": 1000,
|
"max": 1000,
|
||||||
"step": 25
|
"step": 25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"propertyName": "HDD",
|
"propertyName": "HDD",
|
||||||
"required": false,
|
"required": false,
|
||||||
"unit": "GB",
|
"unit": "GB",
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 4000,
|
"max": 4000,
|
||||||
"step": 250
|
"step": 250
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"propertyName": "Traffic",
|
"propertyName": "Traffic",
|
||||||
"required": true,
|
"required": true,
|
||||||
"unit": "GB",
|
"unit": "GB",
|
||||||
"min": 250,
|
"min": 250,
|
||||||
"max": 10000,
|
"max": 10000,
|
||||||
"step": 250
|
"step": 250
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "enumeration",
|
|
||||||
"propertyName": "SLA-Platform",
|
|
||||||
"required": false,
|
|
||||||
"values": [
|
|
||||||
"BASIC",
|
|
||||||
"EXT8H",
|
|
||||||
"EXT4H",
|
|
||||||
"EXT2H"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"propertyName": "SLA-EMail",
|
|
||||||
"required": false,
|
|
||||||
"falseIf": {
|
|
||||||
"SLA-Platform": "BASIC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"propertyName": "SLA-Maria",
|
|
||||||
"required": false,
|
|
||||||
"falseIf": {
|
|
||||||
"SLA-Platform": "BASIC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"propertyName": "SLA-PgSQL",
|
|
||||||
"required": false,
|
|
||||||
"falseIf": {
|
|
||||||
"SLA-Platform": "BASIC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"propertyName": "SLA-Office",
|
|
||||||
"required": false,
|
|
||||||
"falseIf": {
|
|
||||||
"SLA-Platform": "BASIC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"propertyName": "SLA-Web",
|
|
||||||
"required": false,
|
|
||||||
"falseIf": {
|
|
||||||
"SLA-Platform": "BASIC"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""));
|
"""));
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
|
||||||
import static java.util.Map.entry;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesMissingProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
|
||||||
.type(MANAGED_WEBSPACE)
|
|
||||||
.config(emptyMap())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'SSD' is required but missing",
|
|
||||||
"'Traffic' is required but missing"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesUnknownProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
|
||||||
.type(MANAGED_WEBSPACE)
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("HDD", 0),
|
|
||||||
entry("SSD", 1),
|
|
||||||
entry("Traffic", 10),
|
|
||||||
entry("unknown", "some value")
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly("'unknown' is not expected but is 'some value'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesDependentProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER);
|
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
|
||||||
.type(MANAGED_SERVER)
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("CPUs", 2),
|
|
||||||
entry("RAM", 25),
|
|
||||||
entry("SSD", 25),
|
|
||||||
entry("Traffic", 250),
|
|
||||||
entry("SLA-EMail", true)
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly("'SLA-EMail' is expected to be false because SLA-Platform=BASIC but is true");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesValidProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE);
|
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
|
||||||
.type(MANAGED_WEBSPACE)
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("HDD", 200),
|
|
||||||
entry("SSD", 25),
|
|
||||||
entry("Traffic", 250)
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.forType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsCloudServerHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesProperties() {
|
||||||
|
// given
|
||||||
|
final var cloudServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("RAM", 2000),
|
||||||
|
entry("SSD", 256),
|
||||||
|
entry("Traffic", "250"),
|
||||||
|
entry("SLA-Platform", "xxx")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
final var validator = forType(cloudServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(cloudServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'config.SLA-Platform' is not expected but is set to 'xxx'",
|
||||||
|
"'config.CPUs' is required but missing",
|
||||||
|
"'config.RAM' is expected to be <= 128 but is 2000",
|
||||||
|
"'config.SSD' is expected to be multiple of 25 but is 256",
|
||||||
|
"'config.Traffic' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsAllValidations() {
|
||||||
|
// when
|
||||||
|
final var validator = forType(CLOUD_SERVER);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||||
|
"{type=integer, propertyName=CPUs, required=true, unit=null, min=1, max=32, step=null}",
|
||||||
|
"{type=integer, propertyName=RAM, required=true, unit=GB, min=1, max=128, step=null}",
|
||||||
|
"{type=integer, propertyName=SSD, required=true, unit=GB, min=25, max=1000, step=25}",
|
||||||
|
"{type=integer, propertyName=HDD, required=false, unit=GB, min=0, max=4000, step=250}",
|
||||||
|
"{type=integer, propertyName=Traffic, required=true, unit=GB, min=250, max=10000, step=250}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.valid;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
|
|
||||||
|
class HsHostingAssetEntityValidatorsUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validThrowsException() {
|
||||||
|
// given
|
||||||
|
final var cloudServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = catchThrowable( ()-> valid(cloudServerHostingAssetEntity) );
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isInstanceOf(ValidationException.class)
|
||||||
|
.hasMessageContaining(
|
||||||
|
"'config.CPUs' is required but missing",
|
||||||
|
"'config.RAM' is required but missing",
|
||||||
|
"'config.SSD' is required but missing",
|
||||||
|
"'config.Traffic' is required but missing");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsHostingAssetEntityValidators.forType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsManagedServerHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesProperties() {
|
||||||
|
// given
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("RAM", 2000),
|
||||||
|
entry("SSD", 256),
|
||||||
|
entry("Traffic", "250"),
|
||||||
|
entry("SLA-Platform", "xxx")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
final var validator = forType(mangedWebspaceHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'config.SLA-Platform' is not expected but is set to 'xxx'",
|
||||||
|
"'config.CPUs' is required but missing",
|
||||||
|
"'config.RAM' is expected to be <= 128 but is 2000",
|
||||||
|
"'config.SSD' is expected to be multiple of 25 but is 256",
|
||||||
|
"'config.Traffic' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyMap;
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
final HsBookingItemEntity managedServerBookingItem = HsBookingItemEntity.builder()
|
||||||
|
.debitor(HsOfficeDebitorEntity.builder().defaultPrefix("abc").build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
final HsHostingAssetEntity mangedServerAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.bookingItem(managedServerBookingItem)
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("HDD", 0),
|
||||||
|
entry("SSD", 1),
|
||||||
|
entry("Traffic", 10)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesIdentifier() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.parentAsset(mangedServerAssetEntity)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("HDD", 0),
|
||||||
|
entry("SSD", 1),
|
||||||
|
entry("Traffic", 10)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly("'identifier' expected to match '^abc[0-9][0-9]$', but is 'xyz00'");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesMissingProperties() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.parentAsset(mangedServerAssetEntity)
|
||||||
|
.identifier("abc00")
|
||||||
|
.config(emptyMap())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'config.SSD' is required but missing",
|
||||||
|
"'config.Traffic' is required but missing"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesUnknownProperties() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.parentAsset(mangedServerAssetEntity)
|
||||||
|
.identifier("abc00")
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("HDD", 0),
|
||||||
|
entry("SSD", 1),
|
||||||
|
entry("Traffic", 10),
|
||||||
|
entry("unknown", "some value")
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly("'config.unknown' is not expected but is set to 'some value'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesValidProperties() {
|
||||||
|
// given
|
||||||
|
final var validator = HsHostingAssetEntityValidators.forType(MANAGED_WEBSPACE);
|
||||||
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.parentAsset(mangedServerAssetEntity)
|
||||||
|
.identifier("abc00")
|
||||||
|
.config(Map.ofEntries(
|
||||||
|
entry("HDD", 200),
|
||||||
|
entry("SSD", 25),
|
||||||
|
entry("Traffic", 250)
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedWebspaceHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user