add UsageLimit backend stack

This commit is contained in:
amentis 2024-07-12 12:09:40 +03:00
parent 91103ee8ef
commit 09ed61ce6f
16 changed files with 1204 additions and 9 deletions

View File

@ -178,6 +178,11 @@ public class AuditableAction {
public static final EventId Annotation_Created_Notify = new EventId(280000, "Annotation_Created_Notify"); public static final EventId Annotation_Created_Notify = new EventId(280000, "Annotation_Created_Notify");
public static final EventId UsageLimit_Query = new EventId(290000, "UsageLimit_Query");
public static final EventId UsageLimit_Lookup = new EventId(290001, "UsageLimit_Lookup");
public static final EventId UsageLimit_Persist = new EventId(290002, "UsageLimit_Persist");
public static final EventId UsageLimit_Delete = new EventId(290003, "UsageLimit_Delete");
} }

View File

@ -200,6 +200,11 @@ public final class Permission {
public static String EditPrefillingSource= "EditPrefillingSource"; public static String EditPrefillingSource= "EditPrefillingSource";
public static String DeletePrefillingSource = "DeletePrefillingSource"; public static String DeletePrefillingSource = "DeletePrefillingSource";
//UsageLimit
public static String BrowseUsageLimit = "BrowseUsageLimit";
public static String EditUsageLimit = "EditUsageLimit";
public static String DeleteUsageLimit = "DeleteUsageLimit";
//NotificationTemplate //NotificationTemplate
public static String BrowseStatus = "BrowseStatus"; public static String BrowseStatus = "BrowseStatus";
public static String EditStatus = "EditStatus"; public static String EditStatus = "EditStatus";
@ -220,6 +225,7 @@ public final class Permission {
public static String ViewReferenceTypePage = "ViewReferenceTypePage"; public static String ViewReferenceTypePage = "ViewReferenceTypePage";
public static String ViewReferencePaPlge = "ViewReferencePage"; public static String ViewReferencePaPlge = "ViewReferencePage";
public static String ViewEntityLockPage = "ViewEntityLockPage"; public static String ViewEntityLockPage = "ViewEntityLockPage";
public static String ViewUsageLimitPage = "ViewUsageLimitPage";
public static String ViewDescriptionTemplatePage = "ViewDescriptionTemplatePage"; public static String ViewDescriptionTemplatePage = "ViewDescriptionTemplatePage";
public static String ViewPlanBlueprintPage = "ViewPlanBlueprintPage"; public static String ViewPlanBlueprintPage = "ViewPlanBlueprintPage";
public static String ViewPublicDescriptionPage = "ViewPublicDescriptionPage"; public static String ViewPublicDescriptionPage = "ViewPublicDescriptionPage";

View File

@ -0,0 +1,44 @@
package org.opencdmp.commons.enums;
import com.fasterxml.jackson.annotation.JsonValue;
import org.opencdmp.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum UsageLimitMetricValue implements DatabaseEnum<String> {
USER_COUNT(MetricValues.UserCount),
PLAN_COUNT(MetricValues.PlanCount),
BLUEPRINT_COUNT(MetricValues.BlueprintCount),
DESCRIPTION_COUNT(MetricValues.DescriptionCount),
DESCRIPTION_TEMPLATE_COUNT(MetricValues.DescriptionTemplateCount),
DESCRIPTION_TEMPLATE_TYPE_COUNT(MetricValues.DescriptionTemplateTypeCount),
PREFILLING_SOURCES_COUNT(MetricValues.PrefillingSourcesCount),
REFERENCE_TYPE_COUNT(MetricValues.ReferenceTypeCount);
private final String value;
public static class MetricValues {
public static final String UserCount = "user_count";
public static final String PlanCount = "plan_count";
public static final String BlueprintCount = "blueprint_count";
public static final String DescriptionCount = "description_count";
public static final String DescriptionTemplateCount = "description_template_count";
public static final String DescriptionTemplateTypeCount = "description_template_type_count";
public static final String PrefillingSourcesCount = "prefilling_sources_count";
public static final String ReferenceTypeCount = "reference_type_count";
}
UsageLimitMetricValue(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return this.value;
}
private static final Map<String, UsageLimitMetricValue> map = EnumUtils.getEnumValueMap(UsageLimitMetricValue.class);
public static UsageLimitMetricValue of(String i) {
return map.get(i);
}
}

View File

@ -0,0 +1,111 @@
package org.opencdmp.data;
import jakarta.persistence.*;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.data.converters.enums.IsActiveConverter;
import org.opencdmp.data.tenant.TenantScopedBaseEntity;
import java.time.Instant;
import java.util.UUID;
@Entity
@Table(name = "\"UsageLimit\"")
public class UsageLimitEntity extends TenantScopedBaseEntity {
@Id
@Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false)
private UUID id;
public static final String _id = "id";
@Column(name = "label", length = _labelLength, nullable = false)
private String label;
public static final String _label = "label";
public static final int _labelLength = 250;
@Column(name = "metric_value", nullable = false)
private UsageLimitMetricValue metricValue;
public static final String _metricValue = "metricValue";
@Column(name = "value", nullable = false)
private Long value;
public static final String _value = "value";
@Column(name = "is_active", nullable = false)
@Convert(converter = IsActiveConverter.class)
private IsActive isActive;
public static final String _isActive = "isActive";
@Column(name = "created_at", nullable = false)
private Instant createdAt;
public static final String _createdAt = "createdAt";
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public UsageLimitMetricValue getMetricValue() {
return metricValue;
}
public void setMetricValue(UsageLimitMetricValue metricValue) {
this.metricValue = metricValue;
}
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
public IsActive getIsActive() {
return isActive;
}
public void setIsActive(IsActive isActive) {
this.isActive = isActive;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@ -0,0 +1,109 @@
package org.opencdmp.model;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import java.time.Instant;
import java.util.UUID;
public class UsageLimit {
private UUID id;
public static final String _id = "id";
private String label;
public static final String _label = "label";
private UsageLimitMetricValue metricValue;
public static final String _metricValue = "metricValue";
private Long value;
public static final String _value = "value";
private IsActive isActive;
public static final String _isActive = "isActive";
private Instant createdAt;
public static final String _createdAt = "createdAt";
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
private String hash;
public static final String _hash = "hash";
private Boolean belongsToCurrentTenant;
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public UsageLimitMetricValue getMetricValue() {
return metricValue;
}
public void setMetricValue(UsageLimitMetricValue metricValue) {
this.metricValue = metricValue;
}
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
public IsActive getIsActive() {
return isActive;
}
public void setIsActive(IsActive isActive) {
this.isActive = isActive;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public Boolean getBelongsToCurrentTenant() {
return belongsToCurrentTenant;
}
public void setBelongsToCurrentTenant(Boolean belongsToCurrentTenant) {
this.belongsToCurrentTenant = belongsToCurrentTenant;
}
}

View File

@ -0,0 +1,74 @@
package org.opencdmp.model.builder;
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.scope.tenant.TenantScope;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.UsageLimitEntity;
import org.opencdmp.model.UsageLimit;
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
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UsageLimitBuilder extends BaseBuilder<UsageLimit, UsageLimitEntity> {
private final TenantScope tenantScope;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired
public UsageLimitBuilder(
ConventionService conventionService,
TenantScope tenantScope) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(UsageLimitBuilder.class)));
this.tenantScope = tenantScope;
}
public UsageLimitBuilder authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
public List<UsageLimit> build(FieldSet fields, List<UsageLimitEntity> 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<UsageLimit> models = new ArrayList<>();
for (UsageLimitEntity d : data) {
UsageLimit m = new UsageLimit();
if (fields.hasField(this.asIndexer(UsageLimit._id)))
m.setId(d.getId());
if (fields.hasField(this.asIndexer(UsageLimit._label)))
m.setLabel(d.getLabel());
if (fields.hasField(this.asIndexer(UsageLimit._metricValue)))
m.setMetricValue(d.getMetricValue());
if (fields.hasField(this.asIndexer(UsageLimit._value)))
m.setValue(d.getValue());
if (fields.hasField(this.asIndexer(UsageLimit._createdAt)))
m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(UsageLimit._updatedAt)))
m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(UsageLimit._isActive)))
m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(UsageLimit._hash)))
m.setHash(this.hashValue(d.getUpdatedAt()));
if (fields.hasField(this.asIndexer(UsageLimit._belongsToCurrentTenant))) m.setBelongsToCurrentTenant(this.getBelongsToCurrentTenant(d, this.tenantScope));
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
return models;
}
}

