This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git
commit 88ebac7cdd670d6a8ce5a254b5fdf41a205c0a15 Author: Alex Heneveld <a...@cloudsoft.io> AuthorDate: Fri Mar 15 23:00:59 2024 +0000 allow showing nested workflows in expanded workflow --- .../components/workflow/workflow-step.directive.js | 49 ++++++-- .../workflow/workflow-step.template.html | 124 +++++++++++---------- .../workflow/workflow-steps.directive.js | 20 ++-- .../app/components/workflow/workflow-steps.less | 7 +- .../workflow/workflow-steps.template.html | 5 +- .../inspect/activities/detail/detail.controller.js | 2 +- 6 files changed, 130 insertions(+), 77 deletions(-) diff --git a/ui-modules/app-inspector/app/components/workflow/workflow-step.directive.js b/ui-modules/app-inspector/app/components/workflow/workflow-step.directive.js index 5ca07ca7..87c200fe 100644 --- a/ui-modules/app-inspector/app/components/workflow/workflow-step.directive.js +++ b/ui-modules/app-inspector/app/components/workflow/workflow-step.directive.js @@ -41,12 +41,19 @@ export function workflowStepDirective() { expanded: '=', onSizeChange: '=', }, - controller: ['$sce', '$scope', controller], + controller: ['$sce', '$scope', 'entityApi', controller], controllerAs: 'vm', }; - function controller($sce, $scope) { + function controller($sce, $scope, entityApi) { try { + let observers = []; + $scope.$on('$destroy', ()=> { + observers.forEach((observer)=> { + observer.unsubscribe(); + }); + }); + let vm = this; let step = $scope.step; @@ -89,12 +96,12 @@ export function workflowStepDirective() { return "multiline-code multiline-code-resizable lines-"+lines.length; }; - $scope.json = null; - $scope.jsonMode = null; + $scope.addlData = null; + $scope.addlMode = null; vm.showAdditional = (title, mode, obj) => { - $scope.jsonTitle = title; - $scope.jsonMode = mode; - $scope.json = obj==undefined ? null : vm.yamlOrPrimitive(obj); + $scope.addlTitle = title; + $scope.addlMode = mode; + $scope.addlData = obj==undefined ? null : vm.yamlOrPrimitive(obj); } $scope.stepTitle = { @@ -212,6 +219,34 @@ export function workflowStepDirective() { $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. diff --git a/ui-modules/app-inspector/app/components/workflow/workflow-step.template.html b/ui-modules/app-inspector/app/components/workflow/workflow-step.template.html index 1eb67f7d..b504f46d 100644 --- a/ui-modules/app-inspector/app/components/workflow/workflow-step.template.html +++ b/ui-modules/app-inspector/app/components/workflow/workflow-step.template.html @@ -35,7 +35,7 @@ </span> </div> - <div class="workflow-step" id="workflow-step-{{stepIindex}}" ng-class="vm.getWorkflowStepClasses(stepIndex)"> + <div class="workflow-step workflow-{{workflowId}}" id="workflow-step-{{stepIindex}}" ng-class="vm.getWorkflowStepClasses(stepIndex)"> <div class="rhs-icons"> <div ng-if="isFocusTask" class="workflow-step-pill focus-step label-info" title="This step instance is for the task currently selected in the activity view."> selected @@ -183,9 +183,13 @@ </div> <div class="more-space-above"> + <div class="data-row" ng-if="uniqueSubworkflow"> + <workflow-steps nested="true" workflow="uniqueSubworkflow" style="flex: 1 1 auto; margin-left: -2em; margin-top: -24px; margin-right: -1em;"/> + </div> + <div class="data-row" ng-if="stepContext.error"><div class="A">Error</div> <div class="B {{ vm.classForCodeMaybeMultiline(stepContext.error) }}">{{ vm.yamlOrPrimitive(stepContext.error) }}</div></div> - <div class="data-row"><div class="A">Step Definition</div> <div class="B {{ vm.classForCodeMaybeMultiline(step) }}">{{ vm.yamlOrPrimitive(step) }}</div></div> + <div class="data-row" ng-if="showStepDefinitionInBody"><div class="A">Step Definition</div> <div class="B {{ vm.classForCodeMaybeMultiline(step) }}">{{ vm.yamlOrPrimitive(step) }}</div></div> <div ng-if="!isFocusStep || isFocusTask"> <div class="data-row" ng-if="stepContext.otherMetadata" ng-repeat="(key,value) in stepContext.otherMetadata" id="$key"> @@ -199,31 +203,34 @@ </div> <div class="more-space-above" ng-if="vm.nonEmpty(stepContext) || vm.nonEmpty(step) || vm.nonEmpty(osi)" - style="margin-bottom: {{ jsonTitle ? '9px' : '6px' }};"> + style="margin-bottom: {{ addlTitle ? '9px' : '6px' }};"> <div class="btn-group right" uib-dropdown> <button id="extra-data-button" type="button" class="btn btn-sm btn-select-dropdown pull-right" uib-dropdown-toggle - style="padding: 2px 6px; {{ jsonTitle ? 'width: 100%; ' : '' }} text-align: right;"> - {{ jsonTitle ? 'Showing '+jsonTitle : 'More Information' }} - <span class="caret" style="{{ jsonTitle ? 'rotate: 180deg;' : '' }}"></span> + style="padding: 2px 6px; {{ addlTitle ? 'width: 100%; ' : '' }} text-align: right;"> + {{ addlTitle ? 'Showing '+addlTitle : 'More Information' }} + <span class="caret" style="{{ addlTitle ? 'rotate: 180deg;' : '' }}"></span> </button> <ul class="dropdown-menu pull-right workflow-dropdown-small" uib-dropdown-menu role="menu" aria-labelledby="extra-data-button"> - <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Context Variables', 'vars', null)" ng-class="{'selected' : jsonMode === 'vars'}"> + <li role="menuitem" ng-if="!showStepDefinitionInBody"> <a href="" ng-click="vm.showAdditional('Step Definition', 'step_defn', vm.yamlOrPrimitive(step))" ng-class="{'selected' : addlMode === 'step_defn'}"> + <i class="fa fa-check check"></i> + Step Definition</a> </li> + <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Context Variables', 'vars', null)" ng-class="{'selected' : addlMode === 'vars'}"> <i class="fa fa-check check"></i> Context Variables</a> </li> - <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Additional Step Info', 'metadata', null)" ng-class="{'selected' : jsonMode === 'summary'}"> + <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Additional Step Info', 'metadata', null)" ng-class="{'selected' : addlMode === 'summary'}"> <i class="fa fa-check check"></i> Additional Step Info</a> </li> - <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Step Record YAML', 'osi', osi)" ng-class="{'selected' : jsonMode === 'osi'}"> + <li role="menuitem" > <a href="" ng-click="vm.showAdditional('Step Record YAML', 'osi', osi)" ng-class="{'selected' : addlMode === 'osi'}"> <i class="fa fa-check check"></i> Step Record YAML</a> </li> - <li role="menuitem" > <a href="" ng-click="vm.showAdditional(null, null)" ng-class="{'selected' : jsonMode === null}"> + <li role="menuitem" > <a href="" ng-click="vm.showAdditional(null, null)" ng-class="{'selected' : addlMode === null}"> <i class="fa fa-check check"></i> - None</a> </li> + Hide Additional Info</a> </li> </ul> </div> - <div ng-if="jsonMode === 'vars'"> + <div ng-if="addlMode === 'vars'"> <div class="data-row" ng-if="vm.getOutputAndScratchForStep(stepIndex).output"> <div class="A">Output of previous</div> <div class="B {{ vm.classForCodeMaybeMultiline(vm.getOutputAndScratchForStep(stepIndex).output) }}">{{ @@ -239,58 +246,61 @@ <div class="B {{ vm.classForCodeMaybeMultiline(workflow.data.input) }}">{{ vm.yamlOrPrimitive(workflow.data.input) }}</div> </div> + <div class="data-row" ng-if="!vm.getOutputAndScratchForStep(stepIndex).output && !vm.getOutputAndScratchForStep(stepIndex).workflowScratch && !workflow.data.input"> + <div class="A">No context variables set</div> + </div> </div> - <div ng-if="jsonMode === 'metadata'"> - <div class="data-row" ng-if="step.name"><div class="A">Name</div> <div class="B">{{ step.name }}</div></div> - <div class="data-row" ng-if="step.id"><div class="A">ID</div> <div class="B fixed-width">{{ step.id }}</div></div> - <div class="data-row"><div class="A">Step Number</div> <div class="B">{{ stepIndex+1 }}</div></div> - - <div ng-if="!isFocusStep || isFocusTask"> - <!-- only show these if not looking at an earlier run of a step --> - <div class="data-row more-space-above" ng-if="stepContext.input"><div class="A">All Input</div> <div class="B multiline-code">{{ vm.yaml(stepContext.input) }}</div></div> - </div> + <div ng-if="addlMode === 'metadata'"> + <div class="data-row" ng-if="step.name"><div class="A">Name</div> <div class="B">{{ step.name }}</div></div> + <div class="data-row" ng-if="step.id"><div class="A">ID</div> <div class="B fixed-width">{{ step.id }}</div></div> + <div class="data-row"><div class="A">Step Number</div> <div class="B">{{ stepIndex+1 }}</div></div> + + <div ng-if="!isFocusStep || isFocusTask"> + <!-- only show these if not looking at an earlier run of a step --> + <div class="data-row more-space-above" ng-if="stepContext.input"><div class="A">All Input</div> <div class="B multiline-code">{{ vm.yaml(stepContext.input) }}</div></div> + </div> <div class="data-row more-space-above"> - <div class="A"><span ng-if="isCurrentAndActive">CURRENTLY EXECUTING</span><span ng-if="!isCurrentAndActive"><span ng-if="osi.countStarted > 1">LAST </span>EXECUTED</span> IN</div> - <div class="B"> - <span ng-if="isFocusTask"> - task <span class="monospace">{{ stepContext.taskId }}</span> - </span> - <span ng-if="!isFocusTask"> - <a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: stepContext.taskId, workflowId })" - >task <span class="monospace">{{ stepContext.taskId }}</span></a> - </span> - </div> - </div> - <div ng-if="!isFocusStep || isFocusTask"> - <!-- only show these if not looking at an earlier run of a step --> - <div class="data-row"><div class="A">Preceeded by</div> <div class="B"> - <span ng-if="osi.previousTaskId"> - Step {{ osi.previous[0]+1 }} - (<a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: osi.previousTaskId, workflowId })" - >task <span class="monospace">{{ osi.previousTaskId }}</span></a>) - </span> - <span ng-if="!osi.previousTaskId">(workflow start)</span> - </div></div> - - <div class="data-row" ng-if="!isCurrentMaybeInactive"><div class="A">Followed by</div> <div class="B"> - <span ng-if="osi.nextTaskId"> - Step {{ osi.next[0]+1 }} - (<a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: osi.nextTaskId, workflowId })" - >task <span class="monospace">{{ osi.nextTaskId }}</span></a>) - </span> - <span ng-if="!osi.nextTaskId">(workflow end)</span> - </div></div> + <div class="A"><span ng-if="isCurrentAndActive">CURRENTLY EXECUTING</span><span ng-if="!isCurrentAndActive"><span ng-if="osi.countStarted > 1">LAST </span>EXECUTED</span> IN</div> + <div class="B"> + <span ng-if="isFocusTask"> + task <span class="monospace">{{ stepContext.taskId }}</span> + </span> + <span ng-if="!isFocusTask"> + <a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: stepContext.taskId, workflowId })" + >task <span class="monospace">{{ stepContext.taskId }}</span></a> + </span> </div> + </div> + <div ng-if="!isFocusStep || isFocusTask"> + <!-- only show these if not looking at an earlier run of a step --> + <div class="data-row"><div class="A">Preceeded by</div> <div class="B"> + <span ng-if="osi.previousTaskId"> + Step {{ osi.previous[0]+1 }} + (<a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: osi.previousTaskId, workflowId })" + >task <span class="monospace">{{ osi.previousTaskId }}</span></a>) + </span> + <span ng-if="!osi.previousTaskId">(workflow start)</span> + </div></div> - <div ng-if="osi.countStarted > 1 && osi.countStarted > osi.countCompleted" class="space-above"> - <div class="data-row more-space-above"><div class="A">Runs</div> <div class="B"><b>{{ osi.countStarted }}</b></div></div> - <div class="data-row"><div class="A">Succeeded</div> <div class="B">{{ osi.countCompleted }}</div></div> - <div class="data-row"><div class="A">Failed</div> <div class="B">{{ osi.countStarted - osi.countCompleted - (isCurrentAndActive ? 1 : 0) }}</div></div> - </div> + <div class="data-row" ng-if="!isCurrentMaybeInactive"><div class="A">Followed by</div> <div class="B"> + <span ng-if="osi.nextTaskId"> + Step {{ osi.next[0]+1 }} + (<a ui-sref="main.inspect.activities.detail({applicationId: workflow.applicationId, entityId: workflow.entityId, activityId: osi.nextTaskId, workflowId })" + >task <span class="monospace">{{ osi.nextTaskId }}</span></a>) + </span> + <span ng-if="!osi.nextTaskId">(workflow end)</span> + </div></div> + </div> + + <div ng-if="osi.countStarted > 1 && osi.countStarted > osi.countCompleted" class="space-above"> + <div class="data-row more-space-above"><div class="A">Runs</div> <div class="B"><b>{{ osi.countStarted }}</b></div></div> + <div class="data-row"><div class="A">Succeeded</div> <div class="B">{{ osi.countCompleted }}</div></div> + <div class="data-row"><div class="A">Failed</div> <div class="B">{{ osi.countStarted - osi.countCompleted - (isCurrentAndActive ? 1 : 0) }}</div></div> </div> + </div> - <pre ng-if="json" class="more-space-above">{{ json }}</pre> + <pre ng-if="addlData" class="more-space-above">{{ addlData }}</pre> </div> </div> diff --git a/ui-modules/app-inspector/app/components/workflow/workflow-steps.directive.js b/ui-modules/app-inspector/app/components/workflow/workflow-steps.directive.js index b48cf7e6..f82d9216 100644 --- a/ui-modules/app-inspector/app/components/workflow/workflow-steps.directive.js +++ b/ui-modules/app-inspector/app/components/workflow/workflow-steps.directive.js @@ -33,6 +33,7 @@ export function workflowStepsDirective() { scope: { workflow: '=', task: '=?', + nested: '=?', }, controller: ['$sce', '$timeout', '$scope', '$element', controller], controllerAs: 'vm', @@ -42,6 +43,7 @@ export function workflowStepsDirective() { let vm = this; vm.stringify = stringify; + $scope.workflowId = $scope.workflow.data.workflowId; vm.getWorkflowStepsClasses = () => { const c = []; @@ -60,22 +62,23 @@ export function workflowStepsDirective() { vm.onSizeChange = () => $timeout(()=>recompute($scope, $element)); $scope.$watch('workflow', vm.onSizeChange); + $scope.$watch(() => $element[0].offsetHeight, (newVal, oldVal) => { + if (oldVal!=newVal) vm.onSizeChange(); + }); vm.onSizeChange(); } function recompute($scope, $element) { - let svg = $element[0].querySelector('#workflow-step-arrows'); - - let steps = $element[0].querySelectorAll('div'); - // let steps = $element[0].querySelectorAll('.workflow-steps-main'); - steps = $element[0].querySelectorAll('.workflow-step'); - let arrows = makeArrows($scope.workflow, steps); + let svg = $element[0].querySelector('#workflow-step-arrows.workflow-'+$scope.workflowId); + let steps = $element[0].querySelectorAll('.workflow-'+$scope.workflowId+'.workflow-step'); + let arrows = makeArrows($scope.workflow, steps, { width: $scope.nested ? 32 : 56 }); svg.innerHTML = arrows.join('\n'); } } -function makeArrows(workflow, steps) { +function makeArrows(workflow, steps, options) { + const sectionWidth = ((options || {}).width) || 56; workflow = workflow || {}; workflow.data = workflow.data || {}; @@ -105,7 +108,7 @@ function makeArrows(workflow, steps) { if (!opts) opts = {}; const color = opts.class ? '' : opts.color || (opts.colorEnd && opts.colorEnd==opts.colorStart ? opts.colorEnd : '#000'); - const rightFarEdge = 56; + const rightFarEdge = sectionWidth; const rightArrowheadStart = rightFarEdge - arrowheadLength; const leftFarEdge = 10; const leftActive = rightArrowheadStart + (leftFarEdge - rightArrowheadStart) * (opts.width || 1); @@ -250,6 +253,7 @@ function makeArrows(workflow, steps) { var indexOfId = {}; for (var i = 0; i < steps.length; i++) { const s = workflow.data.stepsDefinition[i]; + if (!s) console.log("XXX missing step", i, workflow.data, steps); if (s.id) indexOfId[s.id] = i; } diff --git a/ui-modules/app-inspector/app/components/workflow/workflow-steps.less b/ui-modules/app-inspector/app/components/workflow/workflow-steps.less index 643135bd..a2d73e0e 100644 --- a/ui-modules/app-inspector/app/components/workflow/workflow-steps.less +++ b/ui-modules/app-inspector/app/components/workflow/workflow-steps.less @@ -35,6 +35,11 @@ padding-top: 5px; padding-bottom: 5px; + .workflow-steps .workflow-step { + // when nested, smaller right margin + margin-right: 36px; + } + border: solid @gray-light-lighter 1px; .rhs-icons { @@ -78,7 +83,7 @@ visibility: hidden; //display: none; } - &:hover .step-id.hover-only { + &:hover > .rhs-icons > .step-id.hover-only { visibility: visible; display: inherit; } diff --git a/ui-modules/app-inspector/app/components/workflow/workflow-steps.template.html b/ui-modules/app-inspector/app/components/workflow/workflow-steps.template.html index b571686e..fcddb1bf 100644 --- a/ui-modules/app-inspector/app/components/workflow/workflow-steps.template.html +++ b/ui-modules/app-inspector/app/components/workflow/workflow-steps.template.html @@ -17,15 +17,14 @@ under the License. --> <div class="workflow-steps" ng-class="vm.getWorkflowStepsClasses()"> - <svg width="100%" height="100%" style="position: absolute;" class="workflow-arrows"> - <g transform="scale(-1,1)" transform-origin="center" id="workflow-step-arrows"> + <g transform="scale(-1,1)" transform-origin="center" class="workflow-{{workflowId}}" id="workflow-step-arrows"> </g> </svg> <div style="position: relative;"> <div ng-if="workflow.data.stepsDefinition" class="workflow-steps-main"> - <div ng-repeat="step in workflow.data.stepsDefinition track by $index" id="workflow-step-outer-{{$index}}"> + <div ng-repeat="step in workflow.data.stepsDefinition track by $index" id="workflow-{{workflowId}}-step-outer-{{$index}}"> <workflow-step workflow="workflow" task="task" step="step" step-index="$index" expanded="expandStates[$index]" on-size-change="vm.onSizeChange"></workflow-step> </div> </div> diff --git a/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.controller.js b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.controller.js index b6c870ee..11b46ec0 100644 --- a/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.controller.js +++ b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.controller.js @@ -159,7 +159,7 @@ function DetailController($scope, $state, $stateParams, $location, $log, $uibMod let osi = vm.model.workflow.data.oldStepInfo; vm.model.workflow.finishedWithNoSteps = ((osi["-2"] || {}).previous || [])[0] == -1; - $scope.actions.workflowReplays = []; + $scope.actions.workflowReplays = []; if (vm.model.workflow.data.status !== 'RUNNING') { $scope.actions.workflowReplays = [];