hierarchical-validation-baseline #59
@ -62,8 +62,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
|
||||
final var entityToSave = mapper.map(body, HsBookingItemEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
|
||||
final HsBookingItemEntity entityToSave1 = bookingItemRepo.save(entityToSave);
|
||||
final var saved = HsBookingItemEntityValidatorRegistry.validated(entityToSave1);
|
||||
final var saved = HsBookingItemEntityValidatorRegistry.validated(bookingItemRepo.save(entityToSave));
|
||||
|
||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
|
||||
final var uri =
|
||||
MvcUriComponentsBuilder.fromController(getClass())
|
||||
@ -84,7 +83,7 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
context.define(currentUser, assumedRoles);
|
||||
|
||||
final var result = bookingItemRepo.findByUuid(bookingItemUuid);
|
||||
result.ifPresent(entity -> em.detach(entity));
|
||||
result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading
|
||||
return result
|
||||
hsh-michaelhoennig marked this conversation as resolved
Outdated
hsh-marcsandlus
commented
comment would be nice comment would be nice
|
||||
.map(bookingItemEntity -> ResponseEntity.ok(
|
||||
mapper.map(bookingItemEntity, HsBookingItemResource.class, ENTITY_TO_RESOURCE_POSTMAPPER)))
|
||||
|
@ -21,25 +21,33 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
|
||||
|
||||
public List<String> validate(final HsBookingItemEntity bookingItem) {
|
||||
return sequentiallyValidate(
|
||||
() -> enrich(prefix(bookingItem.toShortString(), "resources"), validateProperties(bookingItem.getResources())),
|
||||
() -> enrich(prefix(bookingItem.toShortString(), "parentItem"), optionallyValidate(bookingItem.getParentItem())),
|
||||
() -> validateProperties(bookingItem),
|
||||
() -> optionallyValidate(bookingItem.getParentItem()),
|
||||
() -> validateAgainstSubEntities(bookingItem)
|
||||
hsh-michaelhoennig marked this conversation as resolved
hsh-marcsandlus
commented
enrich? enrich?
|
||||
);
|
||||
}
|
||||
|
||||
private List<String> validateProperties(final HsBookingItemEntity bookingItem) {
|
||||
return enrich(prefix(bookingItem.toShortString(), "resources"), validateProperties(bookingItem.getResources()));
|
||||
}
|
||||
|
||||
private static List<String> optionallyValidate(final HsBookingItemEntity bookingItem) {
|
||||
return bookingItem != null ? HsBookingItemEntityValidatorRegistry.doValidate(bookingItem) : emptyList();
|
||||
return bookingItem != null
|
||||
? enrich(prefix(bookingItem.toShortString(), ""),
|
||||
HsBookingItemEntityValidatorRegistry.doValidate(bookingItem))
|
||||
: emptyList();
|
||||
}
|
||||
|
||||
protected List<String> validateAgainstSubEntities(final HsBookingItemEntity bookingItem) {
|
||||
return Stream.concat(
|
||||
stream(propertyValidators)
|
||||
.map(propDef -> propDef.validateTotals(bookingItem))
|
||||
.flatMap(Collection::stream),
|
||||
stream(propertyValidators)
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(bookingItem, prop))
|
||||
).filter(Objects::nonNull).toList();
|
||||
return enrich(prefix(bookingItem.toShortString(), "resources"),
|
||||
Stream.concat(
|
||||
stream(propertyValidators)
|
||||
.map(propDef -> propDef.validateTotals(bookingItem))
|
||||
.flatMap(Collection::stream),
|
||||
stream(propertyValidators)
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(bookingItem, prop))
|
||||
).filter(Objects::nonNull).toList());
|
||||
}
|
||||
|
||||
// TODO.refa: convert into generic shape like multi-options validator
|
||||
@ -56,13 +64,13 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
|
||||
final var maxValue = getNonNullIntegerValue(propDef, bookingItem.getResources());
|
||||
if (propDef.thresholdPercentage() != null ) {
|
||||
return totalValue > (maxValue * propDef.thresholdPercentage() / 100)
|
||||
? "%s' total is %d%s, thus exceeds max total %s %d%s, which is above threshold of %d%%"
|
||||
.formatted(propName, totalValue, propUnit, propName, maxValue, propUnit, propDef.thresholdPercentage())
|
||||
? "%s' maximum total is %d%s, but actual total %s %d%s, which exceeds threshold of %d%%"
|
||||
.formatted(propName, maxValue, propUnit, propName, totalValue, propUnit, propDef.thresholdPercentage())
|
||||
: null;
|
||||
} else {
|
||||
return totalValue > maxValue
|
||||
? "%s' total is %d%s, thus exceeds max total %s %d%s"
|
||||
.formatted(propName, totalValue, propUnit, propName, maxValue, propUnit)
|
||||
? "%s' maximum total is %d%s, but actual total %s %d%s"
|
||||
.formatted(propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
@ -23,29 +23,38 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
||||
@Override
|
||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||
return sequentiallyValidate(
|
||||
() -> enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig())),
|
||||
() -> enrich(prefix(assetEntity.toShortString(), "bookingItem"), optionallyValidate(assetEntity.getBookingItem())),
|
||||
() -> enrich(prefix(assetEntity.toShortString(), "parentAsset"), optionallyValidate(assetEntity.getParentAsset())),
|
||||
() -> validateSubEntities(assetEntity)
|
||||
() -> validateProperties(assetEntity),
|
||||
() -> optionallyValidate(assetEntity.getBookingItem()),
|
||||
() -> optionallyValidate(assetEntity.getParentAsset()),
|
||||
() -> validateAgainstSubEntities(assetEntity)
|
||||
);
|
||||
}
|
||||
|
||||
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
|
||||
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
||||
}
|
||||
|
||||
private static List<String> optionallyValidate(final HsHostingAssetEntity assetEntity) {
|
||||
return assetEntity != null ?
|
||||
HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validate(assetEntity) :
|
||||
emptyList();
|
||||
return assetEntity != null
|
||||
? enrich(prefix(assetEntity.toShortString(), "parentAsset"),
|
||||
HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validate(assetEntity))
|
||||
: emptyList();
|
||||
}
|
||||
|
||||
private static List<String> optionallyValidate(final HsBookingItemEntity bookingItem) {
|
||||
return bookingItem != null ? HsBookingItemEntityValidatorRegistry.doValidate(bookingItem) : emptyList();
|
||||
return bookingItem != null
|
||||
? enrich(prefix(bookingItem.toShortString(), "bookingItem"),
|
||||
HsBookingItemEntityValidatorRegistry.doValidate(bookingItem))
|
||||
: emptyList();
|
||||
}
|
||||
|
||||
protected List<String> validateSubEntities(final HsHostingAssetEntity assetEntity) {
|
||||
return stream(propertyValidators)
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(assetEntity, prop))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
protected List<String> validateAgainstSubEntities(final HsHostingAssetEntity assetEntity) {
|
||||
return enrich(prefix(assetEntity.toShortString(), "config"),
|
||||
stream(propertyValidators)
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(assetEntity, prop))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
}
|
||||
|
||||
private String validateMaxTotalValue(
|
||||
@ -60,8 +69,8 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
||||
.reduce(0, Integer::sum);
|
||||
final var maxValue = getNonNullIntegerValue(propDef, hostingAsset.getConfig());
|
||||
return totalValue > maxValue
|
||||
? "%s' total is %d%s, thus exceeds max total %s %d%s".formatted(
|
||||
propName, totalValue, propUnit, propName, maxValue, propUnit)
|
||||
? "%s' maximum total is %d%s, but actual total is %s %d%s".formatted(
|
||||
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
@ -109,10 +109,10 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'D-12345:Test-Project:Test Cloud-Server.parentItem.CPUs' total is 5, thus exceeds max total CPUs 4",
|
||||
"'D-12345:Test-Project:Test Cloud-Server.parentItem.RAM' total is 30 GB, thus exceeds max total RAM 20 GB",
|
||||
"'D-12345:Test-Project:Test Cloud-Server.parentItem.SSD' total is 150 GB, thus exceeds max total SSD 100 GB",
|
||||
"'D-12345:Test-Project:Test Cloud-Server.parentItem.Traffic' total is 5500 GB, thus exceeds max total Traffic 5000 GB"
|
||||
"'D-12345:Test-Project:Test Cloud.resources.CPUs' maximum total is 4, but actual total CPUs 5",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.RAM' maximum total is 20 GB, but actual total RAM 30 GB",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.SSD' maximum total is 100 GB, but actual total SSD 150 GB",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.Traffic' maximum total is 5000 GB, but actual total Traffic 5500 GB"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -120,10 +120,10 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'D-12345:Test-Project:null.parentItem.CPUs' total is 5, thus exceeds max total CPUs 4",
|
||||
"'D-12345:Test-Project:null.parentItem.RAM' total is 30 GB, thus exceeds max total RAM 20 GB",
|
||||
"'D-12345:Test-Project:null.parentItem.SSD' total is 150 GB, thus exceeds max total SSD 100 GB",
|
||||
"'D-12345:Test-Project:null.parentItem.Traffic' total is 5500 GB, thus exceeds max total Traffic 5000 GB"
|
||||
"'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs 5",
|
||||
"'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM 30 GB",
|
||||
"'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD 150 GB",
|
||||
"'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic 5500 GB"
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,6 +132,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
// given
|
||||
final var managedWebspaceBookingItem = HsBookingItemEntity.builder()
|
||||
.type(MANAGED_WEBSPACE)
|
||||
.project(project)
|
||||
.caption("test Managed-Webspace")
|
||||
.resources(ofEntries(
|
||||
entry("SSD", 100),
|
||||
@ -166,10 +167,10 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"Multi=1 allows at maximum 25 unix users, but 26 found",
|
||||
"Multi=1 allows at maximum 5 database users, but 6 found",
|
||||
"Multi=1 allows at maximum 5 databases, but 9 found",
|
||||
"Multi=1 allows at maximum 250 databases, but 260 found"
|
||||
"'D-12345:Test-Project:test Managed-Webspace.resources.Multi=1 allows at maximum 25 unix users, but 26 found",
|
||||
"'D-12345:Test-Project:test Managed-Webspace.resources.Multi=1 allows at maximum 5 database users, but 6 found",
|
||||
"'D-12345:Test-Project:test Managed-Webspace.resources.Multi=1 allows at maximum 5 databases, but 9 found",
|
||||
"'D-12345:Test-Project:test Managed-Webspace.resources.Multi=1 allows at maximum 250 databases, but 260 found"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.item.validators;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.util.List.of;
|
||||
@ -13,6 +15,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HsPrivateCloudBookingItemValidatorTest {
|
||||
|
||||
final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder()
|
||||
.debitorNumber(12345)
|
||||
.build();
|
||||
final HsBookingProjectEntity project = HsBookingProjectEntity.builder()
|
||||
.debitor(debitor)
|
||||
.caption("Test-Project")
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void validatesPropertyTotals() {
|
||||
// given
|
||||
@ -57,6 +67,7 @@ class HsPrivateCloudBookingItemValidatorTest {
|
||||
void validatesExceedingPropertyTotals() {
|
||||
// given
|
||||
final var privateCloudBookingItemEntity = HsBookingItemEntity.builder()
|
||||
.project(project)
|
||||
.type(PRIVATE_CLOUD)
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 4),
|
||||
@ -91,10 +102,10 @@ class HsPrivateCloudBookingItemValidatorTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"CPUs' total is 5, thus exceeds max total CPUs 4",
|
||||
"RAM' total is 30 GB, thus exceeds max total RAM 20 GB",
|
||||
"SSD' total is 150 GB, thus exceeds max total SSD 100 GB",
|
||||
"Traffic' total is 5500 GB, thus exceeds max total Traffic 5000 GB"
|
||||
"'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs 5",
|
||||
"'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM 30 GB",
|
||||
"'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD 150 GB",
|
||||
"'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic 5500 GB"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,10 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.extract().header("Location"); // @formatter:on
|
||||
|
||||
// finally, the new asset can be accessed under the generated UUID
|
||||
final var newUserUuid = UUID.fromString(
|
||||
final var newWebspace = UUID.fromString(
|
||||
location.substring(location.lastIndexOf('/') + 1));
|
||||
assertThat(newUserUuid).isNotNull();
|
||||
toCleanup(HsHostingAssetEntity.class, newUserUuid);
|
||||
assertThat(newWebspace).isNotNull();
|
||||
toCleanup(HsHostingAssetEntity.class, newWebspace);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -334,7 +334,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
{
|
||||
"statusPhrase": "Bad Request",
|
||||
"message": "[
|
||||
<<<'MANAGED_WEBSPACE:fir01.bookingItem.Multi=1 allows at maximum 25 unix users, but 26 found
|
||||
<<<'D-1000111:D-1000111 default project:separate ManagedWebspace.resources.Multi=1 allows at maximum 25 unix users, but 26 found
|
||||
<<<]"
|
||||
}
|
||||
""".replaceAll(" +<<<", ""))); // @formatter:on
|
||||
|
@ -25,8 +25,8 @@ class HsHostingAssetEntityValidatorUnitTest {
|
||||
// then
|
||||
assertThat(result).isInstanceOf(ValidationException.class)
|
||||
.hasMessageContaining(
|
||||
"MANAGED_SERVER:vm1234.config.monit_max_ssd_usage is required but missing",
|
||||
"MANAGED_SERVER:vm1234.config.monit_max_cpu_usage is required but missing",
|
||||
"MANAGED_SERVER:vm1234.config.monit_max_ram_usage is required but missing");
|
||||
"'MANAGED_SERVER:vm1234.config.monit_max_ssd_usage' is required but missing",
|
||||
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is required but missing",
|
||||
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is required but missing");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user
variable inlinen