View File

@ -0,0 +1,40 @@
package org.opencdmp.model.censorship;
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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UsageLimitsCensor extends BaseCensor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageLimitsCensor.class));
protected final AuthorizationService authService;
protected final CensorFactory censorFactory;
public UsageLimitsCensor(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);
}
}

View File

@ -0,0 +1,79 @@
package org.opencdmp.model.deleter;
import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UsageLimitEntity;
import org.opencdmp.query.UsageLimitQuery;
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 javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UsageLimitDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageLimitDeleter.class));
private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory;
protected final DeleterFactory deleterFactory;
@Autowired
public UsageLimitDeleter(
TenantEntityManager entityManager,
QueryFactory queryFactory,
DeleterFactory deleterFactory
) {
this.entityManager = entityManager;
this.queryFactory = queryFactory;
this.deleterFactory = deleterFactory;
}
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids));
List<UsageLimitEntity> data = this.queryFactory.query(UsageLimitQuery.class).ids(ids).collect();
logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.deleteAndSave(data);
}
public void deleteAndSave(List<UsageLimitEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.delete(data);
logger.trace("saving changes");
this.entityManager.flush();
logger.trace("changes saved");
}
public void delete(List<UsageLimitEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
if (data == null || data.isEmpty())
return;
Instant now = Instant.now();
for (UsageLimitEntity item : data) {
logger.trace("deleting item {}", item.getId());
item.setIsActive(IsActive.Inactive);
item.setUpdatedAt(now);
logger.trace("updating item");
this.entityManager.merge(item);
logger.trace("updated item");
}
}
}

