001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.workflow.server.logic;
018
019import com.echothree.control.user.workflow.common.spec.WorkflowStepUniversalSpec;
020import com.echothree.model.control.core.common.ComponentVendors;
021import com.echothree.model.control.core.common.EntityTypes;
022import com.echothree.model.control.core.common.exception.InvalidParameterCountException;
023import com.echothree.model.control.core.server.control.CoreControl;
024import com.echothree.model.control.core.server.logic.EntityInstanceLogic;
025import com.echothree.model.control.workflow.common.exception.MissingRequiredWorkflowNameException;
026import com.echothree.model.control.workflow.common.exception.UnknownDefaultWorkflowStepException;
027import com.echothree.model.control.workflow.common.exception.UnknownWorkflowStepNameException;
028import com.echothree.model.control.workflow.server.control.WorkflowControl;
029import com.echothree.model.data.core.server.entity.EntityInstance;
030import com.echothree.model.data.workflow.server.entity.Workflow;
031import com.echothree.model.data.workflow.server.entity.WorkflowEntityStatus;
032import com.echothree.model.data.workflow.server.entity.WorkflowStep;
033import com.echothree.model.data.workflow.server.entity.WorkflowStepDetail;
034import com.echothree.util.common.exception.BaseException;
035import com.echothree.util.common.message.ExecutionErrors;
036import com.echothree.util.common.persistence.BasePK;
037import com.echothree.util.server.control.BaseLogic;
038import com.echothree.util.server.message.ExecutionErrorAccumulator;
039import com.echothree.util.server.persistence.BaseEntity;
040import com.echothree.util.server.persistence.EntityPermission;
041import com.echothree.util.server.persistence.Session;
042import com.echothree.util.server.validation.ParameterUtils;
043import java.util.Arrays;
044import java.util.HashSet;
045import java.util.List;
046import java.util.Set;
047
048public class WorkflowStepLogic
049        extends BaseLogic {
050
051    private WorkflowStepLogic() {
052        super();
053    }
054    
055    private static class WorkflowStepLogicHolder {
056        static WorkflowStepLogic instance = new WorkflowStepLogic();
057    }
058    
059    public static WorkflowStepLogic getInstance() {
060        return WorkflowStepLogicHolder.instance;
061    }
062
063    public WorkflowStep getWorkflowStepByName(final Class<? extends BaseException> unknownWorkflowException, final ExecutionErrors unknownWorkflowExecutionError,
064            final Class<? extends BaseException>  unknownWorkflowStepException, final ExecutionErrors unknownWorkflowStepExecutionError,
065            final ExecutionErrorAccumulator eea, final String workflowName, final String workflowStepName) {
066        var workflow = WorkflowLogic.getInstance().getWorkflowByName(unknownWorkflowException, unknownWorkflowExecutionError,
067                eea, workflowName, EntityPermission.READ_ONLY);
068        WorkflowStep workflowStep = null;
069
070        if(eea == null || !eea.hasExecutionErrors()) {
071            workflowStep = getWorkflowStepByName(unknownWorkflowStepException, unknownWorkflowStepExecutionError, eea,
072                    workflow, workflowStepName);
073        }
074
075        return workflowStep;
076    }
077
078    public WorkflowStep getWorkflowStepByName(final Class<? extends BaseException> unknownException, final ExecutionErrors unknownExecutionError,
079            final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName, EntityPermission entityPermission) {
080        var workflowControl = Session.getModelController(WorkflowControl.class);
081        var workflowStep = workflowControl.getWorkflowStepByName(workflow, workflowStepName, entityPermission);
082
083        if(workflowStep == null) {
084            handleExecutionError(unknownException, eea, unknownExecutionError.name(), workflow.getLastDetail().getWorkflowName(),
085                    workflowStepName);
086        }
087
088        return workflowStep;
089    }
090
091    public WorkflowStep getWorkflowStepByName(final Class<? extends BaseException> unknownException, final ExecutionErrors unknownExecutionError,
092            final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName) {
093        return getWorkflowStepByName(unknownException, unknownExecutionError, eea, workflow, workflowStepName, EntityPermission.READ_ONLY);
094    }
095
096    public WorkflowStep getWorkflowStepByNameForUpdate(final Class<? extends BaseException> unknownException, final ExecutionErrors unknownExecutionError,
097            final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName) {
098        return getWorkflowStepByName(unknownException, unknownExecutionError, eea, workflow, workflowStepName, EntityPermission.READ_WRITE);
099    }
100
101    public WorkflowStep getWorkflowStepByName(final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName,
102            final EntityPermission entityPermission) {
103        return getWorkflowStepByName(UnknownWorkflowStepNameException.class, ExecutionErrors.UnknownWorkflowStepName,
104                eea, workflow, workflowStepName, entityPermission);
105    }
106
107    public WorkflowStep getWorkflowStepByName(final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName) {
108        return getWorkflowStepByName(UnknownWorkflowStepNameException.class, ExecutionErrors.UnknownWorkflowStepName,
109                eea, workflow, workflowStepName);
110    }
111
112    public WorkflowStep getWorkflowStepByNameForUpdate(final ExecutionErrorAccumulator eea, final Workflow workflow, final String workflowStepName) {
113        return getWorkflowStepByNameForUpdate(UnknownWorkflowStepNameException.class, ExecutionErrors.UnknownWorkflowStepName,
114                eea, workflow, workflowStepName);
115    }
116
117    public WorkflowStep getWorkflowStepByName(final ExecutionErrorAccumulator eea, final String workflowName, final String workflowStepName,
118            final EntityPermission entityPermission) {
119        var workflow = WorkflowLogic.getInstance().getWorkflowByName(eea, workflowName);
120        WorkflowStep workflowStep = null;
121
122        if(eea == null || !eea.hasExecutionErrors()) {
123            workflowStep = getWorkflowStepByName(UnknownWorkflowStepNameException.class, ExecutionErrors.UnknownWorkflowStepName,
124                    eea, workflow, workflowStepName, entityPermission);
125        }
126
127        return workflowStep;
128    }
129
130    public WorkflowStep getWorkflowStepByName(final ExecutionErrorAccumulator eea, final String workflowName, final String workflowStepName) {
131        return getWorkflowStepByName(eea, workflowName, workflowStepName, EntityPermission.READ_ONLY);
132    }
133
134    public WorkflowStep getWorkflowStepByNameForUpdate(final ExecutionErrorAccumulator eea, final String workflowName, final String workflowStepName) {
135        return getWorkflowStepByName(eea, workflowName, workflowStepName, EntityPermission.READ_WRITE);
136    }
137
138    public WorkflowStep getWorkflowStepByUniversalSpec(final ExecutionErrorAccumulator eea, final WorkflowStepUniversalSpec universalSpec,
139            final boolean allowDefault, final EntityPermission entityPermission) {
140        var workflowControl = Session.getModelController(WorkflowControl.class);
141        var workflowName = universalSpec.getWorkflowName();
142        var workflowStepName = universalSpec.getWorkflowStepName();
143        var nameParameterCount= ParameterUtils.getInstance().countNonNullParameters(workflowName, workflowStepName);
144        var possibleEntitySpecs= EntityInstanceLogic.getInstance().countPossibleEntitySpecs(universalSpec);
145        WorkflowStep workflowStep = null;
146
147        if(nameParameterCount < 3 && possibleEntitySpecs == 0) {
148            Workflow workflow = null;
149
150            if(workflowName != null) {
151                workflow = WorkflowLogic.getInstance().getWorkflowByName(eea, workflowName);
152            } else {
153                handleExecutionError(MissingRequiredWorkflowNameException.class, eea, ExecutionErrors.MissingRequiredWorkflowName.name());
154            }
155
156            if(!eea.hasExecutionErrors()) {
157                if(workflowStepName == null) {
158                    if(allowDefault) {
159                        workflowStep = workflowControl.getDefaultWorkflowStep(workflow, entityPermission);
160
161                        if(workflowStep == null) {
162                            handleExecutionError(UnknownDefaultWorkflowStepException.class, eea, ExecutionErrors.UnknownDefaultWorkflowStep.name());
163                        }
164                    } else {
165                        handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
166                    }
167                } else {
168                    workflowStep = getWorkflowStepByName(eea, workflow, workflowStepName, entityPermission);
169                }
170            }
171        } else if(nameParameterCount == 0 && possibleEntitySpecs == 1) {
172            var entityInstance = EntityInstanceLogic.getInstance().getEntityInstance(eea, universalSpec,
173                    ComponentVendors.ECHO_THREE.name(), EntityTypes.WorkflowStep.name());
174
175            if(!eea.hasExecutionErrors()) {
176                workflowStep = workflowControl.getWorkflowStepByEntityInstance(entityInstance, entityPermission);
177            }
178        } else {
179            handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
180        }
181
182        return workflowStep;
183    }
184
185    public WorkflowStep getWorkflowStepByUniversalSpec(final ExecutionErrorAccumulator eea, final WorkflowStepUniversalSpec universalSpec,
186            boolean allowDefault) {
187        return getWorkflowStepByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_ONLY);
188    }
189
190    public WorkflowStep getWorkflowStepByUniversalSpecForUpdate(final ExecutionErrorAccumulator eea, final WorkflowStepUniversalSpec universalSpec,
191            boolean allowDefault) {
192        return getWorkflowStepByUniversalSpec(eea, universalSpec, allowDefault, EntityPermission.READ_WRITE);
193    }
194
195    public Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final BaseEntity baseEntity,
196            String... workflowStepNames) {
197        return isEntityInWorkflowSteps(eea, workflowName, baseEntity, EntityPermission.READ_ONLY, workflowStepNames);
198    }
199    
200    public Set<WorkflowEntityStatus> isEntityInWorkflowStepsForUpdate(final ExecutionErrorAccumulator eea, final String workflowName, final BaseEntity baseEntity,
201            String... workflowStepNames) {
202        return isEntityInWorkflowSteps(eea, workflowName, baseEntity, EntityPermission.READ_WRITE, workflowStepNames);
203    }
204    
205    public Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final BaseEntity baseEntity,
206            EntityPermission entityPermission, String... workflowStepNames) {
207        return isEntityInWorkflowSteps(eea, workflowName, baseEntity.getPrimaryKey(), entityPermission, workflowStepNames);
208    }
209
210    public Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final BasePK pk,
211            String... workflowStepNames) {
212        return isEntityInWorkflowSteps(eea, workflowName, pk, EntityPermission.READ_ONLY, workflowStepNames);
213    }
214    
215    public Set<WorkflowEntityStatus> isEntityInWorkflowStepsForUpdate(final ExecutionErrorAccumulator eea, final String workflowName, final BasePK pk,
216            String... workflowStepNames) {
217        return isEntityInWorkflowSteps(eea, workflowName, pk, EntityPermission.READ_WRITE, workflowStepNames);
218    }
219    
220    public Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final BasePK pk,
221            EntityPermission entityPermission, String... workflowStepNames) {
222        var coreControl = Session.getModelController(CoreControl.class);
223        EntityInstance entityInstance = coreControl.getEntityInstanceByBasePK(pk);
224        
225        return isEntityInWorkflowSteps(eea, workflowName, entityInstance, entityPermission, workflowStepNames);
226    }
227
228    public Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final EntityInstance entityInstance,
229            final String... workflowStepNames) {
230        return isEntityInWorkflowSteps(eea, workflowName, entityInstance, EntityPermission.READ_ONLY, workflowStepNames);
231    }
232    
233    public Set<WorkflowEntityStatus> isEntityInWorkflowStepsForUpdate(final ExecutionErrorAccumulator eea, final String workflowName, final EntityInstance entityInstance,
234            final String... workflowStepNames) {
235        return isEntityInWorkflowSteps(eea, workflowName, entityInstance, EntityPermission.READ_WRITE, workflowStepNames);
236    }
237    
238    private Set<WorkflowEntityStatus> isEntityInWorkflowSteps(final ExecutionErrorAccumulator eea, final String workflowName, final EntityInstance entityInstance,
239            final EntityPermission entityPermission, final String... workflowStepNames) {
240        var workflow = WorkflowLogic.getInstance().getWorkflowByName(eea, workflowName);
241        Set<WorkflowEntityStatus> result = new HashSet<>();
242        
243        if(!hasExecutionErrors(eea)) {
244            var workflowControl = Session.getModelController(WorkflowControl.class);
245            List<WorkflowEntityStatus> workflowEntityStatuses = workflowControl.getWorkflowEntityStatusesByEntityInstance(workflow, entityInstance, entityPermission);
246            Set<String> possibleWorkflowStepNames = new HashSet<>(workflowStepNames.length);
247            
248            possibleWorkflowStepNames.addAll(Arrays.asList(workflowStepNames));
249            
250            workflowEntityStatuses.forEach((workflowEntityStatus) -> {
251                WorkflowStepDetail workflowStepDetail = workflowEntityStatus.getWorkflowStep().getLastDetail();
252                if (workflowStepDetail.getWorkflow().equals(workflow)) {
253                    String workflowStepName = workflowStepDetail.getWorkflowStepName();
254                    if (possibleWorkflowStepNames.contains(workflowStepName)) {
255                        result.add(workflowEntityStatus);
256                    }
257                }
258            });
259
260        }
261
262        return result;
263    }
264    
265    public boolean isWorkflowStepInSet(Set<WorkflowEntityStatus> workflowEntityStatuses, String workflowStepName) {
266        boolean result = false;
267        
268        for(var workflowEntityStatus : workflowEntityStatuses) {
269            if(result |= workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName().equals(workflowStepName)) {
270                break;
271            }
272        }
273        
274        return result;
275    }
276
277}