hosting-asset-data-migration (#79)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #79 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
parent
c191af2ea1
commit
4d27a98c9a
32
.aliases
32
.aliases
@ -1,4 +1,4 @@
|
||||
# For using the alias import-office-tables,
|
||||
# For using the alias gw-importOfficeData or gw-importHostingAssets,
|
||||
# copy the file .tc-environment to .environment (ignored by git)
|
||||
# and amend them according to your external DB.
|
||||
|
||||
@ -42,19 +42,29 @@ postgresAutodoc () {
|
||||
}
|
||||
alias postgres-autodoc=postgresAutodoc
|
||||
|
||||
function importOfficeData() {
|
||||
source .tc-environment
|
||||
|
||||
if [ -f .environment ]; then
|
||||
source .environment
|
||||
fi
|
||||
function importLegacyData() {
|
||||
export target=$1
|
||||
if [ -z "$target" ]; then
|
||||
echo "importLegacyData needs target argument, but none was given" >&2
|
||||
else
|
||||
source .tc-environment
|
||||
|
||||
echo "using environment (with ending ';' for use in IntelliJ IDEA):"
|
||||
set | grep ^HSADMINNG_ | sed 's/$/;/'
|
||||
if [ -f .environment ]; then
|
||||
source .environment
|
||||
fi
|
||||
|
||||
./gradlew importOfficeData --rerun
|
||||
echo "using environment (with ending ';' for use in IntelliJ IDEA):"
|
||||
echo "--- BEGIN: ---"
|
||||
set | grep ^HSADMINNG_ | sed 's/$/;/'
|
||||
echo "---- END. ----"
|
||||
echo
|
||||
|
||||
echo ./gradlew $target --rerun
|
||||
./gradlew $target --rerun
|
||||
fi
|
||||
}
|
||||
alias gw-importOfficeData=importOfficeData
|
||||
alias gw-importOfficeData='importLegacyData importOfficeData'
|
||||
alias gw-importHostingAssets='importLegacyData importHostingAssets'
|
||||
|
||||
alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock'
|
||||
alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock'
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -136,4 +136,9 @@ Desktop.ini
|
||||
# ESLint
|
||||
######################
|
||||
.eslintcache
|
||||
|
||||
######################
|
||||
# Project Related
|
||||
######################
|
||||
/.environment*
|
||||
/src/test/resources/migration-prod/*
|
||||
|
12
build.gradle
12
build.gradle
@ -318,7 +318,7 @@ jacocoTestCoverageVerification {
|
||||
|
||||
tasks.register('importOfficeData', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'import'
|
||||
includeTags 'importOfficeData'
|
||||
}
|
||||
|
||||
group 'verification'
|
||||
@ -327,6 +327,16 @@ tasks.register('importOfficeData', Test) {
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
tasks.register('importHostingAssets', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'importHostingAssets'
|
||||
}
|
||||
|
||||
group 'verification'
|
||||
description 'run the import jobs as tests'
|
||||
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
// pitest mutation testing
|
||||
pitest {
|
||||
|
@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
|
||||
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
|
||||
@Entity
|
||||
@Table(name = "hs_booking_debitor_rv")
|
||||
@Table(name = "hs_booking_debitor_xv")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
@ -184,7 +184,9 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Propertie
|
||||
}
|
||||
|
||||
public HsBookingProjectEntity getRelatedProject() {
|
||||
return project != null ? project : parentItem.getRelatedProject();
|
||||
return project != null ? project
|
||||
: parentItem != null ? parentItem.getRelatedProject()
|
||||
: null; // can be the case for technical assets like IP-numbers
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
|
@ -22,6 +22,10 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
|
||||
|
||||
@Override
|
||||
public List<String> validateEntity(final HsBookingItemEntity bookingItem) {
|
||||
// TODO.impl: HsBookingItemType could do this similar to HsHostingAssetType
|
||||
if ( bookingItem.getParentItem() == null && bookingItem.getProject() == null) {
|
||||
return List.of(bookingItem + ".'parentItem' or .'project' expected to be set, but both are null");
|
||||
}
|
||||
return enrich(prefix(bookingItem.toShortString(), "resources"), super.validateProperties(bookingItem));
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,12 @@ class HsCloudServerBookingItemValidator extends HsBookingItemEntityValidator {
|
||||
// @formatter:off
|
||||
booleanProperty("active") .withDefault(true),
|
||||
|
||||
integerProperty("CPUs") .min( 1) .max( 32) .required(),
|
||||
integerProperty("RAM").unit("GB") .min( 1) .max( 128) .required(),
|
||||
integerProperty("SSD").unit("GB") .min( 0) .max( 1000) .step(25).required(), // (1)
|
||||
integerProperty("HDD").unit("GB") .min( 0) .max( 4000) .step(250).withDefault(0),
|
||||
integerProperty("Traffic").unit("GB") .min(250) .max(10000) .step(250).required(),
|
||||
integerProperty("CPU") .min( 1) .max( 32) .required(),
|
||||
integerProperty("RAM").unit("GB") .min( 1) .max( 8192) .required(),
|
||||
integerProperty("SSD").unit("GB") .min( 25) .max( 1000) .step(25).requiresAtLeastOneOf("SDD", "HDD"),
|
||||
integerProperty("HDD").unit("GB") .min(250) .max( 4000) .step(250).requiresAtLeastOneOf("SSD", "HDD"),
|
||||
integerProperty("Traffic").unit("GB") .min(250) .max(10000) .step(250).requiresAtMaxOneOf("Bandwidth", "Traffic"),
|
||||
integerProperty("Bandwidth").unit("GB") .min(250) .max(10000) .step(250).requiresAtMaxOneOf("Bandwidth", "Traffic"), // TODO.spec
|
||||
|
||||
enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional()
|
||||
// @formatter:on
|
||||
|
@ -10,11 +10,12 @@ class HsManagedServerBookingItemValidator extends HsBookingItemEntityValidator {
|
||||
|
||||
HsManagedServerBookingItemValidator() {
|
||||
super(
|
||||
integerProperty("CPUs").min(1).max(32).required(),
|
||||
integerProperty("CPU").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().asTotalLimit().withThreshold(200),
|
||||
integerProperty("HDD").unit("GB").min(0).max(4000).step(250).withDefault(0).asTotalLimit().withThreshold(200),
|
||||
integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required().asTotalLimit().withThreshold(200),
|
||||
integerProperty("SSD").unit("GB").min(25).max(2000).step(25).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit().withThreshold(200),
|
||||
integerProperty("HDD").unit("GB").min(250).max(10000).step(250).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit().withThreshold(200),
|
||||
integerProperty("Traffic").unit("GB").min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit().withThreshold(200),
|
||||
integerProperty("Bandwidth").unit("GB").min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit().withThreshold(200), // TODO.spec
|
||||
enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").withDefault("BASIC"),
|
||||
booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").withDefault(false),
|
||||
booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(),
|
||||
|
@ -23,16 +23,17 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
|
||||
|
||||
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(),
|
||||
integerProperty("SSD").unit("GB").min(1).max(2000).step(1).required(),
|
||||
integerProperty("HDD").unit("GB").min(0).max(10000).step(10).optional(),
|
||||
integerProperty("Traffic").unit("GB").min(10).max(64000).step(10).requiresAtMaxOneOf("Bandwidth", "Traffic"),
|
||||
integerProperty("Bandwidth").unit("GB").min(10).max(1000).step(10).requiresAtMaxOneOf("Bandwidth", "Traffic"), // TODO.spec
|
||||
integerProperty("Multi").min(1).max(100).step(1).withDefault(1)
|
||||
.eachComprising( 25, unixUsers())
|
||||
.eachComprising( 5, databaseUsers())
|
||||
.eachComprising( 5, databases())
|
||||
.eachComprising(250, eMailAddresses()),
|
||||
integerProperty("Daemons").min(0).max(10).withDefault(0),
|
||||
booleanProperty("Online Office Server").optional(),
|
||||
integerProperty("Daemons").min(0).max(16).withDefault(0),
|
||||
booleanProperty("Online Office Server").optional(), // TODO.impl: shorten to "Office"
|
||||
enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").withDefault("BASIC")
|
||||
);
|
||||
}
|
||||
|
@ -7,15 +7,16 @@ class HsPrivateCloudBookingItemValidator extends HsBookingItemEntityValidator {
|
||||
HsPrivateCloudBookingItemValidator() {
|
||||
super(
|
||||
// @formatter:off
|
||||
integerProperty("CPUs") .min( 1).max( 128).required().asTotalLimit(),
|
||||
integerProperty("CPU") .min( 1).max( 128).required().asTotalLimit(),
|
||||
integerProperty("RAM").unit("GB") .min( 1).max( 512).required().asTotalLimit(),
|
||||
integerProperty("SSD").unit("GB") .min( 25).max( 4000).step(25).required().asTotalLimit(),
|
||||
integerProperty("HDD").unit("GB") .min( 0).max(16000).step(250).withDefault(0).asTotalLimit(),
|
||||
integerProperty("Traffic").unit("GB") .min(250).max(40000).step(250).required().asTotalLimit(),
|
||||
integerProperty("SSD").unit("GB") .min( 25).max( 4000).step(25).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit(),
|
||||
integerProperty("HDD").unit("GB") .min(250).max(16000).step(250).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit(),
|
||||
integerProperty("Traffic").unit("GB") .min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit(),
|
||||
integerProperty("Bandwidth").unit("GB") .min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit(), // TODO.spec
|
||||
|
||||
// Alternatively we could specify it similarly to "Multi" option but exclusively counting:
|
||||
// integerProperty("Resource-Points") .min(4).max(100).required()
|
||||
// .each("CPUs").countsAs(64)
|
||||
// .each("CPU").countsAs(64)
|
||||
// .each("RAM").countsAs(64)
|
||||
// .each("SSD").countsAs(18)
|
||||
// .each("HDD").countsAs(2)
|
||||
|
@ -8,6 +8,7 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||
@ -38,6 +39,7 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
@ -108,7 +110,7 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
private HsOfficeContactEntity alarmContact;
|
||||
|
||||
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name="parentassetuuid", referencedColumnName="uuid")
|
||||
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
|
||||
private List<HsHostingAssetEntity> subHostingAssets;
|
||||
|
||||
@Column(name = "identifier")
|
||||
@ -134,12 +136,20 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
this.isLoaded = true;
|
||||
}
|
||||
|
||||
public HsBookingProjectEntity getRelatedProject() {
|
||||
return Optional.ofNullable(bookingItem)
|
||||
.map(HsBookingItemEntity::getRelatedProject)
|
||||
.orElseGet(() -> Optional.ofNullable(parentAsset)
|
||||
.map(HsHostingAssetEntity::getRelatedProject)
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
public PatchableMapWrapper<Object> getConfig() {
|
||||
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config );
|
||||
return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config);
|
||||
}
|
||||
|
||||
public void putConfig(Map<String, Object> newConfig) {
|
||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper; }, config).assign(newConfig);
|
||||
PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config).assign(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,20 +160,19 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
@Override
|
||||
public Object getContextValue(final String propName) {
|
||||
final var v = config.get(propName);
|
||||
if (v!= null) {
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
|
||||
if (bookingItem!=null) {
|
||||
if (bookingItem != null) {
|
||||
return bookingItem.getResources().get(propName);
|
||||
}
|
||||
if (parentAsset!=null && parentAsset.getBookingItem()!=null) {
|
||||
if (parentAsset != null && parentAsset.getBookingItem() != null) {
|
||||
return parentAsset.getBookingItem().getResources().get(propName);
|
||||
}
|
||||
return emptyMap();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
@ -182,9 +191,9 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data?
|
||||
|
||||
.importEntityAlias("bookingItem", HsBookingItemEntity.class, usingDefaultCase(),
|
||||
dependsOnColumn("bookingItemUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
dependsOnColumn("bookingItemUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
|
||||
.importEntityAlias("parentAsset", HsHostingAssetEntity.class, usingDefaultCase(),
|
||||
dependsOnColumn("parentAssetUuid"),
|
||||
@ -202,7 +211,8 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
|
||||
.switchOnColumn("type",
|
||||
.switchOnColumn(
|
||||
"type",
|
||||
inCaseOf("DOMAIN_SETUP", then -> {
|
||||
then.toRole(GLOBAL, GUEST).grantPermission(INSERT);
|
||||
})
|
||||
@ -231,7 +241,14 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
with.permission(SELECT);
|
||||
})
|
||||
|
||||
.limitDiagramTo("asset", "bookingItem", "bookingItem.debitorRel", "parentAsset", "assignedToAsset", "alarmContact", "global");
|
||||
.limitDiagramTo(
|
||||
"asset",
|
||||
"bookingItem",
|
||||
"bookingItem.debitorRel",
|
||||
"parentAsset",
|
||||
"assignedToAsset",
|
||||
"alarmContact",
|
||||
"global");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
@ -18,7 +18,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator
|
||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||
final var prefixPattern =
|
||||
!assetEntity.isLoaded()
|
||||
? assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix()
|
||||
? assetEntity.getRelatedProject().getDebitor().getDefaultPrefix()
|
||||
: "[a-z][a-z0-9][a-z0-9]";
|
||||
return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$");
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
|
||||
|
||||
private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class)
|
||||
.withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
|
||||
.withProp(e -> e.getPartner().toShortString())
|
||||
.withProp(HsOfficeMembershipEntity::getPartner)
|
||||
.withProp(e -> e.getValidity().asString())
|
||||
.withProp(HsOfficeMembershipEntity::getStatus)
|
||||
.quotedValues(false);
|
||||
|
@ -13,10 +13,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
@ -30,7 +32,7 @@ import static org.apache.commons.lang3.ObjectUtils.isArray;
|
||||
public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T> {
|
||||
|
||||
protected static final String[] KEY_ORDER_HEAD = Array.of("propertyName");
|
||||
protected static final String[] KEY_ORDER_TAIL = Array.of("required", "defaultValue", "readOnly", "writeOnly", "computed", "isTotalsValidator", "thresholdPercentage");
|
||||
protected static final String[] KEY_ORDER_TAIL = Array.of("required", "requiresAtLeastOneOf", "requiresAtMaxOneOf", "defaultValue", "readOnly", "writeOnly", "computed", "isTotalsValidator", "thresholdPercentage");
|
||||
protected static final String[] KEY_ORDER = Array.join(KEY_ORDER_HEAD, KEY_ORDER_TAIL);
|
||||
|
||||
final Class<T> type;
|
||||
@ -40,6 +42,8 @@ public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T
|
||||
private final String[] keyOrder;
|
||||
|
||||
private Boolean required;
|
||||
private Set<String> requiresAtLeastOneOf;
|
||||
private Set<String> requiresAtMaxOneOf;
|
||||
private T defaultValue;
|
||||
|
||||
@JsonIgnore
|
||||
@ -100,9 +104,19 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
|
||||
return self();
|
||||
}
|
||||
|
||||
public ValidatableProperty<P, T> optional() {
|
||||
public P optional() {
|
||||
required = FALSE;
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public P requiresAtLeastOneOf(final String... propNames) {
|
||||
requiresAtLeastOneOf = new LinkedHashSet<>(List.of(propNames));
|
||||
return self();
|
||||
}
|
||||
|
||||
public P requiresAtMaxOneOf(final String... propNames) {
|
||||
requiresAtMaxOneOf = new LinkedHashSet<>(List.of(propNames));
|
||||
return self();
|
||||
}
|
||||
|
||||
public P withDefault(final T value) {
|
||||
@ -172,28 +186,57 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]>
|
||||
final var result = new ArrayList<String>();
|
||||
final var props = propsProvider.directProps();
|
||||
final var propValue = props.get(propertyName);
|
||||
|
||||
if (propValue == null) {
|
||||
if (required) {
|
||||
if (required == TRUE) {
|
||||
result.add(propertyName + "' is required but missing");
|
||||
}
|
||||
validateRequiresAtLeastOneOf(result, propsProvider);
|
||||
}
|
||||
if (propValue != null){
|
||||
validateRequiresAtMaxOneOf(result, propsProvider);
|
||||
|
||||
if ( type.isInstance(propValue)) {
|
||||
//noinspection unchecked
|
||||
validate(result, (T) propValue, propsProvider);
|
||||
} else {
|
||||
result.add(propertyName + "' is expected to be of type " + type.getSimpleName() + ", " +
|
||||
"but is of type " + propValue.getClass().getSimpleName() + "");
|
||||
"but is of type " + propValue.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateRequiresAtLeastOneOf(final ArrayList<String> result, final PropertiesProvider propsProvider) {
|
||||
if (requiresAtLeastOneOf != null ) {
|
||||
final var allPropNames = propsProvider.directProps().keySet();
|
||||
final var entriesWithValue = allPropNames.stream()
|
||||
.filter(name -> requiresAtLeastOneOf.contains(name))
|
||||
.count();
|
||||
if (entriesWithValue == 0) {
|
||||
result.add(propertyName + "' is required once in group " + requiresAtLeastOneOf + " but missing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRequiresAtMaxOneOf(final ArrayList<String> result, final PropertiesProvider propsProvider) {
|
||||
if (requiresAtMaxOneOf != null) {
|
||||
final var allPropNames = propsProvider.directProps().keySet();
|
||||
final var entriesWithValue = allPropNames.stream()
|
||||
.filter(name -> requiresAtMaxOneOf.contains(name))
|
||||
.count();
|
||||
if (entriesWithValue > 1) {
|
||||
result.add(propertyName + "' is required at max once in group " + requiresAtMaxOneOf
|
||||
+ " but multiple properties are set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void validate(final List<String> result, final T propValue, final PropertiesProvider propProvider);
|
||||
|
||||
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()" );
|
||||
if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null) {
|
||||
throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,12 @@
|
||||
--changeset hs-booking-debitor-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create view hs_booking_debitor_rv as
|
||||
create view hs_booking_debitor_xv as
|
||||
select debitor.uuid,
|
||||
debitor.version,
|
||||
(partner.partnerNumber::varchar || debitor.debitorNumberSuffix)::numeric as debitorNumber,
|
||||
debitor.defaultPrefix
|
||||
from hs_office_debitor_rv debitor
|
||||
from hs_office_debitor debitor
|
||||
-- RBAC for debitor is sufficient, for faster access we are bypassing RBAC for the join tables
|
||||
join hs_office_relation debitorRel on debitor.debitorReluUid=debitorRel.uuid
|
||||
join hs_office_relation partnerRel on partnerRel.holderUuid=debitorRel.anchorUuid
|
||||
|
@ -33,11 +33,11 @@ begin
|
||||
managedServerUuid := uuid_generate_v4();
|
||||
insert
|
||||
into hs_booking_item (uuid, projectuuid, type, parentitemuuid, caption, validity, resources)
|
||||
values (privateCloudUuid, relatedProject.uuid, 'PRIVATE_CLOUD', null, 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPUs": 10, "RAM": 32, "SSD": 4000, "HDD": 10000, "Traffic": 2000 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'MANAGED_SERVER', privateCloudUuid, 'some ManagedServer', daterange('20230115', '20240415', '[)'), '{ "CPUs": 2, "RAM": 4, "SSD": 500, "Traffic": 500 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'CLOUD_SERVER', privateCloudUuid, 'test CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPUs": 2, "RAM": 4, "SSD": 750, "Traffic": 500 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'CLOUD_SERVER', privateCloudUuid, 'prod CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPUs": 4, "RAM": 16, "SSD": 1000, "Traffic": 500 }'::jsonb),
|
||||
(managedServerUuid, relatedProject.uuid, 'MANAGED_SERVER', null, 'separate ManagedServer', daterange('20221001', null, '[]'), '{ "CPUs": 2, "RAM": 8, "SSD": 500, "Traffic": 500 }'::jsonb),
|
||||
values (privateCloudUuid, relatedProject.uuid, 'PRIVATE_CLOUD', null, 'some PrivateCloud', daterange('20240401', null, '[]'), '{ "CPU": 10, "RAM": 32, "SSD": 4000, "HDD": 10000, "Traffic": 2000 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'MANAGED_SERVER', privateCloudUuid, 'some ManagedServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "RAM": 4, "SSD": 500, "Traffic": 500 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'CLOUD_SERVER', privateCloudUuid, 'test CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "RAM": 4, "SSD": 750, "Traffic": 500 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'CLOUD_SERVER', privateCloudUuid, 'prod CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 4, "RAM": 16, "SSD": 1000, "Traffic": 500 }'::jsonb),
|
||||
(managedServerUuid, relatedProject.uuid, 'MANAGED_SERVER', null, 'separate ManagedServer', daterange('20221001', null, '[]'), '{ "CPU": 2, "RAM": 8, "SSD": 500, "Traffic": 500 }'::jsonb),
|
||||
(uuid_generate_v4(), null, 'MANAGED_WEBSPACE', managedServerUuid, 'some ManagedWebspace', daterange('20221001', null, '[]'), '{ "SSD": 50, "Traffic": 20, "Daemons": 2, "Multi": 4 }'::jsonb),
|
||||
(uuid_generate_v4(), relatedProject.uuid, 'MANAGED_WEBSPACE', null, 'separate ManagedWebspace', daterange('20221001', null, '[]'), '{ "SSD": 100, "Traffic": 50, "Daemons": 0, "Multi": 1 }'::jsonb);
|
||||
end; $$;
|
||||
|
@ -42,7 +42,7 @@ create table if not exists hs_hosting_asset
|
||||
alarmContactUuid uuid null references hs_office_contact(uuid) initially deferred,
|
||||
|
||||
constraint chk_hs_hosting_asset_has_booking_item_or_parent_asset
|
||||
check (bookingItemUuid is not null or parentAssetUuid is not null or type='DOMAIN_SETUP')
|
||||
check (bookingItemUuid is not null or parentAssetUuid is not null or type in ('DOMAIN_SETUP', 'IPV4_NUMBER', 'IPV6_NUMBER'))
|
||||
);
|
||||
--//
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.coopshares",
|
||||
"..hs.office.debitor",
|
||||
"..hs.office.membership",
|
||||
"..hs.office.migration",
|
||||
"..hs.migration",
|
||||
"..hs.office.partner",
|
||||
"..hs.office.person",
|
||||
"..hs.office.relation",
|
||||
@ -156,6 +156,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.(*)..",
|
||||
"..hs.booking.(*)..",
|
||||
"..hs.hosting.(*)..",
|
||||
"..hs.migration",
|
||||
"..rbac.rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest
|
||||
);
|
||||
|
||||
@ -167,7 +168,8 @@ public class ArchitectureTest {
|
||||
.resideInAnyPackage(
|
||||
"..hs.booking.(*)..",
|
||||
"..hs.hosting.(*)..",
|
||||
"..hs.validation" // TODO.impl: Some Validators need to be refactored to booking package.
|
||||
"..hs.validation", // TODO.impl: Some Validators need to be refactored to booking package.
|
||||
"..hs.migration.."
|
||||
);
|
||||
|
||||
@ArchTest
|
||||
@ -177,7 +179,8 @@ public class ArchitectureTest {
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage(
|
||||
"..hs.hosting.(*)..",
|
||||
"..hs.booking.(*).." // TODO.impl: fix this cyclic dependency
|
||||
"..hs.booking.(*)..", // TODO.impl: fix this cyclic dependency
|
||||
"..hs.migration.."
|
||||
);
|
||||
|
||||
@ArchTest
|
||||
@ -189,7 +192,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.bankaccount..",
|
||||
"..hs.office.sepamandate..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
@ -199,7 +202,7 @@ public class ArchitectureTest {
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.sepamandate..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
@ -212,7 +215,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.migration..",
|
||||
"..hs.migration..",
|
||||
"..hs.hosting.asset.."
|
||||
);
|
||||
|
||||
@ -227,7 +230,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.migration..")
|
||||
"..hs.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
|
||||
@ -239,7 +242,7 @@ public class ArchitectureTest {
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.relation..",
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.migration..")
|
||||
"..hs.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
@ArchTest
|
||||
@ -251,7 +254,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.partner..",
|
||||
"..hs.office.debitor..",
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.migration..")
|
||||
"..hs.migration..")
|
||||
.orShould().haveNameNotMatching(".*Test$");
|
||||
|
||||
@ArchTest
|
||||
@ -263,7 +266,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.membership..",
|
||||
"..hs.office.coopassets..",
|
||||
"..hs.office.coopshares..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
@ -272,7 +275,7 @@ public class ArchitectureTest {
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.coopassets..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
@ -281,14 +284,14 @@ public class ArchitectureTest {
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.coopshares..",
|
||||
"..hs.office.migration..");
|
||||
"..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
public static final ArchRule hsOfficeMigrationPackageRule = classes()
|
||||
.that().resideInAPackage("..hs.office.migration..")
|
||||
.that().resideInAPackage("..hs.migration..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage("..hs.office.migration..");
|
||||
.resideInAnyPackage("..hs.migration..");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -101,7 +101,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"resources": {
|
||||
"RAM": 8,
|
||||
"SSD": 500,
|
||||
"CPUs": 2,
|
||||
"CPU": 2,
|
||||
"Traffic": 500
|
||||
}
|
||||
},
|
||||
@ -114,7 +114,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"HDD": 10000,
|
||||
"RAM": 32,
|
||||
"SSD": 4000,
|
||||
"CPUs": 10,
|
||||
"CPU": 10,
|
||||
"Traffic": 2000
|
||||
}
|
||||
}
|
||||
@ -148,7 +148,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validTo": "{validTo}",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{projectUuid}", givenProject.getUuid().toString())
|
||||
@ -166,7 +166,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{today}",
|
||||
"validTo": "{todayPlus1Month}",
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{today}", LocalDate.now().toString())
|
||||
@ -267,7 +267,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"resources": {
|
||||
"RAM": 8,
|
||||
"SSD": 500,
|
||||
"CPUs": 2,
|
||||
"CPU": 2,
|
||||
"Traffic": 500
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ class HsBookingItemControllerRestTest {
|
||||
"caption": "some new booking",
|
||||
"validTo": "{validTo}",
|
||||
"garbage": "should not be accepted",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{projectUuid}", givenProjectUuid.toString())
|
||||
@ -108,7 +108,7 @@ class HsBookingItemControllerRestTest {
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{today}",
|
||||
"validTo": "{todayPlus1Month}",
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{today}", LocalDate.now().toString())
|
||||
@ -141,7 +141,7 @@ class HsBookingItemControllerRestTest {
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{validFrom}",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{projectUuid}", givenProjectUuid.toString())
|
||||
@ -159,7 +159,7 @@ class HsBookingItemControllerRestTest {
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{today}",
|
||||
"validTo": null,
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
"resources": { "CPU": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{today}", LocalDate.now().toString())
|
||||
|
@ -25,7 +25,7 @@ class HsBookingItemEntityUnitTest {
|
||||
.type(HsBookingItemType.CLOUD_SERVER)
|
||||
.caption("some caption")
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("SSD-storage", 512),
|
||||
entry("HDD-storage", 2048)))
|
||||
.validity(toPostgresDateRange(GIVEN_VALID_FROM, GIVEN_VALID_TO))
|
||||
@ -53,7 +53,7 @@ class HsBookingItemEntityUnitTest {
|
||||
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
||||
final var result = givenBookingItem.toString();
|
||||
|
||||
assertThat(result).isEqualToIgnoringWhitespace("HsBookingItemEntity(D-1234500:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { \"CPUs\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })");
|
||||
assertThat(result).isEqualToIgnoringWhitespace("HsBookingItemEntity(D-1234500:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { \"CPU\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -171,8 +171,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
allTheseBookingItemsAreReturned(
|
||||
result,
|
||||
"HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )",
|
||||
"HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SSD: 500, Traffic: 500 } )",
|
||||
"HsBookingItemEntity(D-1000212:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )");
|
||||
"HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPU: 2, RAM: 8, SSD: 500, Traffic: 500 } )",
|
||||
"HsBookingItemEntity(D-1000212:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )");
|
||||
assertThat(result.stream().filter(bi -> bi.getRelatedHostingAsset()!=null).findAny())
|
||||
.as("at least one relatedProject expected, but none found => fetching relatedProject does not work")
|
||||
.isNotEmpty();
|
||||
@ -194,8 +194,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
exactlyTheseBookingItemsAreReturned(
|
||||
result,
|
||||
"HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )",
|
||||
"HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SSD: 500, Traffic: 500 } )",
|
||||
"HsBookingItemEntity(D-1000111:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )");
|
||||
"HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPU: 2, RAM: 8, SSD: 500, Traffic: 500 } )",
|
||||
"HsBookingItemEntity(D-1000111:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )");
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
context("superuser-alex@hostsharing.net");
|
||||
final var foundBookingItem = em.find(HsBookingItemEntity.class, givenBookingItemUuid);
|
||||
foundBookingItem.getResources().put("CPUs", 2);
|
||||
foundBookingItem.getResources().put("CPU", 2);
|
||||
foundBookingItem.getResources().remove("SSD-storage");
|
||||
foundBookingItem.getResources().put("HSD-storage", 2048);
|
||||
foundBookingItem.setValidity(Range.closedOpen(
|
||||
@ -336,7 +336,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
.validity(Range.closedOpen(
|
||||
LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01")))
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 1),
|
||||
entry("CPU", 1),
|
||||
entry("SSD-storage", 256)))
|
||||
.build();
|
||||
|
||||
|
@ -17,7 +17,7 @@ public class TestHsBookingItem {
|
||||
.type(HsBookingItemType.CLOUD_SERVER)
|
||||
.caption("test cloud server booking item")
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 4),
|
||||
entry("SSD", 50),
|
||||
entry("Traffic", 250)
|
||||
@ -30,7 +30,7 @@ public class TestHsBookingItem {
|
||||
.type(HsBookingItemType.MANAGED_SERVER)
|
||||
.caption("test project booking item")
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 4),
|
||||
entry("SSD", 50),
|
||||
entry("Traffic", 250)
|
||||
|
@ -38,7 +38,7 @@ class HsBookingItemEntityValidatorUnitTest {
|
||||
// then
|
||||
assertThat(result).isInstanceOf(ValidationException.class)
|
||||
.hasMessageContaining(
|
||||
"'D-12345:test project:Test-Server.resources.CPUs' is required but missing",
|
||||
"'D-12345:test project:Test-Server.resources.CPU' is required but missing",
|
||||
"'D-12345:test project:Test-Server.resources.RAM' is required but missing",
|
||||
"'D-12345:test project:Test-Server.resources.SSD' is required but missing",
|
||||
"'D-12345:test project:Test-Server.resources.Traffic' is required but missing");
|
||||
|
@ -33,7 +33,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
.project(project)
|
||||
.caption("Test-Server")
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 25),
|
||||
entry("SSD", 25),
|
||||
entry("Traffic", 250),
|
||||
@ -56,11 +56,12 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
// then
|
||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||
"{type=boolean, propertyName=active, defaultValue=true}",
|
||||
"{type=integer, propertyName=CPUs, min=1, max=32, required=true}",
|
||||
"{type=integer, propertyName=RAM, unit=GB, min=1, max=128, required=true}",
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=0, max=1000, step=25, required=true}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=0, max=4000, step=250, defaultValue=0}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, required=true}",
|
||||
"{type=integer, propertyName=CPU, min=1, max=32, required=true}",
|
||||
"{type=integer, propertyName=RAM, unit=GB, min=1, max=8192, required=true}",
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=25, max=1000, step=25, requiresAtLeastOneOf=[SDD, HDD]}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=250, max=4000, step=250, requiresAtLeastOneOf=[SSD, HDD]}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic]}",
|
||||
"{type=integer, propertyName=Bandwidth, unit=GB, min=250, max=10000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic]}",
|
||||
"{type=enumeration, propertyName=SLA-Infrastructure, values=[BASIC, EXT8H, EXT4H, EXT2H]}");
|
||||
}
|
||||
|
||||
@ -71,7 +72,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
.type(CLOUD_SERVER)
|
||||
.caption("Test Cloud-Server")
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 10),
|
||||
entry("SSD", 50),
|
||||
entry("Traffic", 2500)
|
||||
@ -81,7 +82,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
.type(MANAGED_SERVER)
|
||||
.caption("Test Managed-Server")
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 3),
|
||||
entry("CPU", 3),
|
||||
entry("RAM", 20),
|
||||
entry("SSD", 100),
|
||||
entry("Traffic", 3000)
|
||||
@ -92,7 +93,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
.project(project)
|
||||
.caption("Test Cloud")
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 4),
|
||||
entry("CPU", 4),
|
||||
entry("RAM", 20),
|
||||
entry("SSD", 100),
|
||||
entry("Traffic", 5000)
|
||||
@ -110,7 +111,7 @@ class HsCloudServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'D-12345:Test-Project:Test Cloud.resources.CPUs' maximum total is 4, but actual total CPUs is 5",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.CPU' maximum total is 4, but actual total CPU is 5",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB",
|
||||
"'D-12345:Test-Project:Test Cloud.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB"
|
||||
|
@ -40,7 +40,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
.type(MANAGED_SERVER)
|
||||
.project(project)
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 25),
|
||||
entry("SSD", 25),
|
||||
entry("Traffic", 250),
|
||||
@ -63,11 +63,12 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||
"{type=integer, propertyName=CPUs, min=1, max=32, required=true}",
|
||||
"{type=integer, propertyName=CPU, min=1, max=32, required=true}",
|
||||
"{type=integer, propertyName=RAM, unit=GB, min=1, max=128, required=true}",
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=25, max=1000, step=25, required=true, isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=0, max=4000, step=250, defaultValue=0, isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, required=true, isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=25, max=2000, step=25, requiresAtLeastOneOf=[SSD, HDD], isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=250, max=10000, step=250, requiresAtLeastOneOf=[SSD, HDD], isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=250, max=64000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic], isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=integer, propertyName=Bandwidth, unit=GB, min=250, max=64000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic], isTotalsValidator=true, thresholdPercentage=200}",
|
||||
"{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT8H, EXT4H, EXT2H], defaultValue=BASIC}",
|
||||
"{type=boolean, propertyName=SLA-EMail}", // TODO.impl: falseIf-validation is missing in output
|
||||
"{type=boolean, propertyName=SLA-Maria}",
|
||||
@ -82,7 +83,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||
.type(CLOUD_SERVER)
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 10),
|
||||
entry("SSD", 50),
|
||||
entry("Traffic", 2500)
|
||||
@ -91,7 +92,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder()
|
||||
.type(MANAGED_SERVER)
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 3),
|
||||
entry("CPU", 3),
|
||||
entry("RAM", 20),
|
||||
entry("SSD", 100),
|
||||
entry("Traffic", 3000)
|
||||
@ -101,7 +102,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
.type(PRIVATE_CLOUD)
|
||||
.project(project)
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 4),
|
||||
entry("CPU", 4),
|
||||
entry("RAM", 20),
|
||||
entry("SSD", 100),
|
||||
entry("Traffic", 5000)
|
||||
@ -120,7 +121,7 @@ class HsManagedServerBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs is 5",
|
||||
"'D-12345:Test-Project:null.resources.CPU' maximum total is 4, but actual total CPU is 5",
|
||||
"'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB",
|
||||
"'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB",
|
||||
"'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB"
|
||||
|
@ -29,7 +29,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest {
|
||||
.project(project)
|
||||
.caption("Test Managed-Webspace")
|
||||
.resources(Map.ofEntries(
|
||||
entry("CPUs", 2),
|
||||
entry("CPU", 2),
|
||||
entry("RAM", 25),
|
||||
entry("Traffic", 250),
|
||||
entry("SLA-EMail", true)
|
||||
@ -41,7 +41,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(result).containsExactlyInAnyOrder(
|
||||
"'D-12345:Test-Project:Test Managed-Webspace.resources.CPUs' is not expected but is set to '2'",
|
||||
"'D-12345:Test-Project:Test Managed-Webspace.resources.CPU' is not expected but is set to '2'",
|
||||
"'D-12345:Test-Project:Test Managed-Webspace.resources.RAM' is not expected but is set to '25'",
|
||||
"'D-12345:Test-Project:Test Managed-Webspace.resources.SSD' is required but missing",
|
||||
"'D-12345:Test-Project:Test Managed-Webspace.resources.SLA-EMail' is not expected but is set to 'true'"
|
||||
@ -55,11 +55,12 @@ class HsManagedWebspaceBookingItemValidatorUnitTest {
|
||||
|
||||
// then
|
||||
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=1, max=100, step=1, required=true}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=0, max=250, step=10}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=10, max=1000, step=10, required=true}",
|
||||
"{type=integer, propertyName=SSD, unit=GB, min=1, max=2000, step=1, required=true}",
|
||||
"{type=integer, propertyName=HDD, unit=GB, min=0, max=10000, step=10}",
|
||||
"{type=integer, propertyName=Traffic, unit=GB, min=10, max=64000, step=10, requiresAtMaxOneOf=[Bandwidth, Traffic]}",
|
||||
"{type=integer, propertyName=Bandwidth, unit=GB, min=10, max=1000, step=10, requiresAtMaxOneOf=[Bandwidth, Traffic]}",
|
||||
"{type=integer, propertyName=Multi, min=1, max=100, step=1, defaultValue=1}",
|
||||
"{type=integer, propertyName=Daemons, min=0, max=10, defaultValue=0}",
|
||||
"{type=integer, propertyName=Daemons, min=0, max=16, defaultValue=0}",
|
||||
"{type=boolean, propertyName=Online Office Server}",
|
||||
"{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT24H], defaultValue=BASIC}");
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import static java.util.Map.ofEntries;
|
||||
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.PRIVATE_CLOUD;
|
||||
import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.TEST_PROJECT;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HsPrivateCloudBookingItemValidatorUnitTest {
|
||||
@ -28,9 +29,10 @@ class HsPrivateCloudBookingItemValidatorUnitTest {
|
||||
// given
|
||||
final var privateCloudBookingItemEntity = HsBookingItemEntity.builder()
|
||||
.type(PRIVATE_CLOUD)
|
||||
.project(TEST_PROJECT)
|
||||
.caption("myPC")
|
||||
.resources(ofEntries(
|
||||
entry("CPUs", 4),
|
||||
entry("CPU", 4),
|
||||
entry("RAM", 20),
|
||||