View File

@ -0,0 +1,122 @@
package org.opencdmp.model.persist;
import gr.cite.tools.validation.specification.Specification;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.commons.validation.BaseValidator;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.UsageLimitEntity;
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;
import java.util.UUID;
public class UsageLimitPersist {
private UUID id;
public static final String _id = "id";
private String label;
public static final String _label = "label";
private UsageLimitMetricValue metricValue;;
public static final String _metricValue = "metricValue";
private Long value;
public static final String _value = "value";
private String hash;
public static final String _hash = "hash";
public UUID getId() {
return this.id;
}
public void setId(UUID id) {
this.id = id;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public UsageLimitMetricValue getMetricValue() {
return metricValue;
}
public void setMetricValue(UsageLimitMetricValue metricValue) {
this.metricValue = metricValue;
}
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
public String getHash() {
return this.hash;
}
public void setHash(String hash) {
this.hash = hash;
}
@Component(UsageLimitPersistValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UsageLimitPersistValidator extends BaseValidator<UsageLimitPersist> {
public static final String ValidatorName = "UsageLimitPersistValidator";
private final MessageSource messageSource;
protected UsageLimitPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<UsageLimitPersist> modelClass() {
return UsageLimitPersist.class;
}
@Override
protected List<Specification> specifications(UsageLimitPersist item) {
return Arrays.asList(
this.spec()
.iff(() -> this.isValidGuid(item.getId()))
.must(() -> this.isValidHash(item.getHash()))
.failOn(UsageLimitPersist._hash).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UsageLimitPersist._hash}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isValidGuid(item.getId()))
.must(() -> !this.isValidHash(item.getHash()))
.failOn(UsageLimitPersist._hash).failWith(this.messageSource.getMessage("Validation_OverPosting", new Object[]{}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getLabel()))
.failOn(UsageLimitPersist._label).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UsageLimitPersist._label}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isEmpty(item.getLabel()))
.must(() -> this.lessEqualLength(item.getLabel(), UsageLimitEntity._labelLength))
.failOn(UsageLimitPersist._label).failWith(this.messageSource.getMessage("Validation_MaxLength", new Object[]{UsageLimitPersist._label}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getMetricValue()))
.failOn(UsageLimitPersist._metricValue).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UsageLimitPersist._metricValue}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getValue()))
.failOn(UsageLimitPersist._value).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UsageLimitPersist._value}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -0,0 +1,227 @@
package org.opencdmp.query;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.data.*;
import org.opencdmp.model.UsageLimit;
import org.opencdmp.query.utils.QueryUtilsService;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UsageLimitQuery extends QueryBase<UsageLimitEntity> {
private String like;
private Collection<UUID> ids;
private Collection<IsActive> isActives;
private Collection<UsageLimitMetricValue> usageLimitMetricValues;
private Collection<UUID> excludedIds;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public UsageLimitQuery like(String value) {
this.like = value;
return this;
}
public UsageLimitQuery ids(UUID value) {
this.ids = List.of(value);
return this;
}
public UsageLimitQuery ids(UUID... value) {
this.ids = Arrays.asList(value);
return this;
}
public UsageLimitQuery ids(Collection<UUID> values) {
this.ids = values;
return this;
}
public UsageLimitQuery isActive(IsActive value) {
this.isActives = List.of(value);
return this;
}
public UsageLimitQuery isActive(IsActive... value) {
this.isActives = Arrays.asList(value);
return this;
}
public UsageLimitQuery isActive(Collection<IsActive> values) {
this.isActives = values;
return this;
}
public UsageLimitQuery excludedIds(Collection<UUID> values) {
this.excludedIds = values;
return this;
}
public UsageLimitQuery excludedIds(UUID value) {
this.excludedIds = List.of(value);
return this;
}
public UsageLimitQuery excludedIds(UUID... value) {
this.excludedIds = Arrays.asList(value);
return this;
}
public UsageLimitQuery usageLimitMetricValues(UsageLimitMetricValue value) {
this.usageLimitMetricValues = List.of(value);
return this;
}
public UsageLimitQuery usageLimitMetricValues(UsageLimitMetricValue... value) {
this.usageLimitMetricValues = Arrays.asList(value);
return this;
}
public UsageLimitQuery usageLimitMetricValues(Collection<UsageLimitMetricValue> values) {
this.usageLimitMetricValues = values;
return this;
}
public UsageLimitQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
public UsageLimitQuery enableTracking() {
this.noTracking = false;
return this;
}
public UsageLimitQuery disableTracking() {
this.noTracking = true;
return this;
}
private final UserScope userScope;
private final AuthorizationService authService;
private final QueryUtilsService queryUtilsService;
private final TenantEntityManager tenantEntityManager;
public UsageLimitQuery(
UserScope userScope, AuthorizationService authService, QueryUtilsService queryUtilsService, TenantEntityManager tenantEntityManager) {
this.userScope = userScope;
this.authService = authService;
this.queryUtilsService = queryUtilsService;
this.tenantEntityManager = tenantEntityManager;
}
@Override
protected EntityManager entityManager(){
return this.tenantEntityManager.getEntityManager();
}
@Override
protected Class<UsageLimitEntity> entityClass() {
return UsageLimitEntity.class;
}
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.isActives) || this.isEmpty(this.excludedIds) || this.isEmpty(this.usageLimitMetricValues);
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();
if (this.ids != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UsageLimitEntity._id));
for (UUID item : this.ids)
inClause.value(item);
predicates.add(inClause);
}
if (this.like != null && !this.like.isBlank()) {
predicates.add(this.queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(UsageLimitEntity._label), this.like));
}
if (this.isActives != null) {
CriteriaBuilder.In<IsActive> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UsageLimitEntity._isActive));
for (IsActive item : this.isActives)
inClause.value(item);
predicates.add(inClause);
}
if (this.usageLimitMetricValues != null) {
CriteriaBuilder.In<UsageLimitMetricValue> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UsageLimitEntity._metricValue));
for (UsageLimitMetricValue item : this.usageLimitMetricValues)
inClause.value(item);
predicates.add(inClause);
}
if (this.excludedIds != null) {
CriteriaBuilder.In<UUID> notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(ReferenceEntity._id));
for (UUID item : this.excludedIds)
notInClause.value(item);
predicates.add(notInClause.not());
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return null;
}
}
@Override
protected UsageLimitEntity convert(Tuple tuple, Set<String> columns) {
UsageLimitEntity item = new UsageLimitEntity();
item.setId(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._id, UUID.class));
item.setTenantId(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._tenantId, UUID.class));
item.setLabel(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._label, String.class));
item.setMetricValue(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._metricValue, UsageLimitMetricValue.class));
item.setValue(QueryBase.convertSafe(tuple, columns, UsageLimitEntity._value, Long.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));
return item;
}
@Override
protected String fieldNameOf(FieldResolver item) {
if (item.match(UsageLimit._id))
return UsageLimitEntity._id;
else if (item.match(UsageLimit._label))
return UsageLimitEntity._label;
else if (item.match(UsageLimit._metricValue))
return UsageLimitEntity._metricValue;
else if (item.match(UsageLimit._value))
return UsageLimitEntity._value;
else if (item.match(UsageLimit._createdAt))
return UsageLimitEntity._createdAt;
else if (item.match(UsageLimit._updatedAt))
return UsageLimitEntity._updatedAt;
else if (item.match(UsageLimit._hash))
return UsageLimitEntity._updatedAt;
else if (item.match(UsageLimit._isActive))
return UsageLimitEntity._isActive;
else if (item.prefix(UsageLimit._belongsToCurrentTenant))
return UsageLimitEntity._tenantId;
else
return null;
}
}

