| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import template from "./workflow-step.template.html"; |
| import angular from "angular"; |
| import jsyaml from 'js-yaml'; |
| |
| const MODULE_NAME = 'inspector.workflow-step'; |
| |
| angular.module(MODULE_NAME, []) |
| .directive('workflowStep', workflowStepDirective); |
| |
| export default MODULE_NAME; |
| |
| let count = 0; |
| |
| export function workflowStepDirective() { |
| return { |
| template: template, |
| restrict: 'E', |
| scope: { |
| workflow: '=', |
| task: '=?', |
| step: '<', // definition |
| stepIndex: '<', |
| expanded: '=', |
| onSizeChange: '=', |
| }, |
| controller: ['$sce', '$scope', 'entityApi', controller], |
| controllerAs: 'vm', |
| }; |
| |
| function controller($sce, $scope, entityApi) { |
| try { |
| let observers = []; |
| $scope.$on('$destroy', ()=> { |
| observers.forEach((observer)=> { |
| observer.unsubscribe(); |
| }); |
| }); |
| |
| let vm = this; |
| |
| let step = $scope.step; |
| let index = $scope.stepIndex; |
| $scope.workflowId = ($scope.workflow && $scope.workflow.data || {}).workflowId; |
| |
| vm.toggleExpandState = () => { |
| $scope.expanded = !$scope.expanded; |
| if ($scope.onSizeChange) $scope.onSizeChange(); |
| } |
| vm.stringify = stringify; |
| vm.yaml = (data) => jsyaml.dump(data); |
| vm.yamlOrPrimitive = (data) => typeof data === "string" ? data : vm.yaml(data); |
| vm.nonEmpty = (data) => data && (data.length || Object.keys(data).length); |
| vm.isNullish = _.isNil; |
| |
| vm.getWorkflowNameFromReference = (ref, suffixIfFound) => { |
| if (ref) { |
| if (ref.workflowName) return ref.workflowName; |
| if ($scope.task && $scope.task.children) { |
| var matchingChild = $scope.task.children.find(c => c.metadata && c.metadata.id === ref.workflowId); |
| if (matchingChild && matchingChild.metadata.taskName) { |
| return matchingChild.metadata.taskName + (suffixIfFound ? " " + suffixIfFound + " " : ""); |
| } |
| } |
| } |
| return null; |
| }; |
| vm.hasInterestingWorkflowNameFromReference = (ref, suffixIfFound) => { |
| const wn = vm.getWorkflowNameFromReference(ref, suffixIfFound); |
| return wn && wn.toLowerCase()!='sub-workflow' && !wn.endsWith(' (sub-workflow)'); |
| }; |
| vm.classForCodeMaybeMultiline = (obj) => { |
| let os = vm.yamlOrPrimitive(obj); |
| if (!os) return "simple-code"; |
| os = ''+os; |
| if (os.endsWith('\n')) os = os.substring(0, os.length-1); |
| const lines = os.split('\n'); |
| if (lines.length==1) return "simple-code"; |
| if (lines.length <= 5) return "multiline-code lines-"+lines.length; |
| return "multiline-code multiline-code-resizable lines-"+lines.length; |
| }; |
| |
| $scope.addlData = null; |
| $scope.addlMode = null; |
| vm.showAdditional = (title, mode, obj) => { |
| $scope.addlTitle = title; |
| $scope.addlMode = mode; |
| $scope.addlData = obj==undefined ? null : vm.yamlOrPrimitive(obj); |
| } |
| |
| $scope.stepTitle = { |
| index: index+1, |
| }; |
| if (typeof step === 'string') { |
| $scope.stepTitle.code = step; |
| } else { |
| |
| let shorthand = step.userSuppliedShorthand || step.step || step.s || step.shorthand; |
| $scope.stepTitle.code = shorthand; |
| if (!shorthand) { |
| $scope.stepTitle.code = step.shorthandTypeName || step.type || ''; |
| if (!$scope.stepTitle.code) { |
| if (step.steps) $scope.stepTitle.leftCodeAlternative = "nested workflow"; |
| else $scope.stepTitle.leftCodeAlternative = "workflow step"; // odd... |
| } else { |
| if (step.input) $scope.stepTitle.code += ' ...'; |
| } |
| } |
| if (["workflow","subworkflow"].includes($scope.stepTitle.code)) { |
| $scope.stepTitle.code = null; |
| $scope.stepTitle.leftCodeAlternative = "nested workflow"; |
| } |
| |
| if (step.name) { |
| $scope.stepTitle.name = step.name; |
| } |
| if (step.id) { |
| $scope.stepTitle.id = step.id; |
| } |
| } |
| |
| function gatherOutputAndScratchForStep(osi, result, visited, isPrevious) { |
| if (result.output!=undefined && result.workflowScratch!=undefined) return ; |
| if (osi==undefined) { |
| if (result.workflowScratchPartial != undefined) result.workflowScratch = result.workflowScratchPartial; |
| delete result['workflowScratchPartial']; |
| return; |
| } |
| // osi.woSc |
| let output = osi.context && osi.context.output; |
| if (isPrevious && output != undefined && result.output == undefined) result.output = output; |
| if (isPrevious && osi.workflowScratchUpdates != undefined) result.workflowScratchPartial = |
| Object.assign({}, osi.workflowScratchUpdates, result.workflowScratchPartial); |
| if (osi.workflowScratch != undefined) result.workflowScratchPartial = |
| Object.assign({}, osi.workflowScratchUpdates, result.workflowScratchPartial); |
| |
| let next = undefined; |
| if (osi.previous && osi.previous.length) { |
| const pp = osi.previous[0]; |
| if (!visited.includes(pp)) { |
| visited.push(pp); |
| next = $scope.workflow.data.oldStepInfo[pp]; |
| } |
| } |
| gatherOutputAndScratchForStep(next, result, visited, true); |
| } |
| |
| const resultForStep = {}; |
| vm.getOutputAndScratchForStep = (i) => { |
| if (!resultForStep[i]) { |
| if (!$scope.workflow || !$scope.workflow.data || !$scope.workflow.data.oldStepInfo) return {}; |
| const osi = $scope.workflow.data.oldStepInfo[i] || null; |
| if (!osi) return {}; |
| resultForStep[i] = {}; |
| gatherOutputAndScratchForStep(osi, resultForStep[i], [i], false); |
| } |
| return resultForStep[i]; |
| } |
| |
| function updateData() { |
| let workflow = $scope.workflow; |
| workflow.data = workflow.data || {}; |
| $scope.workflowStepClasses = []; |
| if (workflow.data.currentStepIndex === index) $scope.workflowStepClasses.push('current-step'); |
| |
| $scope.isRunning = (workflow.data.status === 'RUNNING'); |
| $scope.isCurrentMaybeInactive = (workflow.data.currentStepIndex === index); |
| $scope.isCurrentAndActive = ($scope.isCurrentMaybeInactive && $scope.isRunning); |
| $scope.isWorkflowError = (workflow.data.status && workflow.data.status.startsWith('ERROR')); |
| $scope.osi = workflow.data.oldStepInfo[index] || {}; |
| $scope.stepContext = ($scope.isCurrentMaybeInactive ? workflow.data.currentStepInstance : $scope.osi.context) || {}; |
| $scope.isFocusStep = $scope.workflow.tag && ($scope.workflow.tag.stepIndex === index); |
| $scope.isFocusTask = false; |
| $scope.isErrorHandler = $scope.workflow.tag && ($scope.workflow.tag.errorHandlerForTask); |
| |
| // for switch, possibly others -- the step task wraps a chosen step task; |
| // show details for the wrapped chosen task, without showing weird messages |
| $scope.otherMetadata = Object.assign({}, $scope.stepContext.otherMetadata || {}); |
| if ($scope.stepContext.stepState && $scope.stepContext.stepState.selectedStepContext) { |
| $scope.innerStepContext = $scope.stepContext.stepState.selectedStepContext; |
| $scope.outerStepContext = $scope.stepContext; |
| $scope.isWrappingStepTaskOuter = $scope.task && $scope.stepContext.taskId == $scope.task.id; |
| $scope.stepContext = $scope.stepContext.stepState.selectedStepContext; |
| $scope.otherMetadata = Object.assign($scope.otherMetadata, $scope.stepContext.otherMetadata || {}); |
| $scope.isWrappingStepTaskInner = $scope.task && $scope.stepContext.taskId == $scope.task.id; |
| if ($scope.isWrappingStepTaskOuter || $scope.isWrappingStepTaskInner) { |
| $scope.isFocusTask = true; |
| } |
| } |
| |
| if ($scope.task) { |
| if (!vm.isNullish($scope.stepContext.taskId) && $scope.stepContext.taskId === $scope.task.id) { |
| $scope.isFocusTask = true; |
| |
| } else if ($scope.isFocusStep) { |
| // careful -- other instance of this step selected |
| } |
| } |
| |
| $scope.stepCurrentError = |
| ($scope.stepContext.error && !$scope.isFocusTask) |
| ? 'This step had an error.' |
| : ($scope.isWorkflowError && $scope.isCurrentMaybeInactive) |
| ? 'The workflow encountered an error at this step.' /* odd */ |
| : null; |
| const incomplete = $scope.osi.countStarted - $scope.osi.countCompleted > ($scope.isCurrentAndActive ? 1 : 0); |
| |
| $scope.stepCurrentWarning = $scope.stepCurrentError ? null : |
| $scope.stepContext.errorHandlerTaskId |
| ? 'This step had an error which was handled.' |
| : incomplete |
| ? 'This step has previously had an error' |
| : null; |
| $scope.stepCurrentSuccess = $scope.stepCurrentError || $scope.stepCurrentWarning ? null : |
| (!$scope.isCurrentAndActive && !incomplete && $scope.osi.countCompleted > 0) |
| ? 'This step has completed without errors.' : null; |
| } |
| $scope.$watch('workflow', updateData); |
| updateData(); |
| |
| $scope.showStepDefinitionInBody = true; |
| function loadUniqueSubworkflow(workflowTag) { |
| if (!workflowTag) return; |
| |
| return entityApi.getWorkflow(workflowTag.applicationId, workflowTag.entityId, workflowTag.workflowId).then(wResponse => { |
| if (wResponse.data.status === 'RUNNING') { |
| wResponse.interval(1000); |
| observers.push(wResponse.subscribe(() => loadUniqueSubworkflow(workflowTag))); |
| } |
| |
| $scope.uniqueSubworkflow = { |
| applicationId: workflowTag.applicationId, |
| entityId: workflowTag.entityId, |
| workflowTag: workflowTag, |
| data: wResponse.data, |
| }; |
| |
| }).catch(error => { |
| console.log("Unable to load single unique workflow", workflowTag, error); |
| $scope.showStepDefinitionInBody = false; |
| $scope.uniqueSubworkflow = undefined; |
| }); |
| } |
| if (!$scope.workflow.runIsOld && $scope.stepContext.subWorkflows && $scope.stepContext.subWorkflows.length==1) { |
| $scope.showStepDefinitionInBody = false; |
| loadUniqueSubworkflow($scope.stepContext.subWorkflows[0]); |
| } |
| |
| } catch (error) { |
| console.log("error showing workflow step", error); |
| // the ng-repeat seems to swallow and mask any error in the above - can't understand why! but log it here in case something breaks. |
| throw error; |
| } |
| } |
| |
| } |
| |
| function stringify(data) { return JSON.stringify(data, null, 2); } |