From 149f889a061d717329110fe62e001930cea17168 Mon Sep 17 00:00:00 2001 From: Moti Asayag Date: Sat, 1 Apr 2023 12:30:32 +0300 Subject: [PATCH] Refactor workflow aspect WorkFlowExecutionAspect was refactored into several classess to allow separation between pre-execution and post-execution. Signed-off-by: Moti Asayag --- .../aspect/CheckerWorkFlowPostExecution.java | 88 ++++++++ ...ContinuedWorkFlowExecutionInterceptor.java | 45 ++++ ...FirstTimeWorkFlowExecutionInterceptor.java | 34 +++ .../MasterWorkFlowExecutionInterceptor.java | 39 ++++ .../aspect/WorkFlowExecutionAspect.java | 209 ++---------------- .../aspect/WorkFlowExecutionFactory.java | 66 ++++++ .../aspect/WorkFlowExecutionInterceptor.java | 99 +++++++++ .../execution/aspect/WorkFlowInterceptor.java | 13 ++ .../aspect/WorkFlowPostExecution.java | 84 +++++++ .../aspect/WorkFlowPostExecutionHandler.java | 9 + .../aspect/WorkFlowExecutionAspectTest.java | 14 +- .../aspect/WorkFlowExecutionFactoryTest.java | 141 ++++++++++++ .../WorkFlowExecutionInterceptorTest.java | 142 ++++++++++++ 13 files changed, 787 insertions(+), 196 deletions(-) create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/CheckerWorkFlowPostExecution.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/ContinuedWorkFlowExecutionInterceptor.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/FirstTimeWorkFlowExecutionInterceptor.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/MasterWorkFlowExecutionInterceptor.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactory.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptor.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowInterceptor.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecution.java create mode 100644 workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecutionHandler.java create mode 100644 workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactoryTest.java create mode 100644 workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptorTest.java diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/CheckerWorkFlowPostExecution.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/CheckerWorkFlowPostExecution.java new file mode 100644 index 000000000..0e2704065 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/CheckerWorkFlowPostExecution.java @@ -0,0 +1,88 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowCheckerMappingDefinition; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import com.redhat.parodos.workflows.workflow.WorkFlow; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class CheckerWorkFlowPostExecution implements WorkFlowPostExecutionHandler { + + private final WorkFlowDefinition workFlowDefinition; + + private final WorkContext workContext; + + private final WorkFlowExecution workFlowExecution; + + private final WorkFlowExecution masterWorkFlowExecution; + + private final WorkFlowServiceImpl workFlowService; + + private final WorkFlowSchedulerServiceImpl workFlowSchedulerService; + + private final WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + + private final WorkFlow workFlow; + + private final WorkStatus workStatus; + + public CheckerWorkFlowPostExecution(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl, WorkFlowExecution workFlowExecution, + WorkFlowExecution masterWorkFlowExecution, WorkFlow workFlow, WorkStatus workStatus) { + this.workFlowDefinition = workFlowDefinition; + this.workContext = workContext; + this.workFlowService = workFlowService; + this.workFlowExecution = workFlowExecution; + this.masterWorkFlowExecution = masterWorkFlowExecution; + this.workFlowSchedulerService = workFlowSchedulerService; + this.workFlowContinuationServiceImpl = workFlowContinuationServiceImpl; + this.workFlow = workFlow; + this.workStatus = workStatus; + } + + @Override + public WorkReport handlePostWorkFlowExecution() { + workFlowService.updateWorkFlow(workFlowExecution); + /* + * if this workflow is a checker, schedule workflow checker for dynamic run on + * cron expression or stop if done + */ + startOrStopWorkFlowCheckerOnSchedule(workFlow, workFlowDefinition.getCheckerWorkFlowDefinition(), workStatus, + workContext, workFlowExecution.getProjectId().toString(), masterWorkFlowExecution); + return null; + } + + private void startOrStopWorkFlowCheckerOnSchedule(WorkFlow workFlow, + WorkFlowCheckerMappingDefinition workFlowCheckerMappingDefinition, WorkStatus workStatus, + WorkContext workContext, String projectId, WorkFlowExecution masterWorkFlowExecution) { + if (workStatus != WorkStatus.COMPLETED) { + log.info("Schedule workflow checker: {} to run per cron expression: {}", workFlow.getName(), + workFlowCheckerMappingDefinition.getCronExpression()); + workFlowSchedulerService.schedule(workFlow, workContext, + workFlowCheckerMappingDefinition.getCronExpression()); + return; + } + + log.info("Stop workflow checker: {} schedule", workFlow.getName()); + workFlowSchedulerService.stop(workFlow); + + String masterWorkFlowName = WorkContextDelegate.read(workContext, + WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, WorkContextDelegate.Resource.NAME).toString(); + /* + * if this workflow is checker, and it's successful, call continuation service to + * restart master workflow execution with same execution ID + */ + workFlowContinuationServiceImpl.continueWorkFlow(projectId, masterWorkFlowName, workContext, + masterWorkFlowExecution.getId()); + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/ContinuedWorkFlowExecutionInterceptor.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/ContinuedWorkFlowExecutionInterceptor.java new file mode 100644 index 000000000..5797786be --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/ContinuedWorkFlowExecutionInterceptor.java @@ -0,0 +1,45 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.Optional; + +import static com.redhat.parodos.workflow.execution.aspect.WorkFlowExecutionFactory.getMasterWorkFlowExecutionId; + +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.exceptions.WorkflowExecutionNotFoundException; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; + +public class ContinuedWorkFlowExecutionInterceptor extends WorkFlowExecutionInterceptor { + + private WorkFlowExecution masterWorkFlowExecution; + + public ContinuedWorkFlowExecutionInterceptor(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { + super(workFlowDefinition, workContext, workFlowService, workFlowRepository, workFlowSchedulerService, + workFlowContinuationServiceImpl); + } + + @Override + protected WorkFlowExecution doPreWorkFlowExecution() { + this.masterWorkFlowExecution = workFlowRepository.findById(getMasterWorkFlowExecutionId(workContext)) + .orElseThrow(() -> new WorkflowExecutionNotFoundException( + "masterWorkFlow not found for sub-workflow: " + workFlowDefinition.getName())); + + // get the workflow execution if this is triggered by continuation service + return Optional + .ofNullable(workFlowRepository.findFirstByWorkFlowDefinitionIdAndMasterWorkFlowExecution( + workFlowDefinition.getId(), masterWorkFlowExecution)) + .orElseGet(() -> this.saveWorkFlow(masterWorkFlowExecution)); + } + + protected WorkFlowExecution getMasterWorkFlowExecution() { + return masterWorkFlowExecution; + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/FirstTimeWorkFlowExecutionInterceptor.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/FirstTimeWorkFlowExecutionInterceptor.java new file mode 100644 index 000000000..570459148 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/FirstTimeWorkFlowExecutionInterceptor.java @@ -0,0 +1,34 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; + +public class FirstTimeWorkFlowExecutionInterceptor extends WorkFlowExecutionInterceptor { + + public FirstTimeWorkFlowExecutionInterceptor(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { + super(workFlowDefinition, workContext, workFlowService, workFlowRepository, workFlowSchedulerService, + workFlowContinuationServiceImpl); + } + + @Override + protected WorkFlowExecution doPreWorkFlowExecution() { + /* + * if this is the first time execution for master workflow, persist it and write + * its execution id to workContext + */ + WorkFlowExecution workFlowExecution = saveWorkFlow(null); + WorkContextDelegate.write(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + WorkContextDelegate.Resource.ID, workFlowExecution.getId()); + return workFlowExecution; + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/MasterWorkFlowExecutionInterceptor.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/MasterWorkFlowExecutionInterceptor.java new file mode 100644 index 000000000..4705df04a --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/MasterWorkFlowExecutionInterceptor.java @@ -0,0 +1,39 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import static com.redhat.parodos.workflow.execution.aspect.WorkFlowExecutionFactory.getMasterWorkFlowExecutionId; + +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.exceptions.WorkflowExecutionNotFoundException; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; + +public class MasterWorkFlowExecutionInterceptor extends WorkFlowExecutionInterceptor { + + private WorkFlowExecution masterWorkFlowExecution; + + public MasterWorkFlowExecutionInterceptor(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { + super(workFlowDefinition, workContext, workFlowService, workFlowRepository, workFlowSchedulerService, + workFlowContinuationServiceImpl); + } + + @Override + protected WorkFlowExecution doPreWorkFlowExecution() { + masterWorkFlowExecution = workFlowRepository.findById(getMasterWorkFlowExecutionId(workContext)) + .orElseThrow(() -> new WorkflowExecutionNotFoundException( + "masterWorkFlow not found for sub-workflow: " + workFlowDefinition.getName())); + return masterWorkFlowExecution; + } + + @Override + protected WorkFlowExecution getMasterWorkFlowExecution() { + return masterWorkFlowExecution; + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspect.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspect.java index 35d4d2426..bd2ce15f6 100644 --- a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspect.java +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspect.java @@ -15,21 +15,12 @@ */ package com.redhat.parodos.workflow.execution.aspect; -import com.redhat.parodos.workflow.context.WorkContextDelegate; -import com.redhat.parodos.workflow.definition.entity.WorkFlowCheckerMappingDefinition; import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; -import com.redhat.parodos.workflow.definition.entity.WorkFlowTaskDefinition; import com.redhat.parodos.workflow.definition.repository.WorkFlowDefinitionRepository; import com.redhat.parodos.workflow.enums.WorkFlowStatus; import com.redhat.parodos.workflow.enums.WorkFlowType; -import com.redhat.parodos.workflow.exceptions.WorkflowExecutionNotFoundException; -import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; -import com.redhat.parodos.workflow.execution.entity.WorkFlowExecutionContext; -import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; -import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; -import com.redhat.parodos.workflow.util.WorkFlowDTOUtil; import com.redhat.parodos.workflows.work.DefaultWorkReport; import com.redhat.parodos.workflows.work.WorkContext; import com.redhat.parodos.workflows.work.WorkReport; @@ -42,13 +33,6 @@ import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; -import java.util.Date; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - /** * Aspect pointcut to perform state management for a workflow execution * @@ -62,25 +46,18 @@ @Slf4j public class WorkFlowExecutionAspect { - private final WorkFlowRepository workFlowRepository; - - private final WorkFlowServiceImpl workFlowService; - private final WorkFlowSchedulerServiceImpl workFlowSchedulerService; private final WorkFlowDefinitionRepository workFlowDefinitionRepository; - private final WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + private final WorkFlowExecutionFactory workFlowExecutionFactory; - public WorkFlowExecutionAspect(WorkFlowServiceImpl workFlowService, - WorkFlowSchedulerServiceImpl workFlowSchedulerService, - WorkFlowDefinitionRepository workFlowDefinitionRepository, WorkFlowRepository workFlowRepository, - WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { - this.workFlowService = workFlowService; + public WorkFlowExecutionAspect(WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowDefinitionRepository workFlowDefinitionRepository, + WorkFlowExecutionFactory workFlowExecutionFactory) { this.workFlowSchedulerService = workFlowSchedulerService; this.workFlowDefinitionRepository = workFlowDefinitionRepository; - this.workFlowRepository = workFlowRepository; - this.workFlowContinuationServiceImpl = workFlowContinuationServiceImpl; + this.workFlowExecutionFactory = workFlowExecutionFactory; } /** @@ -105,53 +82,23 @@ public WorkReport executeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, W /* get workflow definition entity */ WorkFlowDefinition workFlowDefinition = this.workFlowDefinitionRepository.findFirstByName(workflowName); - - String masterWorkflowName = WorkContextDelegate.read(workContext, - WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, WorkContextDelegate.Resource.NAME).toString(); - boolean isMaster = workflowName.equals(masterWorkflowName); - - WorkFlowExecution workFlowExecution; - WorkFlowExecution masterWorkFlowExecution = null; - String arguments = WorkFlowDTOUtil.writeObjectValueAsString( - WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, workflowName, - WorkContextDelegate.Resource.ARGUMENTS)); - UUID projectId = UUID.fromString(WorkContextDelegate - .read(workContext, WorkContextDelegate.ProcessType.PROJECT, WorkContextDelegate.Resource.ID) - .toString()); - - // get master WorkFlowExecution, this is the first time execution for master - // workflow if return null - UUID masterWorkFlowExecutionId = Optional.ofNullable(WorkContextDelegate.read(workContext, - WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, WorkContextDelegate.Resource.ID)) - .map(id -> UUID.fromString(id.toString())).orElse(null); - - if (masterWorkFlowExecutionId == null) { - workFlowExecution = handleFirstTimeMainWorkFlowExecution(projectId, workFlowDefinition, arguments, - workContext); - } - else { - masterWorkFlowExecution = workFlowRepository.findById(masterWorkFlowExecutionId) - .orElseThrow(() -> new WorkflowExecutionNotFoundException( - "masterWorkFlow not found for sub-workflow: " + workflowName)); - - // get the workflow execution if this is triggered by continuation service - WorkFlowExecution finalMasterWorkFlowExecution = masterWorkFlowExecution; - workFlowExecution = isMaster ? masterWorkFlowExecution - : Optional - .ofNullable(workFlowRepository.findFirstByWorkFlowDefinitionIdAndMasterWorkFlowExecution( - workFlowDefinition.getId(), masterWorkFlowExecution)) - .orElseGet(() -> this.workFlowService.saveWorkFlow(projectId, workFlowDefinition.getId(), - WorkFlowStatus.IN_PROGRESS, finalMasterWorkFlowExecution, arguments)); - - if (workFlowExecution.getStatus().equals(WorkFlowStatus.COMPLETED)) { - // skip the workflow if it is already successful - if (workFlowDefinition.getType().equals(WorkFlowType.CHECKER)) { - workFlowSchedulerService.stop((WorkFlow) proceedingJoinPoint.getTarget()); - } - return new DefaultWorkReport(WorkStatus.COMPLETED, workContext); + WorkFlowExecutionInterceptor executionHandler = workFlowExecutionFactory + .createExecutionHandler(workFlowDefinition, workContext); + WorkFlowExecution workFlowExecution = executionHandler.handlePreWorkFlowExecution(); + + // @Richard, I think this is the right place to check if the workflow is already + // completed + // it used to be checked only for Master Workflow or for Continued workflows, but + // can't see any + // harm in having it here for all. + // FIXME: Resolve question and remove this comment + if (workFlowExecution.getStatus().equals(WorkFlowStatus.COMPLETED)) { + // skip the workflow if it is already successful + if (workFlowDefinition.getType().equals(WorkFlowType.CHECKER)) { + workFlowSchedulerService.stop((WorkFlow) proceedingJoinPoint.getTarget()); } + return new DefaultWorkReport(WorkStatus.COMPLETED, workContext); } - try { report = (WorkReport) proceedingJoinPoint.proceed(); log.info("Workflow {} is {}!", workflowName, report.getStatus().name()); @@ -161,121 +108,7 @@ public WorkReport executeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, W report = new DefaultWorkReport(WorkStatus.FAILED, workContext); } - // update workflow execution entity - workFlowExecution.setStatus(WorkFlowStatus.valueOf(report.getStatus().name())); - workFlowExecution.setEndDate(new Date()); - - WorkReport workReport = postExecution(isMaster, workFlowDefinition, (WorkFlow) proceedingJoinPoint.getTarget(), - report.getStatus(), workContext, workFlowExecution, masterWorkFlowExecution); - - return workReport == null ? report : workReport; - } - - private void startOrStopWorkFlowCheckerOnSchedule(WorkFlow workFlow, - WorkFlowCheckerMappingDefinition workFlowCheckerMappingDefinition, WorkStatus workStatus, - WorkContext workContext, String projectId, WorkFlowExecution masterWorkFlowExecution) { - if (workStatus != WorkStatus.COMPLETED) { - log.info("Schedule workflow checker: {} to run per cron expression: {}", workFlow.getName(), - workFlowCheckerMappingDefinition.getCronExpression()); - workFlowSchedulerService.schedule(workFlow, workContext, - workFlowCheckerMappingDefinition.getCronExpression()); - return; - } - - log.info("Stop workflow checker: {} schedule", workFlow.getName()); - workFlowSchedulerService.stop(workFlow); - - String masterWorkFlowName = WorkContextDelegate.read(workContext, - WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, WorkContextDelegate.Resource.NAME).toString(); - /* - * if this workflow is checker and it's successful, call continuation service to - * restart master workflow execution with same execution Id - */ - workFlowContinuationServiceImpl.continueWorkFlow(projectId, masterWorkFlowName, workContext, - masterWorkFlowExecution.getId()); - } - - private WorkFlowExecution handleFirstTimeMainWorkFlowExecution(UUID projectId, - WorkFlowDefinition workFlowDefinition, String arguments, WorkContext workContext) { - - /* - * if this is the first time execution for master workflow, persist it and write - * its execution id to workContext - */ - WorkFlowExecution workFlowExecution = this.workFlowService.saveWorkFlow(projectId, workFlowDefinition.getId(), - WorkFlowStatus.IN_PROGRESS, null, arguments); - WorkContextDelegate.write(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, - WorkContextDelegate.Resource.ID, workFlowExecution.getId()); - return workFlowExecution; - } - - private WorkReport postExecution(boolean isMaster, WorkFlowDefinition workFlowDefinition, WorkFlow workFlow, - WorkStatus workStatus, WorkContext workContext, WorkFlowExecution workFlowExecution, - WorkFlowExecution masterWorkFlowExecution) { - WorkReport workReport = null; - switch (workFlowDefinition.getType()) { - case INFRASTRUCTURE: - case ASSESSMENT: - workReport = handlePostWorkflowExecution(isMaster, workFlowExecution, workContext, workFlowDefinition, - masterWorkFlowExecution); - break; - - case CHECKER: - handlePostCheckerExecution(workFlowDefinition, workFlow, workFlowExecution, workStatus, workContext, - masterWorkFlowExecution); - break; - default: - break; - } - return workReport; - } - - private WorkReport handlePostWorkflowExecution(boolean isMaster, WorkFlowExecution workFlowExecution, - WorkContext workContext, WorkFlowDefinition workFlowDefinition, WorkFlowExecution masterWorkFlowExecution) { - WorkReport report = null; - if (isMaster) { - workFlowExecution.setWorkFlowExecutionContext(Optional - .ofNullable(workFlowExecution.getWorkFlowExecutionContext()).map(workFlowExecutionContext -> { - workFlowExecutionContext.setWorkContext(workContext); - return workFlowExecutionContext; - }).orElse(WorkFlowExecutionContext.builder().masterWorkFlowExecution(workFlowExecution) - .workContext(workContext).build())); - } - - /* - * if this is infrastructure/assessment workflow, fail it and persist as 'pending' - * if any of its sub work's execution is pending - */ - Set workFlowCheckerMappingDefinitions = workFlowDefinition - .getWorkFlowTaskDefinitions().stream().map(WorkFlowTaskDefinition::getWorkFlowCheckerMappingDefinition) - .filter(Objects::nonNull).collect(Collectors.toSet()); - - if (workFlowCheckerMappingDefinitions.stream() - .map(workFlowCheckerDefinition -> workFlowRepository - .findFirstByWorkFlowDefinitionIdAndMasterWorkFlowExecution( - workFlowCheckerDefinition.getCheckWorkFlow().getId(), masterWorkFlowExecution)) - .anyMatch(checkerExecution -> checkerExecution == null - || !WorkFlowStatus.COMPLETED.equals(checkerExecution.getStatus()))) { - log.info("fail workflow: {} because it has pending/running checker(s)", workFlowDefinition.getName()); - workFlowExecution.setStatus(WorkFlowStatus.PENDING); - report = new DefaultWorkReport(WorkStatus.IN_PROGRESS, workContext); - } - - workFlowService.updateWorkFlow(workFlowExecution); - return report; - } - - public void handlePostCheckerExecution(WorkFlowDefinition workFlowDefinition, WorkFlow workFlow, - WorkFlowExecution workFlowExecution, WorkStatus workStatus, WorkContext workContext, - WorkFlowExecution masterWorkFlowExecution) { - - workFlowService.updateWorkFlow(workFlowExecution); - /* - * if this workflow is a checker, schedule workflow checker for dynamic run on - * cron expression or stop if done - */ - startOrStopWorkFlowCheckerOnSchedule(workFlow, workFlowDefinition.getCheckerWorkFlowDefinition(), workStatus, - workContext, workFlowExecution.getProjectId().toString(), masterWorkFlowExecution); + return executionHandler.handlePostWorkFlowExecution(report, (WorkFlow) proceedingJoinPoint.getTarget()); } } diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactory.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactory.java new file mode 100644 index 000000000..a4c49c6a4 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactory.java @@ -0,0 +1,66 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.Optional; +import java.util.UUID; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; +import org.springframework.stereotype.Service; + +@Service +public class WorkFlowExecutionFactory { + + private final WorkFlowServiceImpl workFlowService; + + private final WorkFlowRepository workFlowRepository; + + private final WorkFlowSchedulerServiceImpl workFlowSchedulerService; + + private final WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + + public WorkFlowExecutionFactory(WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { + this.workFlowService = workFlowService; + this.workFlowRepository = workFlowRepository; + this.workFlowSchedulerService = workFlowSchedulerService; + this.workFlowContinuationServiceImpl = workFlowContinuationServiceImpl; + } + + public WorkFlowExecutionInterceptor createExecutionHandler(WorkFlowDefinition definition, WorkContext workContext) { + // get master WorkFlowExecution, this is the first time execution for master + // workflow if return null + UUID masterWorkFlowExecutionId = getMasterWorkFlowExecutionId(workContext); + if (masterWorkFlowExecutionId == null) { + return new FirstTimeWorkFlowExecutionInterceptor(definition, workContext, workFlowService, + workFlowRepository, workFlowSchedulerService, workFlowContinuationServiceImpl); + } + + if (isMasterWorkFlow(definition, workContext)) { + return new MasterWorkFlowExecutionInterceptor(definition, workContext, workFlowService, workFlowRepository, + workFlowSchedulerService, workFlowContinuationServiceImpl); + } + else { + return new ContinuedWorkFlowExecutionInterceptor(definition, workContext, workFlowService, + workFlowRepository, workFlowSchedulerService, workFlowContinuationServiceImpl); + } + } + + static boolean isMasterWorkFlow(WorkFlowDefinition workflow, WorkContext workContext) { + String masterWorkflowName = WorkContextDelegate.read(workContext, + WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, WorkContextDelegate.Resource.NAME).toString(); + return workflow.getName().equals(masterWorkflowName); + } + + static UUID getMasterWorkFlowExecutionId(WorkContext workContext) { + return Optional.ofNullable(WorkContextDelegate.read(workContext, + WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, WorkContextDelegate.Resource.ID)) + .map(id -> UUID.fromString(id.toString())).orElse(null); + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptor.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptor.java new file mode 100644 index 000000000..0badb2c48 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptor.java @@ -0,0 +1,99 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.Date; +import java.util.UUID; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.enums.WorkFlowStatus; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflow.util.WorkFlowDTOUtil; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import com.redhat.parodos.workflows.workflow.WorkFlow; + +public abstract class WorkFlowExecutionInterceptor implements WorkFlowInterceptor { + + protected final WorkFlowServiceImpl workFlowService; + + protected final WorkFlowRepository workFlowRepository; + + protected final WorkContext workContext; + + protected final WorkFlowDefinition workFlowDefinition; + + protected WorkFlowExecution workFlowExecution; + + private final WorkFlowSchedulerServiceImpl workFlowSchedulerService; + + private final WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + + public WorkFlowExecutionInterceptor(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowSchedulerServiceImpl workFlowSchedulerService, + WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl) { + this.workFlowDefinition = workFlowDefinition; + this.workContext = workContext; + this.workFlowService = workFlowService; + this.workFlowRepository = workFlowRepository; + this.workFlowSchedulerService = workFlowSchedulerService; + this.workFlowContinuationServiceImpl = workFlowContinuationServiceImpl; + } + + protected WorkFlowExecution saveWorkFlow(WorkFlowExecution masterWorkFlowExecution) { + String arguments = WorkFlowDTOUtil.writeObjectValueAsString( + WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + workFlowDefinition.getName(), WorkContextDelegate.Resource.ARGUMENTS)); + UUID projectId = UUID.fromString(WorkContextDelegate + .read(workContext, WorkContextDelegate.ProcessType.PROJECT, WorkContextDelegate.Resource.ID) + .toString()); + return workFlowService.saveWorkFlow(projectId, workFlowDefinition.getId(), WorkFlowStatus.IN_PROGRESS, + masterWorkFlowExecution, arguments); + } + + protected abstract WorkFlowExecution doPreWorkFlowExecution(); + + public WorkFlowExecution handlePreWorkFlowExecution() { + this.workFlowExecution = doPreWorkFlowExecution(); + return this.workFlowExecution; + } + + protected WorkFlowExecution getMasterWorkFlowExecution() { + return null; + } + + public WorkReport handlePostWorkFlowExecution(WorkReport report, WorkFlow workFlow) { + // update workflow execution entity + workFlowExecution.setStatus(WorkFlowStatus.valueOf(report.getStatus().name())); + workFlowExecution.setEndDate(new Date()); + + WorkFlowPostExecutionHandler postExecutor = createPostExecutor(workFlow, report.getStatus()); + WorkReport workReport = null; + if (postExecutor != null) { + workReport = postExecutor.handlePostWorkFlowExecution(); + } + return workReport == null ? report : workReport; + } + + private WorkFlowPostExecutionHandler createPostExecutor(WorkFlow workFlow, WorkStatus workStatus) { + switch (workFlowDefinition.getType()) { + case INFRASTRUCTURE: + case ASSESSMENT: + return new WorkFlowPostExecution(workFlowDefinition, workContext, workFlowService, workFlowRepository, + workFlowExecution, getMasterWorkFlowExecution()); + case CHECKER: + return new CheckerWorkFlowPostExecution(workFlowDefinition, workContext, workFlowService, + workFlowSchedulerService, workFlowContinuationServiceImpl, workFlowExecution, + getMasterWorkFlowExecution(), workFlow, workStatus); + default: + break; + } + return null; + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowInterceptor.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowInterceptor.java new file mode 100644 index 000000000..23e66bbcc --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowInterceptor.java @@ -0,0 +1,13 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.workflow.WorkFlow; + +public interface WorkFlowInterceptor { + + WorkFlowExecution handlePreWorkFlowExecution(); + + WorkReport handlePostWorkFlowExecution(WorkReport report, WorkFlow target); + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecution.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecution.java new file mode 100644 index 000000000..3082c6b80 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecution.java @@ -0,0 +1,84 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.redhat.parodos.workflow.execution.aspect.WorkFlowExecutionFactory.isMasterWorkFlow; + +import com.redhat.parodos.workflow.definition.entity.WorkFlowCheckerMappingDefinition; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.definition.entity.WorkFlowTaskDefinition; +import com.redhat.parodos.workflow.enums.WorkFlowStatus; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecutionContext; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.DefaultWorkReport; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class WorkFlowPostExecution implements WorkFlowPostExecutionHandler { + + private final WorkFlowDefinition workFlowDefinition; + + private final WorkContext workContext; + + private final WorkFlowExecution workFlowExecution; + + private final WorkFlowExecution masterWorkFlowExecution; + + private final WorkFlowServiceImpl workFlowService; + + private final WorkFlowRepository workFlowRepository; + + public WorkFlowPostExecution(WorkFlowDefinition workFlowDefinition, WorkContext workContext, + WorkFlowServiceImpl workFlowService, WorkFlowRepository workFlowRepository, + WorkFlowExecution workFlowExecution, WorkFlowExecution masterWorkFlowExecution) { + this.workFlowDefinition = workFlowDefinition; + this.workContext = workContext; + this.workFlowService = workFlowService; + this.workFlowRepository = workFlowRepository; + this.workFlowExecution = workFlowExecution; + this.masterWorkFlowExecution = masterWorkFlowExecution; + } + + public WorkReport handlePostWorkFlowExecution() { + WorkReport report = null; + if (isMasterWorkFlow(workFlowDefinition, workContext)) { + workFlowExecution.setWorkFlowExecutionContext(Optional + .ofNullable(workFlowExecution.getWorkFlowExecutionContext()).map(workFlowExecutionContext -> { + workFlowExecutionContext.setWorkContext(workContext); + return workFlowExecutionContext; + }).orElse(WorkFlowExecutionContext.builder().masterWorkFlowExecution(workFlowExecution) + .workContext(workContext).build())); + } + + /* + * if this is infrastructure/assessment workflow, fail it and persist as 'pending' + * if any of its sub work's execution is pending + */ + Set workFlowCheckerMappingDefinitions = workFlowDefinition + .getWorkFlowTaskDefinitions().stream().map(WorkFlowTaskDefinition::getWorkFlowCheckerMappingDefinition) + .filter(Objects::nonNull).collect(Collectors.toSet()); + + if (workFlowCheckerMappingDefinitions.stream() + .map(workFlowCheckerDefinition -> workFlowRepository + .findFirstByWorkFlowDefinitionIdAndMasterWorkFlowExecution( + workFlowCheckerDefinition.getCheckWorkFlow().getId(), masterWorkFlowExecution)) + .anyMatch(checkerExecution -> checkerExecution == null + || !WorkFlowStatus.COMPLETED.equals(checkerExecution.getStatus()))) { + log.info("fail workflow: {} because it has pending/running checker(s)", workFlowDefinition.getName()); + workFlowExecution.setStatus(WorkFlowStatus.PENDING); + report = new DefaultWorkReport(WorkStatus.IN_PROGRESS, workContext); + } + + workFlowService.updateWorkFlow(workFlowExecution); + return report; + } + +} diff --git a/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecutionHandler.java b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecutionHandler.java new file mode 100644 index 000000000..62662c3c9 --- /dev/null +++ b/workflow-service/src/main/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowPostExecutionHandler.java @@ -0,0 +1,9 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import com.redhat.parodos.workflows.work.WorkReport; + +public interface WorkFlowPostExecutionHandler { + + WorkReport handlePostWorkFlowExecution(); + +} diff --git a/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspectTest.java b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspectTest.java index b537e3164..51cc8482b 100644 --- a/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspectTest.java +++ b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionAspectTest.java @@ -12,7 +12,6 @@ import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; -import com.redhat.parodos.workflow.execution.repository.WorkFlowTaskRepository; import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; import com.redhat.parodos.workflows.work.DefaultWorkReport; @@ -70,9 +69,6 @@ class WorkFlowExecutionAspectTest { @Mock private WorkFlowRepository workFlowRepository; - @Mock - private WorkFlowTaskRepository workFlowTaskRepository; - @Mock private WorkFlowWorkRepository workFlowWorkRepository; @@ -83,10 +79,12 @@ public void initEach() { this.workFlowService = Mockito.mock(WorkFlowServiceImpl.class); this.workFlowSchedulerService = Mockito.mock(WorkFlowSchedulerServiceImpl.class); this.workFlowDefinitionRepository = Mockito.mock(WorkFlowDefinitionRepository.class); + WorkFlowExecutionFactory workFlowExecutionFactory = new WorkFlowExecutionFactory(workFlowService, + workFlowRepository, workFlowSchedulerService, workFlowContinuationService); WorkFlow workflow = Mockito.mock(WorkFlow.class); WorkFlowDelegate workFlowDelegate = Mockito.mock(WorkFlowDelegate.class); - this.workFlowExecutionAspect = new WorkFlowExecutionAspect(this.workFlowService, this.workFlowSchedulerService, - this.workFlowDefinitionRepository, this.workFlowRepository, this.workFlowContinuationService); + this.workFlowExecutionAspect = new WorkFlowExecutionAspect(this.workFlowSchedulerService, + this.workFlowDefinitionRepository, workFlowExecutionFactory); Mockito.when(workFlowDelegate.getWorkFlowExecutionByName(Mockito.any())) .thenReturn(Mockito.mock(WorkFlow.class)); Mockito.when(workflow.getName()).thenReturn(TEST); @@ -109,7 +107,7 @@ public void ExecuteAroundAdviceWithValidDataTest() { WorkFlowExecution workFlowExecution = getSampleWorkFlowExecution(); Mockito.when(this.workFlowDefinitionRepository.findFirstByName(Mockito.any())).thenReturn(workFlowDefinition); Mockito.when(this.workFlowService.saveWorkFlow(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), - Mockito.any())).thenReturn(getSampleWorkFlowExecution()); + Mockito.any())).thenReturn(workFlowExecution); ProceedingJoinPoint proceedingJoinPoint = Mockito.mock(ProceedingJoinPoint.class); WorkFlow workFlow = Mockito.mock(WorkFlow.class); @@ -156,7 +154,7 @@ void ExecuteAroundAdviceWithInProgressWorkFlowTest() { WorkFlowExecution workFlowExecution = getSampleWorkFlowExecution(); Mockito.when(this.workFlowDefinitionRepository.findFirstByName(Mockito.any())).thenReturn(workFlowDefinition); Mockito.when(this.workFlowService.saveWorkFlow(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), - Mockito.any())).thenReturn(getSampleWorkFlowExecution()); + Mockito.any())).thenReturn(workFlowExecution); Mockito.when(workFlowWorkRepository.findByWorkDefinitionId(Mockito.any())) .thenReturn(List.of(workFlowWorkDefinition)); ProceedingJoinPoint proceedingJoinPoint = Mockito.mock(ProceedingJoinPoint.class); diff --git a/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactoryTest.java b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactoryTest.java new file mode 100644 index 000000000..dfdd23d53 --- /dev/null +++ b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionFactoryTest.java @@ -0,0 +1,141 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +class WorkFlowExecutionFactoryTest { + + private WorkFlowExecutionFactory factory; + + @Mock + private WorkFlowServiceImpl workFlowService; + + @Mock + private WorkFlowRepository workFlowRepository; + + @Mock + private WorkFlowSchedulerServiceImpl workFlowSchedulerService; + + @Mock + private WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + + @BeforeEach + void setUp() { + factory = new WorkFlowExecutionFactory(workFlowService, workFlowRepository, workFlowSchedulerService, + workFlowContinuationServiceImpl); + } + + @Test + void testCreateMasterWorkFlowExecutionInterceptor() { + // given + WorkFlowDefinition workflow = mock(WorkFlowDefinition.class); + when(workflow.getName()).thenReturn("TestWorkflow"); + + WorkContext workContext = mock(WorkContext.class); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, + WorkContextDelegate.Resource.NAME)).thenReturn("TestWorkflow"); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + WorkContextDelegate.Resource.ID)).thenReturn(UUID.randomUUID().toString()); + + // when + WorkFlowExecutionInterceptor executionHandler = factory.createExecutionHandler(workflow, workContext); + + // then + // verify that the correct interceptor is created + assertNotNull(executionHandler); + assertTrue(executionHandler instanceof MasterWorkFlowExecutionInterceptor); + } + + @Test + void testCreateFirstTimeWorkFlowExecutionInterceptor() { + // given + WorkFlowDefinition workflow = mock(WorkFlowDefinition.class); + when(workflow.getName()).thenReturn("TestWorkflow"); + + WorkContext workContext = mock(WorkContext.class); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + WorkContextDelegate.Resource.ID)).thenReturn(null); + + // when + WorkFlowExecutionInterceptor executionHandler = factory.createExecutionHandler(workflow, workContext); + + // then + // verify that the correct interceptor is created + assertNotNull(executionHandler); + assertTrue(executionHandler instanceof FirstTimeWorkFlowExecutionInterceptor); + } + + @Test + void testCreateContinuedWorkFlowExecutionInterceptor() { + // given + WorkFlowDefinition workflow = mock(WorkFlowDefinition.class); + when(workflow.getName()).thenReturn("TestWorkflow"); + + WorkContext workContext = mock(WorkContext.class); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + WorkContextDelegate.Resource.ID)).thenReturn(UUID.randomUUID().toString()); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, + WorkContextDelegate.Resource.NAME)).thenReturn("OtherTestWorkflow"); + + // when + WorkFlowExecutionInterceptor executionHandler = factory.createExecutionHandler(workflow, workContext); + + // then + // verify that the correct interceptor is created + assertNotNull(executionHandler); + assertTrue(executionHandler instanceof ContinuedWorkFlowExecutionInterceptor); + } + + @Test + void testIsMasterWorkFlow() { + // when + WorkFlowDefinition workflow = mock(WorkFlowDefinition.class); + when(workflow.getName()).thenReturn("TestWorkflow"); + + WorkContext workContext = mock(WorkContext.class); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, + WorkContextDelegate.Resource.NAME)).thenReturn("TestWorkflow"); + + // given + boolean isMaster = WorkFlowExecutionFactory.isMasterWorkFlow(workflow, workContext); + + // then + assertTrue(isMaster); + } + + @Test + void testGetMasterWorkFlowExecutionId() { + // when + WorkContext workContext = mock(WorkContext.class); + String expectedExecutionId = UUID.randomUUID().toString(); + when(WorkContextDelegate.read(workContext, WorkContextDelegate.ProcessType.WORKFLOW_EXECUTION, + WorkContextDelegate.Resource.ID)).thenReturn(expectedExecutionId); + + // given + UUID masterId = WorkFlowExecutionFactory.getMasterWorkFlowExecutionId(workContext); + + // then + assertNotNull(masterId); + assertEquals(expectedExecutionId, masterId.toString()); + } + +} diff --git a/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptorTest.java b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptorTest.java new file mode 100644 index 000000000..f095f8d43 --- /dev/null +++ b/workflow-service/src/test/java/com/redhat/parodos/workflow/execution/aspect/WorkFlowExecutionInterceptorTest.java @@ -0,0 +1,142 @@ +package com.redhat.parodos.workflow.execution.aspect; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.redhat.parodos.workflow.context.WorkContextDelegate; +import com.redhat.parodos.workflow.definition.entity.WorkFlowCheckerMappingDefinition; +import com.redhat.parodos.workflow.definition.entity.WorkFlowDefinition; +import com.redhat.parodos.workflow.enums.WorkFlowStatus; +import com.redhat.parodos.workflow.enums.WorkFlowType; +import com.redhat.parodos.workflow.execution.continuation.WorkFlowContinuationServiceImpl; +import com.redhat.parodos.workflow.execution.entity.WorkFlowExecution; +import com.redhat.parodos.workflow.execution.repository.WorkFlowRepository; +import com.redhat.parodos.workflow.execution.scheduler.WorkFlowSchedulerServiceImpl; +import com.redhat.parodos.workflow.execution.service.WorkFlowServiceImpl; +import com.redhat.parodos.workflows.work.WorkContext; +import com.redhat.parodos.workflows.work.WorkReport; +import com.redhat.parodos.workflows.work.WorkStatus; +import com.redhat.parodos.workflows.workflow.WorkFlow; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +public class WorkFlowExecutionInterceptorTest { + + private WorkFlowExecutionInterceptor interceptor; + + @Mock + private WorkFlowServiceImpl workFlowService; + + @Mock + private WorkFlowRepository workFlowRepository; + + @Mock + private WorkContext workContext; + + @Mock + private WorkFlowDefinition workFlowDefinition; + + @Mock + private WorkFlowSchedulerServiceImpl workFlowSchedulerService; + + @Mock + private WorkFlowContinuationServiceImpl workFlowContinuationServiceImpl; + + private WorkFlowExecution expectedWorkFlowExecution; + + @Mock + private WorkFlowExecution masterWorkFlowExecution; + + @BeforeEach + public void setUp() { + expectedWorkFlowExecution = new WorkFlowExecution(); + expectedWorkFlowExecution.setProjectId(UUID.randomUUID()); + interceptor = new WorkFlowExecutionInterceptor(workFlowDefinition, workContext, workFlowService, + workFlowRepository, workFlowSchedulerService, workFlowContinuationServiceImpl) { + @Override + protected WorkFlowExecution doPreWorkFlowExecution() { + return expectedWorkFlowExecution; + } + + @Override + protected WorkFlowExecution getMasterWorkFlowExecution() { + return masterWorkFlowExecution; + } + }; + } + + @Test + public void testHandlePreWorkFlowExecution() { + WorkFlowExecution result = interceptor.handlePreWorkFlowExecution(); + assertEquals(expectedWorkFlowExecution, result); + } + + @Test + public void testHandleIncompletePostWorkFlowExecution() { + // given + WorkReport report = Mockito.mock(WorkReport.class); + when(report.getStatus()).thenReturn(WorkStatus.IN_PROGRESS); + + WorkFlow workFlow = Mockito.mock(WorkFlow.class); + when(workFlow.getName()).thenReturn("TestWorkFlow"); + + when(workFlowDefinition.getType()).thenReturn(WorkFlowType.CHECKER); + when(workFlowDefinition.getCheckerWorkFlowDefinition()) + .thenReturn(Mockito.mock(WorkFlowCheckerMappingDefinition.class)); + + // when + WorkFlowExecution workFlowExecution = interceptor.handlePreWorkFlowExecution(); + assertNotNull(workFlowExecution); + WorkReport result = interceptor.handlePostWorkFlowExecution(report, workFlow); + + // then + verify(workFlowService, Mockito.times(0)).saveWorkFlow(any(UUID.class), any(UUID.class), + eq(WorkFlowStatus.IN_PROGRESS), any(), anyString()); + verify(workFlowSchedulerService, Mockito.times(1)).schedule(Mockito.any(), Mockito.any(WorkContext.class), + Mockito.any()); + + assertEquals(result.getStatus(), report.getStatus()); + } + + @Test + public void testHandleCompletePostWorkFlowExecution() { + // given + WorkReport report = Mockito.mock(WorkReport.class); + when(report.getStatus()).thenReturn(WorkStatus.COMPLETED); + when(workContext.get(WorkContextDelegate.buildKey(WorkContextDelegate.ProcessType.WORKFLOW_DEFINITION, + WorkContextDelegate.Resource.NAME))).thenReturn("TestWorkFlow"); + WorkFlow workFlow = Mockito.mock(WorkFlow.class); + when(workFlow.getName()).thenReturn("TestWorkFlow"); + + when(workFlowDefinition.getType()).thenReturn(WorkFlowType.CHECKER); + when(workFlowDefinition.getCheckerWorkFlowDefinition()) + .thenReturn(Mockito.mock(WorkFlowCheckerMappingDefinition.class)); + when(masterWorkFlowExecution.getId()).thenReturn(UUID.randomUUID()); + + // when + WorkFlowExecution workFlowExecution = interceptor.handlePreWorkFlowExecution(); + assertNotNull(workFlowExecution); + WorkReport result = interceptor.handlePostWorkFlowExecution(report, workFlow); + + // then + verify(workFlowService, Mockito.times(0)).saveWorkFlow(any(UUID.class), any(UUID.class), + eq(WorkFlowStatus.IN_PROGRESS), any(), anyString()); + verify(workFlowSchedulerService, Mockito.times(1)).stop(Mockito.any(WorkFlow.class)); + verify(workFlowContinuationServiceImpl, Mockito.times(1)).continueWorkFlow(Mockito.anyString(), + Mockito.anyString(), Mockito.any(WorkContext.class), Mockito.any(UUID.class)); + assertEquals(result.getStatus(), report.getStatus()); + } + +}