View File

@ -0,0 +1,70 @@
package org.opencdmp.query.lookup;
import gr.cite.tools.data.query.Lookup;
import gr.cite.tools.data.query.QueryFactory;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.query.UsageLimitQuery;
import java.util.List;
import java.util.UUID;
public class UsageLimitLookup extends Lookup {
private String like;
private List<IsActive> isActive;
private List<UUID> ids;
private List<UsageLimitMetricValue> usageLimitMetricValues;
private List<UUID> excludedIds;
public String getLike() {
return like;
}
public void setLike(String like) {
this.like = like;
}
public List<IsActive> getIsActive() {
return isActive;
}
public void setIsActive(List<IsActive> isActive) {
this.isActive = isActive;
}
public List<UUID> getIds() {
return ids;
}
public void setIds(List<UUID> ids) { this.ids = ids; }
public List<UsageLimitMetricValue> getUsageLimitsMetricValues() {
return usageLimitMetricValues;
}
public void setUsageLimitsMetricValues(List<UsageLimitMetricValue> usageLimitMetricValues) {
this.usageLimitMetricValues = usageLimitMetricValues;
}
public List<UUID> getExcludedIds() {
return excludedIds;
}
public void setExcludedIds(List<UUID> excludeIds) {
this.excludedIds = excludeIds;
}
public UsageLimitQuery enrich(QueryFactory queryFactory) {
UsageLimitQuery query = queryFactory.query(UsageLimitQuery.class);
if (this.like != null) query.like(this.like);
if (this.isActive != null) query.isActive(this.isActive);
if (this.ids != null) query.ids(this.ids);
if (this.usageLimitMetricValues != null) query.usageLimitMetricValues(this.usageLimitMetricValues);
if (this.excludedIds != null) query.excludedIds(this.excludedIds);
this.enrichCommon(query);
return query;
}
}

