From 48434f845eba8245e275b5568bb516ac5f02c551 Mon Sep 17 00:00:00 2001 From: amentis Date: Wed, 24 Jul 2024 14:10:40 +0300 Subject: [PATCH] add Usage Limit periodicity --- .../enums/UsageLimitPeriodicityRange.java | 30 ++++++++ .../enums/accounting/AccountingValueType.java | 2 +- .../types/usagelimit/DefinitionEntity.java | 35 +++++++++ .../org/opencdmp/data/UsageLimitEntity.java | 16 ++++ ...tryCreatedIntegrationEventHandlerImpl.java | 3 +- .../java/org/opencdmp/model/UsageLimit.java | 12 +++ .../model/builder/UsageLimitBuilder.java | 18 ++++- .../builder/usagelimit/DefinitionBuilder.java | 55 ++++++++++++++ .../model/censorship/UsageLimitsCensor.java | 4 + .../usagelimit/DefinitionCensor.java | 45 +++++++++++ .../model/persist/UsageLimitPersist.java | 25 +++++- .../persist/usagelimit/DefinitionPersist.java | 76 +++++++++++++++++++ .../opencdmp/model/usagelimit/Definition.java | 28 +++++++ .../org/opencdmp/query/UsageLimitQuery.java | 3 + .../accounting/AccountingProperties.java | 12 ++- .../service/accounting/AccountingService.java | 6 +- .../accounting/AccountingServiceImpl.java | 30 +++++--- .../service/usagelimit/UsageLimitService.java | 3 +- .../usagelimit/UsageLimitServiceImpl.java | 37 +++++++-- ...troller.java => UsageLimitController.java} | 6 +- .../src/main/resources/config/accounting.yml | 1 + .../enum/usage-limit-periodicity-range.ts | 4 + .../app/core/model/usage-limit/usage-limit.ts | 15 ++++ .../services/utilities/enum-utils.service.ts | 9 +++ .../editor/usage-limit-editor.component.html | 17 +++++ .../editor/usage-limit-editor.component.ts | 2 + .../editor/usage-limit-editor.model.ts | 62 ++++++++++++++- .../editor/usage-limit-editor.resolver.ts | 4 +- 28 files changed, 528 insertions(+), 32 deletions(-) create mode 100644 backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitPeriodicityRange.java create mode 100644 backend/core/src/main/java/org/opencdmp/commons/types/usagelimit/DefinitionEntity.java create mode 100644 backend/core/src/main/java/org/opencdmp/model/builder/usagelimit/DefinitionBuilder.java create mode 100644 backend/core/src/main/java/org/opencdmp/model/censorship/usagelimit/DefinitionCensor.java create mode 100644 backend/core/src/main/java/org/opencdmp/model/persist/usagelimit/DefinitionPersist.java create mode 100644 backend/core/src/main/java/org/opencdmp/model/usagelimit/Definition.java rename backend/web/src/main/java/org/opencdmp/controllers/{UsageFilterController.java => UsageLimitController.java} (98%) create mode 100644 frontend/src/app/core/common/enum/usage-limit-periodicity-range.ts diff --git a/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitPeriodicityRange.java b/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitPeriodicityRange.java new file mode 100644 index 000000000..c3e417271 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitPeriodicityRange.java @@ -0,0 +1,30 @@ +package org.opencdmp.commons.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import org.opencdmp.data.converters.enums.DatabaseEnum; + +import java.util.Map; + +public enum UsageLimitPeriodicityRange implements DatabaseEnum { + + Monthly((short) 0), + Yearly((short) 1); + + private final Short value; + + UsageLimitPeriodicityRange(Short value) { + this.value = value; + } + + @JsonValue + public Short getValue() { + return value; + } + + private static final Map map = EnumUtils.getEnumValueMap(UsageLimitPeriodicityRange.class); + + public static UsageLimitPeriodicityRange of(Short i) { + return map.get(i); + } + +} diff --git a/backend/core/src/main/java/org/opencdmp/commons/enums/accounting/AccountingValueType.java b/backend/core/src/main/java/org/opencdmp/commons/enums/accounting/AccountingValueType.java index 3a855062d..e4a46e58e 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/enums/accounting/AccountingValueType.java +++ b/backend/core/src/main/java/org/opencdmp/commons/enums/accounting/AccountingValueType.java @@ -10,7 +10,7 @@ public enum AccountingValueType implements DatabaseEnum { Plus((short) 0), Minus((short) 1), - Reset((short) 3); + Reset((short) 2); private final Short value; diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/usagelimit/DefinitionEntity.java b/backend/core/src/main/java/org/opencdmp/commons/types/usagelimit/DefinitionEntity.java new file mode 100644 index 000000000..d765d6571 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/commons/types/usagelimit/DefinitionEntity.java @@ -0,0 +1,35 @@ +package org.opencdmp.commons.types.usagelimit; + +import jakarta.xml.bind.annotation.*; +import org.opencdmp.commons.enums.ReferenceFieldDataType; +import org.opencdmp.commons.enums.UsageLimitPeriodicityRange; +import org.opencdmp.commons.types.reference.FieldEntity; + +import java.util.List; + +@XmlRootElement(name = "definition") +@XmlAccessorType(XmlAccessType.FIELD) +public class DefinitionEntity { + + @XmlAttribute(name = "hasPeriodicity") + private Boolean hasPeriodicity; + + @XmlAttribute(name = "periodicityRange") + private UsageLimitPeriodicityRange periodicityRange; + + public Boolean getHasPeriodicity() { + return hasPeriodicity; + } + + public void setHasPeriodicity(Boolean hasPeriodicity) { + this.hasPeriodicity = hasPeriodicity; + } + + public UsageLimitPeriodicityRange getPeriodicityRange() { + return periodicityRange; + } + + public void setPeriodicityRange(UsageLimitPeriodicityRange periodicityRange) { + this.periodicityRange = periodicityRange; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/data/UsageLimitEntity.java b/backend/core/src/main/java/org/opencdmp/data/UsageLimitEntity.java index a848ededf..bbda3a76f 100644 --- a/backend/core/src/main/java/org/opencdmp/data/UsageLimitEntity.java +++ b/backend/core/src/main/java/org/opencdmp/data/UsageLimitEntity.java @@ -1,11 +1,13 @@ package org.opencdmp.data; import jakarta.persistence.*; +import org.hibernate.annotations.Type; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.UsageLimitTargetMetric; import org.opencdmp.data.converters.enums.IsActiveConverter; import org.opencdmp.data.converters.enums.UsageLimitTargetMetricConverter; import org.opencdmp.data.tenant.TenantScopedBaseEntity; +import org.opencdmp.data.types.SQLXMLType; import java.time.Instant; import java.util.UUID; @@ -39,6 +41,12 @@ public class UsageLimitEntity extends TenantScopedBaseEntity { public static final String _value = "value"; + @Type(SQLXMLType.class) + @Column(name = "definition", columnDefinition = "xml") + private String definition; + + public static final String _definition = "definition"; + @Column(name = "is_active", nullable = false) @Convert(converter = IsActiveConverter.class) private IsActive isActive; @@ -87,6 +95,14 @@ public class UsageLimitEntity extends TenantScopedBaseEntity { this.value = value; } + public String getDefinition() { + return definition; + } + + public void setDefinition(String definition) { + this.definition = definition; + } + public IsActive getIsActive() { return isActive; } diff --git a/backend/core/src/main/java/org/opencdmp/integrationevent/outbox/accountingentrycreated/AccountingEntryCreatedIntegrationEventHandlerImpl.java b/backend/core/src/main/java/org/opencdmp/integrationevent/outbox/accountingentrycreated/AccountingEntryCreatedIntegrationEventHandlerImpl.java index ba4f3d17c..688b61fea 100644 --- a/backend/core/src/main/java/org/opencdmp/integrationevent/outbox/accountingentrycreated/AccountingEntryCreatedIntegrationEventHandlerImpl.java +++ b/backend/core/src/main/java/org/opencdmp/integrationevent/outbox/accountingentrycreated/AccountingEntryCreatedIntegrationEventHandlerImpl.java @@ -49,7 +49,8 @@ public class AccountingEntryCreatedIntegrationEventHandlerImpl implements Accoun event.setType(valueType); event.setResource(tenantCode); event.setUserId(subjectId); - event.setValue(1.0); + if (valueType != null && valueType.equals(AccountingValueType.Reset)) event.setValue(0.0); + else event.setValue(1.0); event.setTenant(tenantId); this.handle(event); diff --git a/backend/core/src/main/java/org/opencdmp/model/UsageLimit.java b/backend/core/src/main/java/org/opencdmp/model/UsageLimit.java index 72d00761c..908217c67 100644 --- a/backend/core/src/main/java/org/opencdmp/model/UsageLimit.java +++ b/backend/core/src/main/java/org/opencdmp/model/UsageLimit.java @@ -2,6 +2,7 @@ package org.opencdmp.model; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.UsageLimitTargetMetric; +import org.opencdmp.model.usagelimit.Definition; import java.time.Instant; import java.util.UUID; @@ -23,6 +24,9 @@ public class UsageLimit { private IsActive isActive; public static final String _isActive = "isActive"; + private Definition definition; + public static final String _definition = "definition"; + private Instant createdAt; public static final String _createdAt = "createdAt"; @@ -67,6 +71,14 @@ public class UsageLimit { this.value = value; } + public Definition getDefinition() { + return definition; + } + + public void setDefinition(Definition definition) { + this.definition = definition; + } + public IsActive getIsActive() { return isActive; } diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/UsageLimitBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/UsageLimitBuilder.java index 764eefe62..ade29f572 100644 --- a/backend/core/src/main/java/org/opencdmp/model/builder/UsageLimitBuilder.java +++ b/backend/core/src/main/java/org/opencdmp/model/builder/UsageLimitBuilder.java @@ -1,14 +1,18 @@ package org.opencdmp.model.builder; +import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.DataLogEntry; import gr.cite.tools.logging.LoggerService; import org.opencdmp.authorization.AuthorizationFlags; +import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.scope.tenant.TenantScope; +import org.opencdmp.commons.types.usagelimit.DefinitionEntity; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.UsageLimitEntity; import org.opencdmp.model.UsageLimit; +import org.opencdmp.model.builder.usagelimit.DefinitionBuilder; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -22,15 +26,19 @@ import java.util.*; public class UsageLimitBuilder extends BaseBuilder { private final TenantScope tenantScope; + private final XmlHandlingService xmlHandlingService; + private final BuilderFactory builderFactory; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); @Autowired public UsageLimitBuilder( - ConventionService conventionService, - TenantScope tenantScope) { + ConventionService conventionService, + TenantScope tenantScope, XmlHandlingService xmlHandlingService, BuilderFactory builderFactory) { super(conventionService, new LoggerService(LoggerFactory.getLogger(UsageLimitBuilder.class))); this.tenantScope = tenantScope; + this.xmlHandlingService = xmlHandlingService; + this.builderFactory = builderFactory; } public UsageLimitBuilder authorize(EnumSet values) { @@ -45,6 +53,8 @@ public class UsageLimitBuilder extends BaseBuilder if (fields == null || data == null || fields.isEmpty()) return new ArrayList<>(); + FieldSet definitionFields = fields.extractPrefixed(this.asPrefix(UsageLimit._definition)); + List models = new ArrayList<>(); for (UsageLimitEntity d : data) { UsageLimit m = new UsageLimit(); @@ -64,6 +74,10 @@ public class UsageLimitBuilder extends BaseBuilder m.setIsActive(d.getIsActive()); if (fields.hasField(this.asIndexer(UsageLimit._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); + if (!definitionFields.isEmpty() && d.getDefinition() != null) { + DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, d.getDefinition()); + m.setDefinition(this.builderFactory.builder(DefinitionBuilder.class).authorize(this.authorize).build(definitionFields, definition)); + } if (fields.hasField(this.asIndexer(UsageLimit._belongsToCurrentTenant))) m.setBelongsToCurrentTenant(this.getBelongsToCurrentTenant(d, this.tenantScope)); models.add(m); diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/usagelimit/DefinitionBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/usagelimit/DefinitionBuilder.java new file mode 100644 index 000000000..ae4e49084 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/builder/usagelimit/DefinitionBuilder.java @@ -0,0 +1,55 @@ +package org.opencdmp.model.builder.usagelimit; + +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.opencdmp.authorization.AuthorizationFlags; +import org.opencdmp.commons.types.usagelimit.DefinitionEntity; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.model.builder.BaseBuilder; +import org.opencdmp.model.usagelimit.Definition; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component("usagelimitdefinitionbuilder") +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class DefinitionBuilder extends BaseBuilder { + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + @Autowired + public DefinitionBuilder( + ConventionService conventionService) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(DefinitionBuilder.class))); + } + + public DefinitionBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + public List build(FieldSet fields, List data) throws MyApplicationException { + this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); + this.logger.trace(new DataLogEntry("requested fields", fields)); + if (fields == null || data == null || fields.isEmpty()) + return new ArrayList<>(); + + List models = new ArrayList<>(); + for (DefinitionEntity d : data) { + Definition m = new Definition(); + if (fields.hasField(this.asIndexer(Definition._periodicityRange))) m.setPeriodicityRange(d.getPeriodicityRange()); + if (fields.hasField(this.asIndexer(Definition._hasPeriodicity))) m.setHasPeriodicity(d.getHasPeriodicity()); + + models.add(m); + } + this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); + return models; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/censorship/UsageLimitsCensor.java b/backend/core/src/main/java/org/opencdmp/model/censorship/UsageLimitsCensor.java index b26d158f6..f2d0c686b 100644 --- a/backend/core/src/main/java/org/opencdmp/model/censorship/UsageLimitsCensor.java +++ b/backend/core/src/main/java/org/opencdmp/model/censorship/UsageLimitsCensor.java @@ -7,6 +7,8 @@ import gr.cite.tools.logging.DataLogEntry; import gr.cite.tools.logging.LoggerService; import org.opencdmp.authorization.Permission; import org.opencdmp.convention.ConventionService; +import org.opencdmp.model.UsageLimit; +import org.opencdmp.model.censorship.usagelimit.DefinitionCensor; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; @@ -35,6 +37,8 @@ public class UsageLimitsCensor extends BaseCensor { return; this.authService.authorizeForce(Permission.BrowseUsageLimit); + FieldSet definitionFields = fields.extractPrefixed(this.asIndexerPrefix(UsageLimit._definition)); + this.censorFactory.censor(DefinitionCensor.class).censor(definitionFields); } } diff --git a/backend/core/src/main/java/org/opencdmp/model/censorship/usagelimit/DefinitionCensor.java b/backend/core/src/main/java/org/opencdmp/model/censorship/usagelimit/DefinitionCensor.java new file mode 100644 index 000000000..7928ae736 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/censorship/usagelimit/DefinitionCensor.java @@ -0,0 +1,45 @@ +package org.opencdmp.model.censorship.usagelimit; + +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.DataLogEntry; +import gr.cite.tools.logging.LoggerService; +import org.opencdmp.authorization.Permission; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.model.censorship.BaseCensor; +import org.opencdmp.model.censorship.reference.FieldCensor; +import org.opencdmp.model.reference.Definition; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("usagelimitdefinitioncensor") +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class DefinitionCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DefinitionCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public DefinitionCensor(ConventionService conventionService, + AuthorizationService authService, + CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowseUsageLimit); + } + +} diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/UsageLimitPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/UsageLimitPersist.java index 7d5ea711b..2fc6324dc 100644 --- a/backend/core/src/main/java/org/opencdmp/model/persist/UsageLimitPersist.java +++ b/backend/core/src/main/java/org/opencdmp/model/persist/UsageLimitPersist.java @@ -1,11 +1,13 @@ package org.opencdmp.model.persist; +import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.specification.Specification; import org.opencdmp.commons.enums.UsageLimitTargetMetric; import org.opencdmp.commons.validation.BaseValidator; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.UsageLimitEntity; import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.opencdmp.model.persist.usagelimit.DefinitionPersist; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; @@ -30,6 +32,9 @@ public class UsageLimitPersist { private Long value; public static final String _value = "value"; + private DefinitionPersist definition; + public static final String _definition = "definition"; + private String hash; public static final String _hash = "hash"; @@ -65,6 +70,14 @@ public class UsageLimitPersist { this.value = value; } + public DefinitionPersist getDefinition() { + return definition; + } + + public void setDefinition(DefinitionPersist definition) { + this.definition = definition; + } + public String getHash() { return this.hash; } @@ -81,9 +94,12 @@ public class UsageLimitPersist { private final MessageSource messageSource; - protected UsageLimitPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + private final ValidatorFactory validatorFactory; + + protected UsageLimitPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { super(conventionService, errors); this.messageSource = messageSource; + this.validatorFactory = validatorFactory; } @Override @@ -117,7 +133,12 @@ public class UsageLimitPersist { .failOn(UsageLimitPersist._value).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UsageLimitPersist._value}, LocaleContextHolder.getLocale())), this.spec() .must(() -> item.getValue() > 0) - .failOn(UsageLimitPersist._value).failWith(this.messageSource.getMessage("Validation_UnexpectedValue", new Object[]{UsageLimitPersist._value}, LocaleContextHolder.getLocale())) + .failOn(UsageLimitPersist._value).failWith(this.messageSource.getMessage("Validation_UnexpectedValue", new Object[]{UsageLimitPersist._value}, LocaleContextHolder.getLocale())), + this.refSpec() + .iff(() -> !this.isNull(item.getDefinition())) + .on(UsageLimitPersist._definition) + .over(item.getDefinition()) + .using(() -> this.validatorFactory.validator(org.opencdmp.model.persist.usagelimit.DefinitionPersist.DefinitionPersistValidator.class)) ); } } diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/usagelimit/DefinitionPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/usagelimit/DefinitionPersist.java new file mode 100644 index 000000000..142d87aa1 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/usagelimit/DefinitionPersist.java @@ -0,0 +1,76 @@ +package org.opencdmp.model.persist.usagelimit; + +import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.enums.UsageLimitPeriodicityRange; +import org.opencdmp.commons.validation.BaseValidator; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Scope; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +public class DefinitionPersist { + + private Boolean hasPeriodicity; + public final static String _hasPeriodicity = "hasPeriodicity"; + + private UsageLimitPeriodicityRange periodicityRange; + public final static String _periodicityRange = "periodicityRange"; + + public Boolean getHasPeriodicity() { + return hasPeriodicity; + } + + public void setHasPeriodicity(Boolean hasPeriodicity) { + this.hasPeriodicity = hasPeriodicity; + } + + public UsageLimitPeriodicityRange getPeriodicityRange() { + return periodicityRange; + } + + public void setPeriodicityRange(UsageLimitPeriodicityRange periodicityRange) { + this.periodicityRange = periodicityRange; + } + + @Component(DefinitionPersistValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class DefinitionPersistValidator extends BaseValidator { + + public static final String ValidatorName = "UsageLimit.DefinitionPersistValidator"; + + private final MessageSource messageSource; + + + protected DefinitionPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return DefinitionPersist.class; + } + + @Override + protected List specifications(DefinitionPersist item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isNull(item.getHasPeriodicity())) + .failOn(DefinitionPersist._hasPeriodicity).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DefinitionPersist._hasPeriodicity}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isNull(item.getHasPeriodicity()) && item.getHasPeriodicity()) + .must(() -> !this.isNull(item.getPeriodicityRange())) + .failOn(DefinitionPersist._periodicityRange).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DefinitionPersist._periodicityRange}, LocaleContextHolder.getLocale())) + ); + } + + + } + +} diff --git a/backend/core/src/main/java/org/opencdmp/model/usagelimit/Definition.java b/backend/core/src/main/java/org/opencdmp/model/usagelimit/Definition.java new file mode 100644 index 000000000..6c8708684 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/usagelimit/Definition.java @@ -0,0 +1,28 @@ +package org.opencdmp.model.usagelimit; + +import org.opencdmp.commons.enums.UsageLimitPeriodicityRange; + +public class Definition { + + private Boolean hasPeriodicity; + public final static String _hasPeriodicity = "hasPeriodicity"; + + private UsageLimitPeriodicityRange periodicityRange; + public final static String _periodicityRange = "periodicityRange"; + + public Boolean getHasPeriodicity() { + return hasPeriodicity; + } + + public void setHasPeriodicity(Boolean hasPeriodicity) { + this.hasPeriodicity = hasPeriodicity; + } + + public UsageLimitPeriodicityRange getPeriodicityRange() { + return periodicityRange; + } + + public void setPeriodicityRange(UsageLimitPeriodicityRange periodicityRange) { + this.periodicityRange = periodicityRange; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/query/UsageLimitQuery.java b/backend/core/src/main/java/org/opencdmp/query/UsageLimitQuery.java index d80ab910f..d8ed71187 100644 --- a/backend/core/src/main/java/org/opencdmp/query/UsageLimitQuery.java +++ b/backend/core/src/main/java/org/opencdmp/query/UsageLimitQuery.java @@ -194,6 +194,7 @@ public class UsageLimitQuery extends QueryBase { item.setLabel(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._label, String.class)); item.setTargetMetric(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._targetMetric, UsageLimitTargetMetric.class)); item.setValue(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._value, Long.class)); + item.setDefinition(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._definition, String.class)); item.setCreatedAt(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._createdAt, Instant.class)); item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._updatedAt, Instant.class)); item.setIsActive(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._isActive, IsActive.class)); @@ -210,6 +211,8 @@ public class UsageLimitQuery extends QueryBase { return UsageLimitEntity._targetMetric; else if (item.match(UsageLimit._value)) return UsageLimitEntity._value; + else if (item.prefix(UsageLimit._definition)) + return UsageLimitEntity._definition; else if (item.match(UsageLimit._createdAt)) return UsageLimitEntity._createdAt; else if (item.match(UsageLimit._updatedAt)) diff --git a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingProperties.java b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingProperties.java index 45de0c685..1ddaa4227 100644 --- a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingProperties.java +++ b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingProperties.java @@ -3,15 +3,15 @@ package org.opencdmp.service.accounting; import org.opencdmp.commons.types.accounting.AccountingSourceEntity; import org.springframework.boot.context.properties.ConfigurationProperties; +import java.time.Instant; import java.util.List; @ConfigurationProperties(prefix = "accounting") public class AccountingProperties { private Boolean enabled; private String serviceId; - + private Instant fromInstant; private String subjectId; - private List projectFields; private List sources; @@ -28,6 +28,14 @@ public class AccountingProperties { this.enabled = enabled; } + public Instant getFromInstant() { + return fromInstant; + } + + public void setFromInstant(Instant fromInstant) { + this.fromInstant = fromInstant; + } + public void setServiceId(String serviceId) { this.serviceId = serviceId; } diff --git a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingService.java b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingService.java index 846f2397e..f3bda76ce 100644 --- a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingService.java +++ b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingService.java @@ -1,14 +1,16 @@ package org.opencdmp.service.accounting; import org.opencdmp.commons.enums.UsageLimitTargetMetric; +import org.opencdmp.commons.types.usagelimit.DefinitionEntity; import javax.management.InvalidApplicationException; +import java.util.UUID; public interface AccountingService { - Integer getCurrentMetricValue(UsageLimitTargetMetric metric) throws InvalidApplicationException; + Integer getCurrentMetricValue(UsageLimitTargetMetric metric, DefinitionEntity definition) throws InvalidApplicationException; - void set(String metric); + void set(String metric, UUID tenantId, String tenantCode) throws InvalidApplicationException; void increase(String metric) throws InvalidApplicationException; diff --git a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingServiceImpl.java index a59a3b0b5..a7757808d 100644 --- a/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/accounting/AccountingServiceImpl.java @@ -11,6 +11,7 @@ import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidatorFactory; +import org.opencdmp.commons.enums.UsageLimitPeriodicityRange; import org.opencdmp.commons.enums.UsageLimitTargetMetric; import org.opencdmp.commons.enums.accounting.AccountingAggregateType; import org.opencdmp.commons.enums.accounting.AccountingDataRangeType; @@ -19,6 +20,7 @@ import org.opencdmp.commons.enums.accounting.AccountingValueType; import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.user.UserScope; import org.opencdmp.commons.types.accounting.AccountingSourceEntity; +import org.opencdmp.commons.types.usagelimit.DefinitionEntity; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.UserCredentialEntity; import org.opencdmp.integrationevent.outbox.accountingentrycreated.AccountingEntryCreatedIntegrationEvent; @@ -47,10 +49,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Service public class AccountingServiceImpl implements AccountingService { @@ -130,7 +129,7 @@ public class AccountingServiceImpl implements AccountingService { }); } - public Integer getCurrentMetricValue(UsageLimitTargetMetric metric) throws InvalidApplicationException { + public Integer getCurrentMetricValue(UsageLimitTargetMetric metric, DefinitionEntity definition) throws InvalidApplicationException { if (this.isEnabled) { AccountingClient accountingClient = null; try { @@ -154,13 +153,20 @@ public class AccountingServiceImpl implements AccountingService { throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{this.accountingProperties.getSources().getFirst().getRepositoryId(), AccountingClient.class.getSimpleName()}, LocaleContextHolder.getLocale())); AccountingInfoLookup lookup = new AccountingInfoLookup(); - lookup.setTo(Instant.now()); - lookup.setDateRangeType(AccountingDataRangeType.ThisYear); + if (definition.getHasPeriodicity()) { + if (definition.getPeriodicityRange().equals(UsageLimitPeriodicityRange.Monthly)) lookup.setDateRangeType(AccountingDataRangeType.ThisMonth); + else lookup.setDateRangeType(AccountingDataRangeType.ThisYear); + } else { + lookup.setDateRangeType(AccountingDataRangeType.Custom); + lookup.setTo(Instant.now()); + lookup.setFrom(this.accountingProperties.getFromInstant()); + } + lookup.setMeasure(AccountingMeasureType.Unit); lookup.setServiceCodes(List.of(this.accountingProperties.getServiceId())); lookup.setAggregateTypes(List.of(AccountingAggregateType.Sum)); lookup.setActionCodes(List.of(metric.getValue())); - lookup.setUserCodes(List.of(this.getSubjectId())); + try { lookup.setResourceCodes(List.of(this.tenantScope.getTenantCode() != null ? this.tenantScope.getTenantCode() : this.tenantScope.getDefaultTenantCode())); } catch (InvalidApplicationException e) { @@ -191,8 +197,12 @@ public class AccountingServiceImpl implements AccountingService { return null; } - public void set(String metric) { - //TODO + public void set(String metric, UUID tenantId, String tenantCode) throws InvalidApplicationException{ + if (this.isEnabled) { + String subjectId = this.getSubjectId(); + + this.accountingEntryCreatedIntegrationEventHandler.handleAccountingEntry(metric, AccountingValueType.Reset, subjectId, tenantId, tenantCode); + } } public void increase(String metric) throws InvalidApplicationException { diff --git a/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitService.java b/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitService.java index cedcad0df..98642c35f 100644 --- a/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitService.java +++ b/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitService.java @@ -5,6 +5,7 @@ import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; +import jakarta.xml.bind.JAXBException; import org.opencdmp.commons.enums.UsageLimitTargetMetric; import org.opencdmp.model.UsageLimit; import org.opencdmp.model.persist.UsageLimitPersist; @@ -14,7 +15,7 @@ import java.util.UUID; public interface UsageLimitService { - UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; + UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; diff --git a/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitServiceImpl.java index ae3c2cbc8..69700d6f5 100644 --- a/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/usagelimit/UsageLimitServiceImpl.java @@ -11,11 +11,15 @@ import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; +import jakarta.xml.bind.JAXBException; +import org.jetbrains.annotations.NotNull; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.Permission; +import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.UsageLimitTargetMetric; import gr.cite.tools.data.query.QueryFactory; +import org.opencdmp.commons.types.usagelimit.DefinitionEntity; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.UsageLimitEntity; @@ -24,6 +28,7 @@ import org.opencdmp.model.UsageLimit; import org.opencdmp.model.builder.UsageLimitBuilder; import org.opencdmp.model.deleter.UsageLimitDeleter; import org.opencdmp.model.persist.UsageLimitPersist; +import org.opencdmp.model.persist.usagelimit.DefinitionPersist; import org.opencdmp.query.UsageLimitQuery; import org.opencdmp.service.accounting.AccountingProperties; import org.opencdmp.service.accounting.AccountingService; @@ -63,6 +68,8 @@ public class UsageLimitServiceImpl implements UsageLimitService { private final TenantEntityManager tenantEntityManager; + private final XmlHandlingService xmlHandlingService; + private final AccountingService accountingService; private final UsageLimitProperties usageLimitProperties; @@ -79,7 +86,7 @@ public class UsageLimitServiceImpl implements UsageLimitService { ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, - QueryFactory queryFactory, TenantEntityManager tenantEntityManager, AccountingService accountingService, UsageLimitProperties usageLimitProperties, AccountingProperties accountingProperties) { + QueryFactory queryFactory, TenantEntityManager tenantEntityManager, XmlHandlingService xmlHandlingService, AccountingService accountingService, UsageLimitProperties usageLimitProperties, AccountingProperties accountingProperties) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -89,12 +96,13 @@ public class UsageLimitServiceImpl implements UsageLimitService { this.messageSource = messageSource; this.queryFactory = queryFactory; this.tenantEntityManager = tenantEntityManager; + this.xmlHandlingService = xmlHandlingService; this.accountingService = accountingService; this.usageLimitProperties = usageLimitProperties; this.accountingProperties = accountingProperties; } - public UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { + public UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException { logger.debug(new MapLogEntry("persisting data UsageLimit").And("model", model).And("fields", fields)); this.authorizationService.authorizeForce(Permission.EditUsageLimit); @@ -132,6 +140,8 @@ public class UsageLimitServiceImpl implements UsageLimitService { data.setTargetMetric(model.getTargetMetric()); data.setValue(model.getValue()); data.setUpdatedAt(Instant.now()); + data.setDefinition(this.xmlHandlingService.toXml(this.buildDefinitionEntity(model.getDefinition()))); + if (isUpdate) this.entityManager.merge(data); else @@ -141,6 +151,17 @@ public class UsageLimitServiceImpl implements UsageLimitService { return this.builderFactory.builder(UsageLimitBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(BaseFieldSet.build(fields, UsageLimit._id), data); } + private @NotNull DefinitionEntity buildDefinitionEntity(DefinitionPersist persist) { + DefinitionEntity data = new DefinitionEntity(); + if (persist == null) + return data; + + data.setHasPeriodicity(persist.getHasPeriodicity()); + if (persist.getHasPeriodicity()) data.setPeriodicityRange(persist.getPeriodicityRange()); + + return data; + } + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting UsageLimit: {}", id); @@ -155,13 +176,17 @@ public class UsageLimitServiceImpl implements UsageLimitService { if (metric == null) throw new MyApplicationException("Target Metric not defined"); - Integer currentValue = this.accountingService.getCurrentMetricValue(metric); - try { this.tenantEntityManager.loadExplicitTenantFilters(); - UsageLimitEntity usageLimitEntity = this.queryFactory.query(UsageLimitQuery.class).disableTracking().usageLimitTargetMetrics(metric).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(UsageLimit._label).ensure(UsageLimit._targetMetric).ensure(UsageLimit._value)); - if (usageLimitEntity != null && currentValue >= usageLimitEntity.getValue()) throw new MyValidationException(this.errors.getUsageLimitException().getCode(), usageLimitEntity.getLabel()); + UsageLimitEntity usageLimitEntity = this.queryFactory.query(UsageLimitQuery.class).disableTracking().usageLimitTargetMetrics(metric).isActive(IsActive.Active).first(); + if (usageLimitEntity != null) { + DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, usageLimitEntity.getDefinition()); + if (definition == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{usageLimitEntity.getId(), DefinitionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + Integer currentValue = this.accountingService.getCurrentMetricValue(metric, definition); + if (currentValue >= usageLimitEntity.getValue()) throw new MyValidationException(this.errors.getUsageLimitException().getCode(), usageLimitEntity.getLabel()); + } } catch (InvalidApplicationException e) { log.error(e.getMessage(), e); throw new MyApplicationException(e.getMessage()); diff --git a/backend/web/src/main/java/org/opencdmp/controllers/UsageFilterController.java b/backend/web/src/main/java/org/opencdmp/controllers/UsageLimitController.java similarity index 98% rename from backend/web/src/main/java/org/opencdmp/controllers/UsageFilterController.java rename to backend/web/src/main/java/org/opencdmp/controllers/UsageLimitController.java index b6c6d1592..1ec003198 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/UsageFilterController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/UsageLimitController.java @@ -46,9 +46,9 @@ import java.util.UUID; @RestController @RequestMapping(path = "api/usage-limit") -public class UsageFilterController { +public class UsageLimitController { - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageFilterController.class)); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageLimitController.class)); private final BuilderFactory builderFactory; @@ -62,7 +62,7 @@ public class UsageFilterController { private final MessageSource messageSource; - public UsageFilterController( + public UsageLimitController( BuilderFactory builderFactory, AuditService auditService, UsageLimitService usageLimitService, diff --git a/backend/web/src/main/resources/config/accounting.yml b/backend/web/src/main/resources/config/accounting.yml index 17322e233..cd8f0e166 100644 --- a/backend/web/src/main/resources/config/accounting.yml +++ b/backend/web/src/main/resources/config/accounting.yml @@ -2,5 +2,6 @@ accounting: enabled: ${ACCOUNTING_ENABLED} serviceId: ${SERVICE_ID} subjectId: unknown + fromInstant: 1990-01-01T05:47:26.853Z projectFields: - sum diff --git a/frontend/src/app/core/common/enum/usage-limit-periodicity-range.ts b/frontend/src/app/core/common/enum/usage-limit-periodicity-range.ts new file mode 100644 index 000000000..d5f7d8130 --- /dev/null +++ b/frontend/src/app/core/common/enum/usage-limit-periodicity-range.ts @@ -0,0 +1,4 @@ +export enum UsageLimitPeriodicityRange { + Monthly = 0, + Yearly = 1 +} diff --git a/frontend/src/app/core/model/usage-limit/usage-limit.ts b/frontend/src/app/core/model/usage-limit/usage-limit.ts index 029c1a71c..2578b43df 100644 --- a/frontend/src/app/core/model/usage-limit/usage-limit.ts +++ b/frontend/src/app/core/model/usage-limit/usage-limit.ts @@ -1,3 +1,4 @@ +import { UsageLimitPeriodicityRange } from "@app/core/common/enum/usage-limit-periodicity-range"; import { UsageLimitTargetMetric } from "@app/core/common/enum/usage-limit-target-metric"; import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; @@ -5,10 +6,24 @@ export interface UsageLimit extends BaseEntity { label: string; targetMetric: UsageLimitTargetMetric; value: number; + definition: UsageLimitDefinition; } +export interface UsageLimitDefinition { + hasPeriodicity: Boolean; + periodicityRange: UsageLimitPeriodicityRange +} + +// persist + export interface UsageLimitPersist extends BaseEntityPersist { label: string; targetMetric: UsageLimitTargetMetric; value: number; + definition: UsageLimitDefinitionPersist; +} + +export interface UsageLimitDefinitionPersist { + hasPeriodicity: Boolean; + periodicityRange: UsageLimitPeriodicityRange } \ No newline at end of file diff --git a/frontend/src/app/core/services/utilities/enum-utils.service.ts b/frontend/src/app/core/services/utilities/enum-utils.service.ts index 1bd8b72ea..eead5eea0 100644 --- a/frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -30,6 +30,7 @@ import { PlanBlueprintExtraFieldDataType } from '../../common/enum/plan-blueprin import { PlanStatus } from '../../common/enum/plan-status'; import { ValidationType } from '../../common/enum/validation-type'; import { UsageLimitTargetMetric } from '@app/core/common/enum/usage-limit-target-metric'; +import { UsageLimitPeriodicityRange } from '@app/core/common/enum/usage-limit-periodicity-range'; @Injectable() export class EnumUtils { @@ -318,4 +319,12 @@ export class EnumUtils { } } + public toUsageLimitPeriodicityRangeString(value: UsageLimitPeriodicityRange): string { + switch (value) { + case UsageLimitPeriodicityRange.Monthly: return this.language.instant('TYPES.USAGE-LIMIT-PERIODICITY-RANGE.MONTHLY'); + case UsageLimitPeriodicityRange.Yearly: return this.language.instant('TYPES.USAGE-LIMIT-PERIODICITY-RANGE.YEARLY'); + default: return ''; + } + } + } diff --git a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.html b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.html index 9ba56fa5c..eefc1fbcd 100644 --- a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.html +++ b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.html @@ -52,6 +52,23 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}} +
+ + {{'USAGE-LIMIT-EDITOR.FIELDS.HAS-PERIODICITY' | translate}} + {{formGroup.get('definition').get('hasPeriodicity').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'USAGE-LIMIT-EDITOR.FIELDS.PERIODICITY-RANGE' | translate}} + + {{enumUtils.toUsageLimitPeriodicityRangeString(range)}} + + {{formGroup.get('definition').get('periodicityRange').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
diff --git a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.ts b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.ts index e50de3afe..a72b4369a 100644 --- a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.ts +++ b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.component.ts @@ -28,6 +28,7 @@ import { UsageLimitEditorService } from './usage-limit-editor.service'; import { RouterUtilsService } from '@app/core/services/router/router-utils.service'; import { Title } from '@angular/platform-browser'; import { UsageLimitTargetMetric } from '@app/core/common/enum/usage-limit-target-metric'; +import { UsageLimitPeriodicityRange } from '@app/core/common/enum/usage-limit-periodicity-range'; @Component({ @@ -43,6 +44,7 @@ export class UsageLimitEditorComponent extends BaseEditor(UsageLimitTargetMetric); + periodicityRangeEnum = this.enumUtils.getEnumValues(UsageLimitPeriodicityRange); protected get canDelete(): boolean { return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteUsageLimit) && this.editorModel.belongsToCurrentTenant != false;; diff --git a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.model.ts b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.model.ts index a32d7e35c..c85df72cc 100644 --- a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.model.ts +++ b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.model.ts @@ -1,6 +1,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { UsageLimitPeriodicityRange } from "@app/core/common/enum/usage-limit-periodicity-range"; import { UsageLimitTargetMetric } from "@app/core/common/enum/usage-limit-target-metric"; -import { UsageLimit, UsageLimitPersist } from "@app/core/model/usage-limit/usage-limit"; +import { UsageLimit, UsageLimitDefinition, UsageLimitDefinitionPersist, UsageLimitPersist } from "@app/core/model/usage-limit/usage-limit"; import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; @@ -11,6 +12,7 @@ export class UsageLimitEditorModel extends BaseEditorModel implements UsageLimit targetMetric: UsageLimitTargetMetric; value: number; permissions: string[]; + definition: DefinitionEditorModel = new DefinitionEditorModel(this.validationErrorModel); public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); @@ -23,6 +25,7 @@ export class UsageLimitEditorModel extends BaseEditorModel implements UsageLimit this.label = item.label; this.targetMetric = item.targetMetric; this.value = item.value; + if (item.definition) this.definition = new DefinitionEditorModel(this.validationErrorModel).fromModel(item.definition); } return this; } @@ -35,6 +38,10 @@ export class UsageLimitEditorModel extends BaseEditorModel implements UsageLimit label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], targetMetric: [{ value: this.targetMetric, disabled: disabled }, context.getValidation('targetMetric').validators], value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], + definition: this.definition.buildForm({ + rootPath: `definition.`, + disabled: disabled + }), hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] }); } @@ -52,3 +59,56 @@ export class UsageLimitEditorModel extends BaseEditorModel implements UsageLimit return baseContext; } } + +export class DefinitionEditorModel implements UsageLimitDefinitionPersist { + hasPeriodicity: Boolean = false; + periodicityRange: UsageLimitPeriodicityRange; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: UsageLimitDefinition): DefinitionEditorModel { + if (item) { + this.hasPeriodicity = item.hasPeriodicity; + this.periodicityRange = item.periodicityRange; + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DefinitionEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + hasPeriodicity: [{ value: this.hasPeriodicity, disabled: disabled }, context.getValidation('hasPeriodicity').validators], + periodicityRange: [{ value: this.periodicityRange, disabled: disabled }, context.getValidation('periodicityRange').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'hasPeriodicity', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}hasPeriodicity`)] }); + baseValidationArray.push({ key: 'periodicityRange', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}periodicityRange`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} diff --git a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.resolver.ts b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.resolver.ts index f4e8a1567..e41249ee7 100644 --- a/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.resolver.ts +++ b/frontend/src/app/ui/admin/usage-limit/editor/usage-limit-editor.resolver.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { UsageLimit } from '@app/core/model/usage-limit/usage-limit'; +import { UsageLimit, UsageLimitDefinition } from '@app/core/model/usage-limit/usage-limit'; import { UsageLimitService } from '@app/core/services/usage-limit/usage.service'; import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; import { BaseEditorResolver } from '@common/base/base-editor.resolver'; @@ -22,6 +22,8 @@ export class UsageLimitEditorResolver extends BaseEditorResolver { nameof(x => x.label), nameof(x => x.targetMetric), nameof(x => x.value), + [nameof(x => x.definition), nameof(x => x.hasPeriodicity)].join('.'), + [nameof(x => x.definition), nameof(x => x.periodicityRange)].join('.'), nameof(x => x.createdAt), nameof(x => x.updatedAt), nameof(x => x.hash),