diff --git a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java index 3624dcfbd..7de6d743b 100644 --- a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java +++ b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java @@ -207,6 +207,9 @@ public class AuditableAction { public static final EventId DescriptionStatus_Persist = new EventId(400002, "DescriptionStatus_Persist"); public static final EventId DescriptionStatus_Delete = new EventId(400003, "DescriptionStatus_Delete"); - + public static final EventId PlanWorkflow_Query = new EventId(500000, "PlanWorkflow_Query"); + public static final EventId PlanWorkflow_Lookup = new EventId(500001, "PlanWorkflow_Lookup"); + public static final EventId PlanWorkflow_Persist = new EventId(500002, "PlanWorkflow_Persist"); + public static final EventId PlanWorkflow_Delete = new EventId(500003, "PlanWorkflow_Delete"); } diff --git a/backend/core/src/main/java/org/opencdmp/authorization/Permission.java b/backend/core/src/main/java/org/opencdmp/authorization/Permission.java index 8937fd780..18ac3a45e 100644 --- a/backend/core/src/main/java/org/opencdmp/authorization/Permission.java +++ b/backend/core/src/main/java/org/opencdmp/authorization/Permission.java @@ -95,6 +95,11 @@ public final class Permission { public static String EditPlanStatus = "EditPlanStatus"; public static String DeletePlanStatus = "DeletePlanStatus"; + //PlanWorkflow + public static String BrowsePlanWorkflow = "BrowsePlanWorkflow"; + public static String EditPlanWorkflow = "EditPlanWorkflow"; + public static String DeletePlanWorkflow = "DeletePlanWorkflow"; + //DescriptionStatus public static String BrowseDescriptionStatus = "BrowseDescriptionStatus"; public static String EditDescriptionStatus = "EditDescriptionStatus"; @@ -251,6 +256,7 @@ public final class Permission { public static String ViewTenantConfigurationPage = "ViewTenantConfigurationPage"; public static String ViewStatusPage = "ViewStatusPage"; public static String ViewPlanStatusPage = "ViewPlanStatusPage"; + public static String ViewPlanWorkflowPage = "ViewPlanWorkflowPage"; public static String ViewDescriptionStatusPage = "ViewPlanStatusPage"; } diff --git a/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitTargetMetric.java b/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitTargetMetric.java index 522bd5ff0..5e19747b9 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitTargetMetric.java +++ b/backend/core/src/main/java/org/opencdmp/commons/enums/UsageLimitTargetMetric.java @@ -9,6 +9,7 @@ public enum UsageLimitTargetMetric implements DatabaseEnum { USER_COUNT(TargetMetrics.UserCount), PLAN_COUNT(TargetMetrics.PlanCount), PLAN_STATUS_COUNT(TargetMetrics.PlanStatusCount), + PLAN_WORKFLOW_COUNT(TargetMetrics.PLAN_WORKFLOW_COUNT), BLUEPRINT_COUNT(TargetMetrics.BlueprintCount), DESCRIPTION_COUNT(TargetMetrics.DescriptionCount), DESCRIPTION_STATUS_COUNT(TargetMetrics.DescriptionStatusCount), @@ -37,6 +38,7 @@ public enum UsageLimitTargetMetric implements DatabaseEnum { public static final String UserCount = "user_count"; public static final String PlanCount = "plan_count"; public static final String PlanStatusCount = "plan_status_count"; + public static final String PLAN_WORKFLOW_COUNT = "plan_workflow_count"; public static final String BlueprintCount = "blueprint_count"; public static final String DescriptionCount = "description_count"; public static final String DescriptionStatusCount = "description_status_count"; diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionEntity.java b/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionEntity.java new file mode 100644 index 000000000..a04f16c39 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionEntity.java @@ -0,0 +1,27 @@ +package org.opencdmp.commons.types.planworkflow; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; + +import java.util.List; +import java.util.UUID; + +@XmlRootElement(name = "definition") +@XmlAccessorType(XmlAccessType.FIELD) +public class PlanWorkflowDefinitionEntity { + + private UUID startingStatusId; + public final static String _startingStatusId = "startingStatusId"; + + private List statusTransitions; + public final static String _statusTransitions = "statusTransitions"; + + public UUID getStartingStatusId() { return startingStatusId; } + + public void setStartingStatusId(UUID startingStatusId) { this.startingStatusId = startingStatusId; } + + public List getStatusTransitions() { return statusTransitions; } + + public void setStatusTransitions(List statusTransitions) { this.statusTransitions = statusTransitions; } +} diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionTransitionEntity.java b/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionTransitionEntity.java new file mode 100644 index 000000000..e50d18218 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/commons/types/planworkflow/PlanWorkflowDefinitionTransitionEntity.java @@ -0,0 +1,29 @@ +package org.opencdmp.commons.types.planworkflow; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; + +import java.util.UUID; + +@XmlRootElement(name = "statusTransitions") +@XmlAccessorType(XmlAccessType.FIELD) +public class PlanWorkflowDefinitionTransitionEntity { + + @XmlElement(name = "fromStatusId") + private UUID fromStatusId; + public final static String _fromStatusId = "fromStatusId"; + + @XmlElement(name = "toStatusId") + private UUID toStatusId; + public final static String _toStatusId = "toStatusId"; + + public UUID getFromStatusId() { return fromStatusId; } + + public void setFromStatusId(UUID fromStatusId) { this.fromStatusId = fromStatusId; } + + public UUID getToStatusId() { return toStatusId; } + + public void setToStatusId(UUID toStatusId) { this.toStatusId = toStatusId; } +} diff --git a/backend/core/src/main/java/org/opencdmp/data/PlanWorkflowEntity.java b/backend/core/src/main/java/org/opencdmp/data/PlanWorkflowEntity.java new file mode 100644 index 000000000..237f2bc95 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/data/PlanWorkflowEntity.java @@ -0,0 +1,76 @@ +package org.opencdmp.data; + +import jakarta.persistence.*; +import org.hibernate.annotations.Type; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.data.converters.enums.IsActiveConverter; +import org.opencdmp.data.tenant.TenantScopedBaseEntity; +import org.opencdmp.data.types.SQLXMLType; + +import java.time.Instant; +import java.util.UUID; + +@Entity +@Table(name = "\"PlanWorkflow\"") +public class PlanWorkflowEntity extends TenantScopedBaseEntity { + @Id + @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) + private UUID id; + public static final String _id = "id"; + + @Column(name = "name", length = PlanWorkflowEntity._nameLength, nullable = false) + private String name; + public static final String _name = "name"; + public static final int _nameLength = 250; + + @Column(name = "description", nullable = true) + private String description; + public static final String _description = "description"; + + @Column(name = "created_at", nullable = true) + private Instant createdAt; + public static final String _createdAt = "createdAt"; + + @Column(name = "updated_at", nullable = true) + private Instant updatedAt; + public static final String _updatedAt = "updatedAt"; + + @Column(name = "is_active", nullable = false) + @Convert(converter = IsActiveConverter.class) + private IsActive isActive; + public static final String _isActive = "isActive"; + + @Type(SQLXMLType.class) + @Column(name = "definition", nullable = false, columnDefinition = "xml") + private String definition; + public static final String _definition = "definition"; + + + public UUID getId() { return id; } + + public void setId(UUID id) { this.id = id; } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + 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 IsActive getIsActive() { return isActive; } + + public void setIsActive(IsActive isActive) { this.isActive = isActive; } + + public String getDefinition() { return definition; } + + public void setDefinition(String definition) { this.definition = definition; } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowBuilder.java new file mode 100644 index 000000000..238422349 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowBuilder.java @@ -0,0 +1,77 @@ +package org.opencdmp.model.builder.planworkflow; + +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.planworkflow.PlanWorkflowDefinitionEntity; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.data.PlanWorkflowEntity; +import org.opencdmp.model.builder.BaseBuilder; +import org.opencdmp.model.planworkflow.PlanWorkflow; +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.*; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowBuilder extends BaseBuilder { + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + private final XmlHandlingService xmlHandlingService; + private final BuilderFactory builderFactory; + private final TenantScope tenantScope; + + + public PlanWorkflowBuilder(ConventionService conventionService, XmlHandlingService xmlHandlingService, BuilderFactory builderFactory, TenantScope tenantScope) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(PlanWorkflowBuilder.class))); + this.xmlHandlingService = xmlHandlingService; + this.builderFactory = builderFactory; + this.tenantScope = tenantScope; + } + + public PlanWorkflowBuilder 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 || fields.isEmpty() || data == null) + return new ArrayList<>(); + + List models = new ArrayList<>(); + + FieldSet definitionFields = fields.extractPrefixed(this.asIndexer(PlanWorkflow._definition)); + + for (PlanWorkflowEntity d : data) { + PlanWorkflow m = new PlanWorkflow(); + if (fields.hasField(this.asIndexer(PlanWorkflow._id))) m.setId(d.getId()); + if (fields.hasField(this.asIndexer(PlanWorkflow._name))) m.setName(d.getName()); + if (fields.hasField(this.asIndexer(PlanWorkflow._description))) m.setDescription(d.getDescription()); + if (fields.hasField(this.asIndexer(PlanWorkflow._createdAt))) m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(PlanWorkflow._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(PlanWorkflow._isActive))) m.setIsActive(d.getIsActive()); + if (fields.hasField(this.asIndexer(PlanWorkflow._belongsToCurrentTenant))) m.setBelongsToCurrentTenant(this.getBelongsToCurrentTenant(d, this.tenantScope)); + + if (definitionFields != null && d.getDefinition() != null && !d.getDefinition().isBlank()) { + PlanWorkflowDefinitionEntity definitionData = this.xmlHandlingService.fromXmlSafe(PlanWorkflowDefinitionEntity.class, d.getDefinition()); + m.setDefinition(this.builderFactory.builder(PlanWorkflowDefinitionBuilder.class).authorize(this.authorize).build(definitionFields, definitionData)); + } + + models.add(m); + } + + return models; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionBuilder.java new file mode 100644 index 000000000..4979fc64a --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionBuilder.java @@ -0,0 +1,87 @@ +package org.opencdmp.model.builder.planworkflow; + +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +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.planworkflow.PlanWorkflowDefinitionEntity; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.model.builder.BaseBuilder; +import org.opencdmp.model.builder.planstatus.PlanStatusBuilder; +import org.opencdmp.model.planstatus.PlanStatus; +import org.opencdmp.model.planworkflow.PlanWorkflowDefinition; +import org.opencdmp.query.PlanStatusQuery; +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.*; +import java.util.stream.Collectors; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowDefinitionBuilder extends BaseBuilder { + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + private final BuilderFactory builderFactory; + private final QueryFactory queryFactory; + + public PlanWorkflowDefinitionBuilder(ConventionService conventionService, BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(PlanWorkflowDefinitionBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + public PlanWorkflowDefinitionBuilder authorize(EnumSet values) { + 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 || fields.isEmpty() || data == null) + return new ArrayList<>(); + + List models = new ArrayList<>(); + + FieldSet startingStatusFields = fields.extractPrefixed(PlanWorkflowDefinition._startingStatus); + Map startingStatusItemsMap = this.collectPlanStatuses(startingStatusFields, data); + + FieldSet transitionsFields = fields.extractPrefixed(this.asPrefix(PlanWorkflowDefinition._statusTransitions)); + + for (PlanWorkflowDefinitionEntity d : data) { + PlanWorkflowDefinition m = new PlanWorkflowDefinition(); + if (startingStatusItemsMap != null && !startingStatusFields.isEmpty() && startingStatusItemsMap.containsKey(d.getStartingStatusId())) m.setStartingStatus(startingStatusItemsMap.get(d.getStartingStatusId())); + if (transitionsFields != null && !d.getStatusTransitions().isEmpty()) m.setStatusTransitions(this.builderFactory.builder(PlanWorkflowDefinitionTransitionBuilder.class).build(transitionsFields, d.getStatusTransitions())); + + models.add(m); + } + + return models; + } + + private Map collectPlanStatuses(FieldSet fields, List data) { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", PlanStatus.class.getSimpleName()); + + Map itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(PlanStatus._id)); + PlanStatusQuery q = this.queryFactory.query(PlanStatusQuery.class).disableTracking().authorize(this.authorize).ids(data.stream().map(PlanWorkflowDefinitionEntity::getStartingStatusId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(PlanStatusBuilder.class).authorize(this.authorize).asForeignKey(q, clone, PlanStatus::getId); + + if (!fields.hasField(this.asIndexer(PlanStatus._id))) + itemMap.values().stream().filter(Objects::nonNull).forEach(x -> { x.setId(null); }); + + return itemMap; + + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionTransitionBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionTransitionBuilder.java new file mode 100644 index 000000000..c255add85 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/builder/planworkflow/PlanWorkflowDefinitionTransitionBuilder.java @@ -0,0 +1,89 @@ +package org.opencdmp.model.builder.planworkflow; + +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.fieldset.BaseFieldSet; +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.planworkflow.PlanWorkflowDefinitionTransitionEntity; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.model.builder.BaseBuilder; +import org.opencdmp.model.builder.planstatus.PlanStatusBuilder; +import org.opencdmp.model.planstatus.PlanStatus; +import org.opencdmp.model.planworkflow.PlanWorkflowDefinitionTransition; +import org.opencdmp.query.PlanStatusQuery; +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.*; +import java.util.stream.Collectors; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowDefinitionTransitionBuilder extends BaseBuilder { + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + private final BuilderFactory builderFactory; + + private final QueryFactory queryFactory; + + public PlanWorkflowDefinitionTransitionBuilder authorize(EnumSet values) { + this.authorize = values; + return this; + } + + public PlanWorkflowDefinitionTransitionBuilder(ConventionService conventionService, BuilderFactory builderFactory, QueryFactory queryFactory) { + super(conventionService, new LoggerService(LoggerFactory.getLogger(PlanWorkflowDefinitionTransitionBuilder.class))); + this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + } + + @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<>(); + + FieldSet fromStatusFields = fields.extractPrefixed(PlanWorkflowDefinitionTransition._fromStatus); + Map fromStatusItemsMap = this.collectPlanStatuses(fromStatusFields, data, data.stream().map(PlanWorkflowDefinitionTransitionEntity::getFromStatusId).distinct().collect(Collectors.toList())); + + FieldSet toStatusFields = fields.extractPrefixed(PlanWorkflowDefinitionTransition._fromStatus); + Map toStatusItemsMap = this.collectPlanStatuses(toStatusFields, data, data.stream().map(PlanWorkflowDefinitionTransitionEntity::getToStatusId).distinct().collect(Collectors.toList())); + + for (PlanWorkflowDefinitionTransitionEntity d : data) { + PlanWorkflowDefinitionTransition m = new PlanWorkflowDefinitionTransition(); + if (fromStatusItemsMap != null && !fromStatusFields.isEmpty() && fromStatusItemsMap.containsKey(d.getFromStatusId())) m.setFromStatus(fromStatusItemsMap.get(d.getFromStatusId())); + if (toStatusItemsMap != null && !toStatusFields.isEmpty() && toStatusItemsMap.containsKey(d.getToStatusId())) m.setToStatus(toStatusItemsMap.get(d.getToStatusId())); + + models.add(m); + } + + return models; + } + + private Map collectPlanStatuses(FieldSet fields, List data, Collection planStatusIds) { + if (fields.isEmpty() || data.isEmpty()) + return null; + this.logger.debug("checking related - {}", PlanStatus.class.getSimpleName()); + + Map itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(PlanStatus._id)); + PlanStatusQuery q = this.queryFactory.query(PlanStatusQuery.class).disableTracking().authorize(this.authorize).ids(planStatusIds); + itemMap = this.builderFactory.builder(PlanStatusBuilder.class).authorize(this.authorize).asForeignKey(q, clone, PlanStatus::getId); + + if (!fields.hasField(this.asIndexer(PlanStatus._id))) + itemMap.values().stream().filter(Objects::nonNull).forEach(x -> { x.setId(null); }); + + return itemMap; + + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowCensor.java b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowCensor.java new file mode 100644 index 000000000..4072f00f5 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowCensor.java @@ -0,0 +1,44 @@ +package org.opencdmp.model.censorship.planworkflow; + +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.planworkflow.PlanWorkflow; +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 +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowCensor.class)); + + private final AuthorizationService authService; + private final CensorFactory censorFactory; + + public PlanWorkflowCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorize(Permission.BrowsePlanWorkflow); + + FieldSet definitionFields = fields.extractPrefixed(this.asIndexerPrefix(PlanWorkflow._definition)); + this.censorFactory.censor(PlanWorkflowDefinitionCensor.class).censor(definitionFields, userId); + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionCensor.java b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionCensor.java new file mode 100644 index 000000000..8b575269e --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionCensor.java @@ -0,0 +1,40 @@ +package org.opencdmp.model.censorship.planworkflow; + +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.convention.ConventionService; +import org.opencdmp.model.censorship.BaseCensor; +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 +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowDefinitionCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowDefinitionCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + + public PlanWorkflowDefinitionCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionTransitionCensor.java b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionTransitionCensor.java new file mode 100644 index 000000000..bb51a8f3b --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/censorship/planworkflow/PlanWorkflowDefinitionTransitionCensor.java @@ -0,0 +1,44 @@ +package org.opencdmp.model.censorship.planworkflow; + +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.planworkflow.PlanWorkflowDefinition; +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 +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowDefinitionTransitionCensor extends BaseCensor { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowDefinitionTransitionCensor.class)); + + protected final AuthorizationService authService; + protected final CensorFactory censorFactory; + + public PlanWorkflowDefinitionTransitionCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) { + super(conventionService); + this.authService = authService; + this.censorFactory = censorFactory; + } + + public void censor(FieldSet fields, UUID userId) { + logger.debug(new DataLogEntry("censoring fields", fields)); + if (fields == null || fields.isEmpty()) + return; + + this.authService.authorizeForce(Permission.BrowsePlanWorkflow); + + FieldSet transitionsFields = fields.extractPrefixed(this.asIndexerPrefix(PlanWorkflowDefinition._statusTransitions)); + this.censorFactory.censor(PlanWorkflowDefinitionTransitionCensor.class).censor(transitionsFields, userId); + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/deleter/PlanWorkflowDeleter.java b/backend/core/src/main/java/org/opencdmp/model/deleter/PlanWorkflowDeleter.java new file mode 100644 index 000000000..f52e46451 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/deleter/PlanWorkflowDeleter.java @@ -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.commons.enums.UsageLimitTargetMetric; +import org.opencdmp.data.PlanWorkflowEntity; +import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.query.PlanWorkflowQuery; +import org.opencdmp.service.accounting.AccountingService; +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(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class PlanWorkflowDeleter implements Deleter { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowDeleter.class)); + + private final TenantEntityManager entityManager; + + protected final QueryFactory queryFactory; + + protected final DeleterFactory deleterFactory; + + protected final AccountingService accountingService; + + @Autowired + public PlanWorkflowDeleter(TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory, AccountingService accountingService) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + this.deleterFactory = deleterFactory; + this.accountingService = accountingService; + } + + public void deleteAndSaveByIds(List ids) { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids)); + List data = this.queryFactory.query(PlanWorkflowQuery.class).ids(ids).collect(); + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + + } + + public void deleteAndSave(List 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 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 (PlanWorkflowEntity 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"); + this.accountingService.decrease(UsageLimitTargetMetric.PLAN_WORKFLOW_COUNT.getValue()); + } + + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionPersist.java new file mode 100644 index 000000000..43d96216e --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionPersist.java @@ -0,0 +1,75 @@ +package org.opencdmp.model.persist.planworkflow; + +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.validation.ValidatorFactory; +import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.validation.BaseValidator; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.slf4j.LoggerFactory; +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 PlanWorkflowDefinitionPersist { + + public final static String _startingStatusId = "startingStatusId"; + private UUID startingStatusId; + + public final static String _statusTransitions = "statusTransitions"; + private List statusTransitions = null; + + public UUID getStartingStatusId() { return startingStatusId; } + + public void setStartingStatusId(UUID startingStatusId) { this.startingStatusId = startingStatusId; } + + public List getStatusTransitions() { return statusTransitions; } + + public void setStatusTransitions(List statusTransitions) { this.statusTransitions = statusTransitions; } + + @Component(PlanWorkflowDefinitionPersist.PlanWorkflowDefinitionPersistValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class PlanWorkflowDefinitionPersistValidator extends BaseValidator { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowDefinitionPersistValidator.class)); + + public static final String ValidatorName = "PlanWorkflowPersistValidator.PlanWorkflowDefinitionPersistValidator"; + + private final MessageSource messageSource; + private final ValidatorFactory validatorFactory; + + protected PlanWorkflowDefinitionPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { + super(conventionService, errors); + this.messageSource = messageSource; + this.validatorFactory = validatorFactory; + } + + @Override + protected Class modelClass() { return PlanWorkflowDefinitionPersist.class; } + + @Override + protected List specifications(PlanWorkflowDefinitionPersist item) { + return Arrays.asList( + this.spec() + .must(() -> this.isValidGuid(item.getStartingStatusId())) + .failOn(PlanWorkflowDefinitionPersist._startingStatusId) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowDefinitionPersist._startingStatusId}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isListNullOrEmpty(item.getStatusTransitions())) + .failOn(PlanWorkflowDefinitionPersist._startingStatusId) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowDefinitionPersist._startingStatusId}, LocaleContextHolder.getLocale())), + this.navSpec() + .iff(() -> !this.isListNullOrEmpty(item.getStatusTransitions())) + .on(PlanWorkflowDefinitionPersist._statusTransitions) + .over(item.getStatusTransitions()) + .using((itm) -> this.validatorFactory.validator(PlanWorkflowDefinitionTransitionPersist.PlanWorkflowDefinitionTransitionPersistValidator.class)) + ); + } + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionTransitionPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionTransitionPersist.java new file mode 100644 index 000000000..59506891b --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowDefinitionTransitionPersist.java @@ -0,0 +1,64 @@ +package org.opencdmp.model.persist.planworkflow; + +import gr.cite.tools.validation.specification.Specification; +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; +import java.util.UUID; + +public class PlanWorkflowDefinitionTransitionPersist { + + public final static String _fromStatusId = "fromStatusId"; + private UUID fromStatusId; + + public final static String _toStatusId = "toStatusId"; + private UUID toStatusId; + + public UUID getFromStatusId() { return fromStatusId; } + + public void setFromStatusId(UUID fromStatusId) { this.fromStatusId = fromStatusId; } + + public UUID getToStatusId() { return toStatusId; } + + public void setToStatusId(UUID toStatusId) { this.toStatusId = toStatusId; } + + + @Component(PlanWorkflowDefinitionTransitionPersist.PlanWorkflowDefinitionTransitionPersistValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class PlanWorkflowDefinitionTransitionPersistValidator extends BaseValidator { + + public static final String ValidatorName = "PlanWorkflowPersistValidator.PlanWorkflowDefinitionPersistValidator.PlanWorkflowDefinitionTransitionPersistValidator"; + + private final MessageSource messageSource; + + protected PlanWorkflowDefinitionTransitionPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { return PlanWorkflowDefinitionTransitionPersist.class; } + + @Override + protected List specifications(PlanWorkflowDefinitionTransitionPersist item) { + return Arrays.asList( + this.spec() + .must(() -> this.isValidGuid(item.getFromStatusId())) + .failOn(PlanWorkflowDefinitionTransitionPersist._fromStatusId) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowDefinitionTransitionPersist._fromStatusId}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> this.isValidGuid(item.getToStatusId())) + .failOn(PlanWorkflowDefinitionTransitionPersist._toStatusId) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowDefinitionTransitionPersist._toStatusId}, LocaleContextHolder.getLocale())) + ); + } + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowPersist.java new file mode 100644 index 000000000..1294666ba --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/planworkflow/PlanWorkflowPersist.java @@ -0,0 +1,114 @@ +package org.opencdmp.model.persist.planworkflow; + +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.validation.ValidatorFactory; +import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.commons.validation.BaseValidator; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.slf4j.LoggerFactory; +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 PlanWorkflowPersist { + + private UUID id; + + public static final String _name = "name"; + private String name; + + public static final String _description = "description"; + private String description; + + public static final String _isActive = "isActive"; + private IsActive isActive; + + public static final String _hash = "hash"; + private String hash; + + public static final String _definition = "definition"; + private PlanWorkflowDefinitionPersist definition; + + + public UUID getId() { return id; } + + public void setId(UUID id) { this.id = id; } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public IsActive getIsActive() { return isActive; } + + public void setIsActive(IsActive isActive) { this.isActive = isActive; } + + public String getHash() { return hash; } + + public void setHash(String hash) { this.hash = hash; } + + public PlanWorkflowDefinitionPersist getDefinition() { return definition; } + + public void setDefinition(PlanWorkflowDefinitionPersist definition) { this.definition = definition; } + + @Component(PlanWorkflowPersist.PlanWorkflowPersistValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class PlanWorkflowPersistValidator extends BaseValidator { + + public static final String ValidatorName = "PlanWorkflowPersistValidator"; + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowPersistValidator.class)); + + private final MessageSource messageSource; + private final ValidatorFactory validatorFactory; + + protected PlanWorkflowPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { + super(conventionService, errors); + this.messageSource = messageSource; + this.validatorFactory = validatorFactory; + } + + @Override + protected Class modelClass() { return PlanWorkflowPersist.class; } + + @Override + protected List specifications(PlanWorkflowPersist item) { + return Arrays.asList( + this.spec() + .iff(() -> this.isValidGuid(item.getId())) + .must(() -> this.isValidHash(item.getHash())) + .failOn(PlanWorkflowPersist._hash) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowPersist._hash}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isValidGuid(item.getId())) + .must(() -> !this.isValidHash(item.getHash())) + .failOn(PlanWorkflowPersist._hash) + .failWith(this.messageSource.getMessage("Validation_OverPosting", new Object[]{PlanWorkflowPersist._hash}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getName()) && !this.isEmpty(item.getName())) + .failOn(PlanWorkflowPersist._name) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowPersist._name}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getIsActive())) + .failOn(PlanWorkflowPersist._isActive) + .failWith(this.messageSource.getMessage("Validation_Required", new Object[]{PlanWorkflowPersist._isActive}, LocaleContextHolder.getLocale())), + this.refSpec() + .iff(() -> !this.isNull(item.getDefinition())) + .on(PlanWorkflowPersist._definition) + .over(item.getDefinition()) + .using(() -> this.validatorFactory.validator(PlanWorkflowDefinitionPersist.PlanWorkflowDefinitionPersistValidator.class)) + ); + } + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflow.java b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflow.java new file mode 100644 index 000000000..be1605941 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflow.java @@ -0,0 +1,73 @@ +package org.opencdmp.model.planworkflow; + +import org.opencdmp.commons.enums.IsActive; + +import java.time.Instant; +import java.util.UUID; + +public class PlanWorkflow { + + public final static String _id = "id"; + private UUID id; + + public final static String _name = "name"; + private String name; + + public final static String _description = "description"; + private String description; + + public final static String _createdAt = "createdAt"; + private Instant createdAt; + + public final static String _updatedAt = "updatedAt"; + private Instant updatedAt; + + public final static String _isActive = "isActive"; + private IsActive isActive; + + public final static String _definition = "definition"; + private PlanWorkflowDefinition definition; + + public final static String _hash = "hash"; + private String hash; + + public final static String _belongsToCurrentTenant = "belongsToCurrentTenant"; + private Boolean belongsToCurrentTenant; + + + public UUID getId() { return this.id; } + + public void setId(UUID id) { this.id = id; } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + 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 IsActive getIsActive() { return isActive; } + + public void setIsActive(IsActive isActive) { this.isActive = isActive; } + + public PlanWorkflowDefinition getDefinition() { return definition; } + + public void setDefinition(PlanWorkflowDefinition definition) { this.definition = definition; } + + 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; } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinition.java b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinition.java new file mode 100644 index 000000000..206c73466 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinition.java @@ -0,0 +1,23 @@ +package org.opencdmp.model.planworkflow; + +import org.opencdmp.model.planstatus.PlanStatus; + +import java.util.List; + +public class PlanWorkflowDefinition { + + public final static String _startingStatus = "startingStatus"; + private PlanStatus startingStatus; + + public final static String _statusTransitions = "statusTransitions"; + private List statusTransitions; + + + public PlanStatus getStartingStatus() { return startingStatus; } + + public void setStartingStatus(PlanStatus startingStatus) { this.startingStatus = startingStatus; } + + public List getStatusTransitions() { return statusTransitions; } + + public void setStatusTransitions(List statusTransitions) { this.statusTransitions = statusTransitions; } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinitionTransition.java b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinitionTransition.java new file mode 100644 index 000000000..c68b92b60 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/planworkflow/PlanWorkflowDefinitionTransition.java @@ -0,0 +1,21 @@ +package org.opencdmp.model.planworkflow; + +import org.opencdmp.model.planstatus.PlanStatus; + +public class PlanWorkflowDefinitionTransition { + + public final static String _fromStatus = "fromStatus"; + private PlanStatus fromStatus; + + public final static String _toStatus = "toStatus"; + private PlanStatus toStatus; + + + public PlanStatus getFromStatus() { return fromStatus; } + + public void setFromStatus(PlanStatus fromStatus) { this.fromStatus = fromStatus; } + + public PlanStatus getToStatus() { return toStatus; } + + public void setToStatus(PlanStatus toStatus) { this.toStatus = toStatus; } +} diff --git a/backend/core/src/main/java/org/opencdmp/query/PlanWorkflowQuery.java b/backend/core/src/main/java/org/opencdmp/query/PlanWorkflowQuery.java new file mode 100644 index 000000000..2a6d525ec --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/query/PlanWorkflowQuery.java @@ -0,0 +1,201 @@ +package org.opencdmp.query; + +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.data.PlanWorkflowEntity; +import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.model.planworkflow.PlanWorkflow; +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 PlanWorkflowQuery extends QueryBase { + + private String like; + + private Collection ids; + + private Collection isActives; + + private Collection excludedIds; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + protected boolean noTracking; + + + public PlanWorkflowQuery like(String value) { + this.like = like; + return this; + } + + public PlanWorkflowQuery ids(UUID value) { + this.ids = List.of(value); + return this; + } + + public PlanWorkflowQuery ids(UUID... value) { + this.ids = Arrays.asList(value); + return this; + } + + public PlanWorkflowQuery ids(Collection values) { + this.ids = values; + return this; + } + + public PlanWorkflowQuery isActives(IsActive value) { + this.isActives = List.of(value); + return this; + } + + public PlanWorkflowQuery isActives(IsActive... value) { + this.isActives = Arrays.asList(value); + return this; + } + + public PlanWorkflowQuery isActives(Collection values) { + this.isActives = values; + return this; + } + + public PlanWorkflowQuery excludedIds(UUID value) { + this.excludedIds = List.of(value); + return this; + } + + public PlanWorkflowQuery excludedIds(UUID... value) { + this.excludedIds = Arrays.asList(value); + return this; + } + + public PlanWorkflowQuery excludedIds(Collection values) { + this.excludedIds = values; + return this; + } + + public PlanWorkflowQuery authorize(EnumSet values) { + this.authorize = values; + return this; + } + + public PlanWorkflowQuery enableTracking() { + this.noTracking = false; + return this; + } + + public PlanWorkflowQuery disableTracking() { + this.noTracking = true; + return this; + } + + private final QueryUtilsService queryUtilsService; + + private final TenantEntityManager entityManager; + + public PlanWorkflowQuery(QueryUtilsService queryUtilsService, TenantEntityManager entityManager) { + this.queryUtilsService = queryUtilsService; + this.entityManager = entityManager; + } + + @Override + protected EntityManager entityManager(){ + return this.entityManager.getEntityManager(); + } + + @Override + protected Boolean isFalseQuery() { + return this.isEmpty(this.ids) || this.isEmpty(this.isActives); + } + + @Override + protected Class entityClass() { + return PlanWorkflowEntity.class; + } + + @Override + protected Predicate applyFilters(QueryContext queryContext) { + List predicates = new ArrayList<>(); + if (this.ids != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(PlanWorkflowEntity._id)); + for (UUID item : this.ids) + inClause.value(item); + predicates.add(inClause); + } + if (this.like != null && !this.like.isBlank()) { + predicates.add(queryContext.CriteriaBuilder.or( + this.queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(PlanWorkflowEntity._name), this.like), + this.queryUtilsService.ilike(queryContext.CriteriaBuilder, queryContext.Root.get(PlanWorkflowEntity._description), this.like) + )); + } + if (this.isActives != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(PlanWorkflowEntity._isActive)); + for (IsActive item : this.isActives) + inClause.value(item); + predicates.add(inClause); + } + if (this.excludedIds != null) { + CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(PlanWorkflowEntity._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 String fieldNameOf(FieldResolver item) { + if (item.match(PlanWorkflow._id)) + return PlanWorkflowEntity._id; + else if (item.match(PlanWorkflow._name)) + return PlanWorkflowEntity._name; + else if (item.match(PlanWorkflow._description)) + return PlanWorkflowEntity._description; + else if (item.match(PlanWorkflow._createdAt)) + return PlanWorkflowEntity._createdAt; + else if (item.match(PlanWorkflow._updatedAt)) + return PlanWorkflowEntity._updatedAt; + else if (item.match(PlanWorkflow._isActive)) + return PlanWorkflowEntity._isActive; + else if (item.match(PlanWorkflow._definition)) + return PlanWorkflowEntity._definition; + else if (item.match(PlanWorkflow._hash)) + return PlanWorkflowEntity._updatedAt; + else if (item.match(PlanWorkflow._belongsToCurrentTenant)) + return PlanWorkflowEntity._tenantId; + return null; + } + + @Override + protected PlanWorkflowEntity convert(Tuple tuple, Set columns) { + PlanWorkflowEntity item = new PlanWorkflowEntity(); + item.setId(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._id, UUID.class)); + item.setName(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._name, String.class)); + item.setDescription(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._description, String.class)); + item.setCreatedAt(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._createdAt, Instant.class)); + item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._updatedAt, Instant.class)); + item.setIsActive(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._isActive, IsActive.class)); + item.setTenantId(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._tenantId, UUID.class)); + item.setDefinition(QueryBase.convertSafe(tuple, columns, PlanWorkflowEntity._definition, String.class)); + return item; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/query/lookup/PlanWorkflowLookup.java b/backend/core/src/main/java/org/opencdmp/query/lookup/PlanWorkflowLookup.java new file mode 100644 index 000000000..d80a7b93f --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/query/lookup/PlanWorkflowLookup.java @@ -0,0 +1,48 @@ +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.query.PlanWorkflowQuery; + +import java.util.List; +import java.util.UUID; + +public class PlanWorkflowLookup extends Lookup { + + private String like; + + private List ids; + + private List excludedIds; + + private List isActive; + + public String getLike() { return like; } + + public void setLike(String like) { this.like = like; } + + public List getIds() { return ids; } + + public void setIds(List ids) { this.ids = ids; } + + public List getExcludedIds() { return excludedIds; } + + public void setExcludedIds(List excludedIds) { this.excludedIds = excludedIds; } + + public List getIsActive() { return isActive; } + + public void setIsActive(List isActive) { this.isActive = isActive; } + + public PlanWorkflowQuery enrich(QueryFactory queryFactory) { + PlanWorkflowQuery query = queryFactory.query(PlanWorkflowQuery.class); + if (this.like != null) query.like(this.like); + if (this.ids != null) query.ids(this.ids); + if (this.excludedIds != null) query.excludedIds(this.excludedIds); + if (this.isActive != null) query.isActives(this.isActive); + + this.enrichCommon(query); + + return query; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/service/planworkflow/PlanWorkflowService.java b/backend/core/src/main/java/org/opencdmp/service/planworkflow/PlanWorkflowService.java new file mode 100644 index 000000000..649cbdb38 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/service/planworkflow/PlanWorkflowService.java @@ -0,0 +1,19 @@ +package org.opencdmp.service.planworkflow; + +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 jakarta.xml.bind.JAXBException; +import org.opencdmp.model.persist.planworkflow.PlanWorkflowPersist; +import org.opencdmp.model.planworkflow.PlanWorkflow; + +import javax.management.InvalidApplicationException; +import java.util.UUID; + +public interface PlanWorkflowService { + PlanWorkflow persist(PlanWorkflowPersist 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/planworkflow/PlanWorkflowServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/planworkflow/PlanWorkflowServiceImpl.java new file mode 100644 index 000000000..969bf9e3c --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/service/planworkflow/PlanWorkflowServiceImpl.java @@ -0,0 +1,140 @@ +package org.opencdmp.service.planworkflow; + +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 jakarta.xml.bind.JAXBException; +import org.opencdmp.authorization.Permission; +import org.opencdmp.commons.XmlHandlingService; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.commons.types.planworkflow.PlanWorkflowDefinitionEntity; +import org.opencdmp.commons.types.planworkflow.PlanWorkflowDefinitionTransitionEntity; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.data.PlanWorkflowEntity; +import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.opencdmp.model.builder.planworkflow.PlanWorkflowBuilder; +import org.opencdmp.model.deleter.PlanWorkflowDeleter; +import org.opencdmp.model.persist.planworkflow.PlanWorkflowDefinitionPersist; +import org.opencdmp.model.persist.planworkflow.PlanWorkflowDefinitionTransitionPersist; +import org.opencdmp.model.persist.planworkflow.PlanWorkflowPersist; +import org.opencdmp.model.planworkflow.PlanWorkflow; +import org.slf4j.LoggerFactory; +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.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +public class PlanWorkflowServiceImpl implements PlanWorkflowService { + + private final static LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowServiceImpl.class)); + + private final AuthorizationService authorizationService; + private final ConventionService conventionService; + private final XmlHandlingService xmlHandlingService; + private final TenantEntityManager entityManager; + private final BuilderFactory builderFactory; + private final DeleterFactory deleterFactory; + private final MessageSource messageSource; + private final ErrorThesaurusProperties errors; + public PlanWorkflowServiceImpl(AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, BuilderFactory builderFactory, DeleterFactory deleterFactory, MessageSource messageSource, ErrorThesaurusProperties errors) { + this.authorizationService = authorizationService; + this.conventionService = conventionService; + this.xmlHandlingService = xmlHandlingService; + this.entityManager = entityManager; + this.builderFactory = builderFactory; + this.deleterFactory = deleterFactory; + this.messageSource = messageSource; + this.errors = errors; + } + + @Override + public PlanWorkflow persist(PlanWorkflowPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("persisting data plan workflow").And("model", model).And("fields", fields)); + + this.authorizationService.authorizeForce(Permission.EditPlanWorkflow); + + Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); + + PlanWorkflowEntity data; + if (isUpdate) { + data = this.entityManager.find(PlanWorkflowEntity.class, model.getId()); + if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), PlanWorkflow.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 PlanWorkflowEntity(); + data.setId(UUID.randomUUID()); + data.setIsActive(IsActive.Active); + data.setCreatedAt(Instant.now()); + } + + data.setName(model.getName()); + data.setDescription(model.getDescription()); + data.setDefinition(this.xmlHandlingService.toXml(this.buildPlanWorkflowDefinitionEntity(model.getDefinition()))); + data.setUpdatedAt(Instant.now()); + + if (isUpdate) + this.entityManager.merge(data); + else + this.entityManager.persist(data); + + this.entityManager.flush(); + + return this.builderFactory.builder(PlanWorkflowBuilder.class).build(BaseFieldSet.build(fields, PlanWorkflow._id), data); + } + + @Override + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { + logger.debug("deleting plan workflow: {}", id); + + this.authorizationService.authorizeForce(Permission.DeletePlanWorkflow); + + this.deleterFactory.deleter(PlanWorkflowDeleter.class).deleteAndSaveByIds(List.of(id)); + } + + private PlanWorkflowDefinitionEntity buildPlanWorkflowDefinitionEntity(PlanWorkflowDefinitionPersist persist) { + PlanWorkflowDefinitionEntity data = new PlanWorkflowDefinitionEntity(); + if (persist == null) + return data; + + data.setStartingStatusId(persist.getStartingStatusId()); + + if (persist.getStatusTransitions() != null) { + List transitionsData = this.buildPlanWorkflowDefinitionEntity(persist.getStatusTransitions()); + data.setStatusTransitions(transitionsData); + } + + return data; + } + + private List buildPlanWorkflowDefinitionEntity(List persistData) { + + List data = new ArrayList<>(); + if (persistData == null || persistData.isEmpty()) + return data; + + for (PlanWorkflowDefinitionTransitionPersist persist : persistData) { + PlanWorkflowDefinitionTransitionEntity d = new PlanWorkflowDefinitionTransitionEntity(); + d.setFromStatusId(persist.getFromStatusId()); + d.setToStatusId(persist.getToStatusId()); + + data.add(d); + } + + return data; + } +} diff --git a/backend/web/src/main/java/org/opencdmp/controllers/PlanWorkflowController.java b/backend/web/src/main/java/org/opencdmp/controllers/PlanWorkflowController.java new file mode 100644 index 000000000..c1b3aa63d --- /dev/null +++ b/backend/web/src/main/java/org/opencdmp/controllers/PlanWorkflowController.java @@ -0,0 +1,186 @@ +package org.opencdmp.controllers; + +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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.xml.bind.JAXBException; +import org.opencdmp.audit.AuditableAction; +import org.opencdmp.authorization.AuthorizationFlags; +import org.opencdmp.controllers.swagger.SwaggerHelpers; +import org.opencdmp.controllers.swagger.annotation.OperationWithTenantHeader; +import org.opencdmp.controllers.swagger.annotation.Swagger400; +import org.opencdmp.controllers.swagger.annotation.Swagger404; +import org.opencdmp.controllers.swagger.annotation.SwaggerCommonErrorResponses; +import org.opencdmp.data.PlanWorkflowEntity; +import org.opencdmp.model.builder.planworkflow.PlanWorkflowBuilder; +import org.opencdmp.model.censorship.planworkflow.PlanWorkflowCensor; +import org.opencdmp.model.persist.planworkflow.PlanWorkflowPersist; +import org.opencdmp.model.planworkflow.PlanWorkflow; +import org.opencdmp.model.result.QueryResult; +import org.opencdmp.query.PlanWorkflowQuery; +import org.opencdmp.query.lookup.PlanWorkflowLookup; +import org.opencdmp.service.planworkflow.PlanWorkflowService; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.management.InvalidApplicationException; +import java.util.*; + +@RestController +@RequestMapping(path = "api/plan-workflow") +@io.swagger.v3.oas.annotations.tags.Tag(name = "PlanWorkflows", description = "Manage tags") +@SwaggerCommonErrorResponses +public class PlanWorkflowController { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(PlanWorkflowController.class)); + + private final BuilderFactory builderFactory; + + private final AuditService auditService; + + private final PlanWorkflowService planWorkflowService; + + private final CensorFactory censorFactory; + + private final QueryFactory queryFactory; + + private final MessageSource messageSource; + + public PlanWorkflowController( + BuilderFactory builderFactory, + AuditService auditService, + PlanWorkflowService planWorkflowService, + CensorFactory censorFactory, + QueryFactory queryFactory, + MessageSource messageSource) { + this.builderFactory = builderFactory; + this.auditService = auditService; + this.planWorkflowService = planWorkflowService; + this.censorFactory = censorFactory; + this.queryFactory = queryFactory; + this.messageSource = messageSource; + } + + @PostMapping("query") + @OperationWithTenantHeader(summary = "Query all planWorkflows", description = SwaggerHelpers.Tag.endpoint_query, requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(description = SwaggerHelpers.Tag.endpoint_query_request_body, content = @Content( + examples = { + @ExampleObject( + name = SwaggerHelpers.Commons.pagination_example, + description = SwaggerHelpers.Commons.pagination_example_description, + value = SwaggerHelpers.Tag.endpoint_query_request_body_example + ) + } + )), responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content( + array = @ArraySchema( + schema = @Schema( + implementation = PlanWorkflow.class + ) + ), + examples = @ExampleObject( + name = SwaggerHelpers.Commons.pagination_response_example, + description = SwaggerHelpers.Commons.pagination_response_example_description, + value = SwaggerHelpers.Tag.endpoint_query_response_example + )))) + public QueryResult Query(@RequestBody PlanWorkflowLookup lookup) throws MyApplicationException, MyForbiddenException { + logger.debug("querying {}", PlanWorkflow.class.getSimpleName()); + + this.censorFactory.censor(PlanWorkflowCensor.class).censor(lookup.getProject(), null); + + PlanWorkflowQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.AllExceptPublic); + + List data = query.collectAs(lookup.getProject()); + List models = this.builderFactory.builder(PlanWorkflowBuilder.class).build(lookup.getProject(), data); + long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size(); + + this.auditService.track(AuditableAction.PlanWorkflow_Query); + + return new QueryResult<>(models, count); + } + + @GetMapping("{id}") + @OperationWithTenantHeader(summary = "Fetch a specific PlanWorkflow by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content( + schema = @Schema( + implementation = PlanWorkflow.class + )) + )) + @Swagger404 + public PlanWorkflow Get( + @Parameter(name = "id", description = "The id of a PlanWorkflow to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id, + @Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet, + Locale locale + ) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + logger.debug(new MapLogEntry("retrieving" + PlanWorkflow.class.getSimpleName()).And("id", id).And("fields", fieldSet)); + + this.censorFactory.censor(PlanWorkflowCensor.class).censor(fieldSet, null); + + PlanWorkflowQuery query = this.queryFactory.query(PlanWorkflowQuery.class).disableTracking().authorize(AuthorizationFlags.AllExceptPublic).ids(id); + PlanWorkflow model = this.builderFactory.builder(PlanWorkflowBuilder.class).build(fieldSet, query.firstAs(fieldSet)); + if (model == null) + throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, PlanWorkflow.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + this.auditService.track(AuditableAction.PlanWorkflow_Lookup, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + + return model; + } + + @PostMapping("persist") + @OperationWithTenantHeader(summary = "Create a new or update an existing PlanWorkflow", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content( + schema = @Schema( + implementation = PlanWorkflow.class + )) + )) + @Swagger400 + @Swagger404 + @Transactional + @ValidationFilterAnnotation(validator = PlanWorkflowPersist.PlanWorkflowPersistValidator.ValidatorName, argumentName = "model") + public PlanWorkflow Persist( + @RequestBody PlanWorkflowPersist model, + @Parameter(name = "fieldSet",description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet + ) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, JAXBException { + logger.debug(new MapLogEntry("persisting"+PlanWorkflow.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet)); + PlanWorkflow persisted = this.planWorkflowService.persist(model, fieldSet); + + this.auditService.track(AuditableAction.PlanWorkflow_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + + return persisted; + } + + @DeleteMapping("{id}") + @OperationWithTenantHeader(summary = "Delete a PlanWorkflow by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200")) + @Swagger404 + @Transactional + public void Delete( + @Parameter(name = "id", description = "The id of PlanWorkflow to delete", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id + ) throws MyForbiddenException, InvalidApplicationException { + logger.debug(new MapLogEntry("deleting"+ PlanWorkflow.class.getSimpleName()).And("id", id)); + + this.planWorkflowService.deleteAndSave(id); + + this.auditService.track(AuditableAction.PlanWorkflow_Delete, "id", id); + } +} diff --git a/backend/web/src/main/resources/config/permissions.yml b/backend/web/src/main/resources/config/permissions.yml index 8bb06feec..51bda04eb 100644 --- a/backend/web/src/main/resources/config/permissions.yml +++ b/backend/web/src/main/resources/config/permissions.yml @@ -143,6 +143,33 @@ permissions: allowAnonymous: false allowAuthenticated: false + #DescriptionWorkflow + BrowseDescriptionWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + EditDescriptionWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + DeleteDescriptionWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + claims: [ ] + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + #PlanStatus BrowsePlanStatus: roles: @@ -170,6 +197,34 @@ permissions: allowAnonymous: false allowAuthenticated: false + + #PlanWorkflow + BrowsePlanWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + EditPlanWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + DeletePlanWorkflow: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + claims: [ ] + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + # NotificationTemplate BrowseNotificationTemplate: roles: [ ] @@ -1358,6 +1413,22 @@ permissions: clients: [ ] allowAnonymous: false allowAuthenticated: false + ViewPlanWorkflowPage: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false + ViewDescriptionWorkflowPage: + roles: + - Admin + - TenantAdmin + - TenantConfigManager + clients: [ ] + allowAnonymous: false + allowAuthenticated: false ViewDescriptionStatusPage: roles: - Admin diff --git a/dmp-db-scema/updates/00.01.074_Add_PlanWorkflow_table.sql b/dmp-db-scema/updates/00.01.074_Add_PlanWorkflow_table.sql new file mode 100644 index 000000000..34d344bd6 --- /dev/null +++ b/dmp-db-scema/updates/00.01.074_Add_PlanWorkflow_table.sql @@ -0,0 +1,27 @@ +DO $$DECLARE + this_version CONSTANT varchar := '00.01.074'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + CREATE TABLE public."PlanWorkflow" + ( + id uuid NOT NULL, + name character varying(250) COLLATE pg_catalog."default" NOT NULL, + description text COLLATE pg_catalog."default", + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + is_active smallint NOT NULL, + definition xml, + tenant uuid, + CONSTRAINT "PlanWorkflow_pkey" PRIMARY KEY (id), + CONSTRAINT "PlanWorkflow_tenant_fkey" FOREIGN KEY (tenant) + REFERENCES public."Tenant" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID + ); + + INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.074', '2024-08-23 12:00:00.000000+02', now(), 'Add plan workflow table.'); + +END$$; \ No newline at end of file