View File

@ -1,11 +1,23 @@
package org.opencdmp.service.usagelimit; package org.opencdmp.service.usagelimit;
public class UsageLimitService { import gr.cite.tools.exception.MyApplicationException;
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 org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.model.UsageLimit;
import org.opencdmp.model.persist.UsageLimitPersist;
private void checkIncrease(String metric) { import javax.management.InvalidApplicationException;
//Get/Calculate current metric value from accountingService import java.util.UUID;
//Find metric value from db
// compare these two and throw UsageLimitException when current > metric value public interface UsageLimitService {
}
UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
void checkIncrease(UsageLimitMetricValue metric);
} }

View File

@ -0,0 +1,126 @@
package org.opencdmp.service.usagelimit;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
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 org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.Permission;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.UsageLimitMetricValue;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UsageLimitEntity;
import org.opencdmp.errorcode.ErrorThesaurusProperties;
import org.opencdmp.model.Tag;
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.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
@Service
public class UsageLimitServiceImpl implements UsageLimitService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageLimitServiceImpl.class));
private final TenantEntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final ConventionService conventionService;
private final ErrorThesaurusProperties errors;
private final MessageSource messageSource;
private final UserScope userScope;
@Autowired
public UsageLimitServiceImpl(
TenantEntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
ErrorThesaurusProperties errors,
MessageSource messageSource,
UserScope userScope) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.conventionService = conventionService;
this.errors = errors;
this.messageSource = messageSource;
this.userScope = userScope;
}
public UsageLimit persist(UsageLimitPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting data tag").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditUsageLimit);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
UsageLimitEntity data;
if (isUpdate) {
data = this.entityManager.find(UsageLimitEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tag.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
} else {
data = new UsageLimitEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
}
data.setLabel(model.getLabel());
data.setMetricValue(model.getMetricValue());
data.setValue(model.getValue());
data.setUpdatedAt(Instant.now());
if (isUpdate)
this.entityManager.merge(data);
else
this.entityManager.persist(data);
this.entityManager.flush();
return this.builderFactory.builder(UsageLimitBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(BaseFieldSet.build(fields, UsageLimit._id), data);
}
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting UsageLimit: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteUsageLimit);
this.deleterFactory.deleter(UsageLimitDeleter.class).deleteAndSaveByIds(List.of(id));
}
public void checkIncrease(UsageLimitMetricValue metric) {
//TODO
}
}

View File

@ -0,0 +1,142 @@
package org.opencdmp.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidationFilterAnnotation;
import jakarta.transaction.Transactional;
import jakarta.xml.bind.JAXBException;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.data.UsageLimitEntity;
import org.opencdmp.model.UsageLimit;
import org.opencdmp.model.builder.UsageLimitBuilder;
import org.opencdmp.model.censorship.UsageLimitsCensor;
import org.opencdmp.model.persist.UsageLimitPersist;
import org.opencdmp.model.result.QueryResult;
import org.opencdmp.query.UsageLimitQuery;
import org.opencdmp.query.lookup.UsageLimitLookup;
import org.opencdmp.service.usagelimit.UsageLimitService;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.*;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.management.InvalidApplicationException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping(path = "api/usage-limit")
public class UsageFilterController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UsageFilterController.class));
private final BuilderFactory builderFactory;
private final AuditService auditService;
private final UsageLimitService usageLimitService;
private final CensorFactory censorFactory;
private final QueryFactory queryFactory;
private final MessageSource messageSource;
public UsageFilterController(
BuilderFactory builderFactory,
AuditService auditService,
UsageLimitService usageLimitService,
CensorFactory censorFactory,
QueryFactory queryFactory,
MessageSource messageSource) {
this.builderFactory = builderFactory;
this.auditService = auditService;
this.usageLimitService = usageLimitService;
this.censorFactory = censorFactory;
this.queryFactory = queryFactory;
this.messageSource = messageSource;
}
@PostMapping("query")
public QueryResult<UsageLimit> query(@RequestBody UsageLimitLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
logger.debug("querying {}", UsageLimit.class.getSimpleName());
this.censorFactory.censor(UsageLimitsCensor.class).censor(lookup.getProject());
UsageLimitQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.AllExceptPublic);
List<UsageLimitEntity> data = query.collectAs(lookup.getProject());
List<UsageLimit> models = this.builderFactory.builder(UsageLimitBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.UsageLimit_Query, "lookup", lookup);
return new QueryResult<>(models, count);
}
@GetMapping("{id}")
public UsageLimit get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
logger.debug(new MapLogEntry("retrieving" + UsageLimit.class.getSimpleName()).And("id", id).And("fields", fieldSet));
this.censorFactory.censor(UsageLimitsCensor.class).censor(fieldSet);
UsageLimitQuery query = this.queryFactory.query(UsageLimitQuery.class).disableTracking().authorize(AuthorizationFlags.AllExceptPublic).ids(id);
UsageLimit model = this.builderFactory.builder(UsageLimitBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, UsageLimit.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.auditService.track(AuditableAction.UsageLimit_Lookup, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
));
return model;
}
@PostMapping("persist")
@Transactional
@ValidationFilterAnnotation(validator = UsageLimitPersist.UsageLimitPersistValidator.ValidatorName, argumentName = "model")
public UsageLimit persist(@RequestBody UsageLimitPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
logger.debug(new MapLogEntry("persisting" + UsageLimit.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
this.censorFactory.censor(UsageLimitsCensor.class).censor(fieldSet);
UsageLimit persisted = this.usageLimitService.persist(model, fieldSet);
this.auditService.track(AuditableAction.UsageLimit_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
));
return persisted;
}
@DeleteMapping("{id}")
@Transactional
public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug(new MapLogEntry("retrieving" + UsageLimit.class.getSimpleName()).And("id", id));
this.usageLimitService.deleteAndSave(id);
this.auditService.track(AuditableAction.UsageLimit_Delete, "id", id);
}
}

View File

@ -1063,6 +1063,28 @@ permissions:
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# Tenant Permissions
BrowseUsageLimit:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditUsageLimit:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUsageLimit:
roles:
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Status # Status
BrowseStatus: BrowseStatus:
roles: roles:
@ -1179,6 +1201,12 @@ permissions:
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
ViewUsageLimitPage:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ViewDescriptionTemplatePage: ViewDescriptionTemplatePage:
roles: roles:
- Admin - Admin

View File

@ -4,7 +4,7 @@ BEGIN
PERFORM * FROM "DBVersion" WHERE version = this_version; PERFORM * FROM "DBVersion" WHERE version = this_version;
IF FOUND THEN RETURN; END IF; IF FOUND THEN RETURN; END IF;
CREATE TABLE public."UsageLimits" CREATE TABLE public."UsageLimit"
( (
id uuid NOT NULL, id uuid NOT NULL,
label character varying NOT NULL, label character varying NOT NULL,
@ -15,13 +15,13 @@ BEGIN
is_active smallint NOT NULL, is_active smallint NOT NULL,
tenant uuid, tenant uuid,
PRIMARY KEY (id), PRIMARY KEY (id),
CONSTRAINT "UsageLimits_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "UsageLimit_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
NOT VALID NOT VALID
); );
INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.067', '2024-11-07 12:00:00.000000+02', now(), 'Add UsageLimits table.'); INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.067', '2024-11-07 12:00:00.000000+02', now(), 'Add UsageLimit table.');
END$$; END$$;