You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2022/10/21 13:58:26 UTC
[brooklyn-ui] 17/24: replay buttons and arrow tidies
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 62c2b8668eb6910a8a08c1a886fc8bf8d1020313
Author: Alex Heneveld <al...@cloudsoft.io>
AuthorDate: Mon Oct 10 21:26:43 2022 +0100
replay buttons and arrow tidies
---
.../components/providers/entity-api.provider.js | 5 ++
.../components/workflow/workflow-step.directive.js | 2 +-
.../workflow/workflow-steps.directive.js | 20 ++++--
.../inspect/activities/detail/detail.controller.js | 72 ++++++++++++++++------
.../inspect/activities/detail/detail.template.html | 14 ++---
5 files changed, 83 insertions(+), 30 deletions(-)
diff --git a/ui-modules/app-inspector/app/components/providers/entity-api.provider.js b/ui-modules/app-inspector/app/components/providers/entity-api.provider.js
index 62d64f76..aa34cfd3 100644
--- a/ui-modules/app-inspector/app/components/providers/entity-api.provider.js
+++ b/ui-modules/app-inspector/app/components/providers/entity-api.provider.js
@@ -74,6 +74,7 @@ function EntityApi($http, $q) {
getWorkflows: getWorkflows,
getWorkflow: getWorkflow,
+ replayWorkflow: replayWorkflow,
};
function getEntity(applicationId, entityId) {
@@ -196,4 +197,8 @@ function EntityApi($http, $q) {
function getWorkflow(applicationId, entityId, workflowId) {
return $http.get('/v1/applications/'+ applicationId +'/entities/' + entityId + '/workflows/' + workflowId, {observable: true, ignoreLoadingBar: true});
}
+ function replayWorkflow(applicationId, entityId, workflowId, step, options) {
+ return $http.post('/v1/applications/'+ applicationId +'/entities/' + entityId + '/workflows/' + workflowId
+ + '/replay/from/' + step, {params: options, observable: true, ignoreLoadingBar: true});
+ }
}
\ No newline at end of file
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 dadf25a7..7cc22544 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
@@ -118,7 +118,7 @@ export function workflowStepDirective() {
: ($scope.isWorkflowError && $scope.isCurrentMaybeInactive) ? 'The workflow encountered an error around this step.'
: null;
const incomplete = $scope.osi.countStarted - $scope.osi.countCompleted > ($scope.isCurrentAndActive ? 1 : 0);
- $scope.stepCurrentWarning = incomplete ? 'This step has previously been interrupted.' : null;
+ $scope.stepCurrentWarning = incomplete && !$scope.stepCurrentError ? 'This step has previously had an error' : null;
$scope.stepCurrentSuccess = (!$scope.isCurrentAndActive && !incomplete && $scope.osi.countCompleted > 0)
? 'This step has completed without errors.' : null;
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 c808f1f8..143b665c 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
@@ -89,6 +89,7 @@ function makeArrows(workflow, steps) {
defs.push('<marker id="arrowhead" markerWidth="'+3*arrowheadWidth+'" markerHeight="'+3*arrowheadWidth+'" refX="'+0+'" refY="'+1.5*arrowheadWidth+'" orient="auto"><polygon fill="#000" points="0 0, '+3*arrowheadWidth+' '+1.5*arrowheadWidth+', 0 '+(3*arrowheadWidth)+'" /></marker><');
defs.push('<marker id="arrowhead-gray" markerWidth="'+3*arrowheadWidth+'" markerHeight="'+3*arrowheadWidth+'" refX="'+0+'" refY="'+1.5*arrowheadWidth+'" orient="auto"><polygon fill="#C0C0C0" points="0 0, '+3*arrowheadWidth+' '+1.5*arrowheadWidth+', 0 '+(3*arrowheadWidth)+'" /></marker><');
+ defs.push('<marker id="arrowhead-red" markerWidth="'+3*arrowheadWidth+'" markerHeight="'+3*arrowheadWidth+'" refX="'+0+'" refY="'+1.5*arrowheadWidth+'" orient="auto"><polygon fill="red" points="0 0, '+3*arrowheadWidth+' '+1.5*arrowheadWidth+', 0 '+(3*arrowheadWidth)+'" /></marker><');
if (steps) {
let gradientCount = 0;
@@ -179,6 +180,8 @@ function makeArrows(workflow, steps) {
return arrowSvg(s1, s2, opts);
}
+ let jumpSizes = {1: true};
+
function arrowStep2(prev, i, opts) {
let curveX = 0.5;
let curveY = 0.75;
@@ -216,7 +219,6 @@ function makeArrows(workflow, steps) {
return 'rgb('+gray+','+gray+','+gray+')';
}
- let jumpSizes = {1: true};
let arrowSpecs = {};
function recordTransition(from, to, opts) {
if (to!=-1 && from!=-1 && to!=from) {
@@ -259,22 +261,32 @@ function makeArrows(workflow, steps) {
for (var i = 0; i < steps.length; i++) {
const s = workflow.data.stepsDefinition[i];
+
+ let opts = { insertionPoint: 0 };
+ if (workflow.data.currentStepIndex === i && workflow.data.status && workflow.data.status.startsWith('ERROR')) {
+ recordTransition(i, -1, { ...opts, color: 'red', arrowheadId: 'arrowhead-red' });
+ }
+
+ opts = { ...opts, color: '#C0C0C0', arrowheadId: 'arrowhead-gray', dashLength: 8 };
+
let next = null;
if (s.next) {
if (s.next.toLowerCase()=='end') next = -1;
else if (indexOfId[s.next]) next = indexOfId[s.next];
}
if (isStepType(s, 'return')) next = -1;
+
if (next!=null) {
// special next per step
- recordTransition(i, next, { insertionPoint: 0, color: '#C0C0C0', arrowheadId: 'arrowhead-gray', dashLength: 8 });
+ recordTransition(i, next, opts);
if (!s.condition) continue;
}
// if nothing special, or if was conditional, then go to next step
+ // (only go forward 1, even if it is conditional, otherwise too many arrows)
+
next = i+1;
if (i + 1 >= steps.length) next = -1;
-
- recordTransition(i, next, { insertionPoint: 0, color: '#C0C0C0', arrowheadId: 'arrowhead-gray', dashLength: 8 });
+ recordTransition(i, next, opts);
}
jumpSizes = Object.keys(jumpSizes).sort();
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 de1fa975..a5cbe6b2 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
@@ -51,7 +51,7 @@ function DetailController($scope, $state, $stateParams, $log, $uibModal, $timeou
vm.modalTemplate = modalTemplate;
vm.wideKilt = false;
- vm.actions = {};
+ $scope.actions = {};
let observers = [];
@@ -76,21 +76,57 @@ function DetailController($scope, $state, $stateParams, $log, $uibModal, $timeou
vm.model.workflow.applicationId = workflowTag.applicationId;
vm.model.workflow.entityId = workflowTag.entityId;
- vm.actions.workflowReplays = [];
+ $scope.actions.workflowReplays = [];
if (!vm.model.activity.endTimeUtc || vm.model.activity.endTimeUtc<=0) {
// can't replay if active (same logic as 'cancel')
} else {
- [
- // TODO get from server
- // [ 'step 3 (continuing)', null ],
- // [ 'step 3 (replay point)', [2] ],
- // [ 'start (replay point)', [0] ],
- ].forEach(r => vm.actions.workflowReplays.push(r));
- vm.actions.workflowReplays.forEach(r => {
- r.push( () => console.log("TODO - replay from "+r[0], r[1]) );
- })
+ $scope.actions.workflowReplays = [];
+ const stepIndex = (vm.model.workflow.tag || {}).stepIndex;
+
+ let replayableFromStart = vm.model.workflow.data.replayableFromStart, replayableContinuing = vm.model.workflow.data.replayableLastStep>=0;
+
+ if (replayableContinuing) {
+ $scope.actions.workflowReplays.push({ targetId: 'end', targetName: 'Resume '+(stepIndex>=0 ? 'workflow ' : '')+' (at step '+(vm.model.workflow.data.replayableLastStep+1)+')' });
+ }
+
+ // get current step, replay from that step
+ if (stepIndex>=0) {
+ const osi = workflow.data.oldStepInfo[stepIndex] || {};
+ if (osi.replayableFromHere) {
+ $scope.actions.workflowReplays.push({ targetId: ''+stepIndex, targetName: 'Replay from here (step '+(stepIndex+1) });
+ } else {
+ $scope.actions.workflowReplays.push({ targetId: ''+stepIndex, targetName: 'Force replay from here (step '+(stepIndex+1), force: true });
+ }
+ }
+
+ if (replayableFromStart) {
+ let w1 = 'Restart', w2 = '(not resumable)';
+ if (stepIndex<0) { w1 = 'Run'; w2 = 'again'; }
+ else if (_.isNil(stepIndex)) { w2 = '(did not start)'; }
+ else if (replayableContinuing) w2 = '';
+
+ $scope.actions.workflowReplays.push({targetId: 'start', targetName: 'Restart '+(stepIndex>=0 ? 'workflow ' : '')+reason});
+ }
+
+ if (!replayableFromStart) {
+ $scope.actions.workflowReplays.push({targetId: 'start', targetName: 'Force restart', force: true});
+ }
+ // force replays
+ $scope.actions.workflowReplays.forEach(r => {
+ // could prompt for a reason
+ const targetId = r.targetId;
+ const opts = {};
+ opts.reason = "UI manual replay";
+ if (r.force) {
+ opts.force = true;
+ opts.reason += " (forced)";
+ }
+ r.action = () => {
+ entityApi.replay(applicationId, entityId, $scope.workflowId. targetId, opts);
+ };
+ });
}
- if (!vm.actions.workflowReplays.length) delete vm.actions['workflowReplays'];
+ if (!$scope.actions.workflowReplays.length) delete $scope.actions['workflowReplays'];
if (vm.model.workflow.data.status === 'RUNNING') wResponse.interval(1000);
observers.push(wResponse.subscribe((wResponse2)=> {
@@ -112,22 +148,22 @@ function DetailController($scope, $state, $stateParams, $log, $uibModal, $timeou
activityApi.activity(activityId).then((response)=> {
vm.model.activity = response.data;
- delete vm.actions['effector'];
- delete vm.actions['invokeAgain'];
+ delete $scope.actions['effector'];
+ delete $scope.actions['invokeAgain'];
if ((vm.model.activity.tags || []).find(t => t=="EFFECTOR")) {
const effectorName = (vm.model.activity.tags.find(t => t.effectorName) || {}).effectorName;
const effectorParams = (vm.model.activity.tags.find(t => t.effectorParams) || {}).effectorParams;
if (effectorName) {
- vm.actions.effector = {effectorName};
+ $scope.actions.effector = {effectorName};
if (effectorParams) {
- vm.actions.invokeAgain = {effectorName, effectorParams, doAction: () => vm.invokeEffector(effectorName, effectorParams) };
+ $scope.actions.invokeAgain = {effectorName, effectorParams, doAction: () => vm.invokeEffector(effectorName, effectorParams) };
}
}
}
- delete vm.actions['cancel'];
+ delete $scope.actions['cancel'];
if (!vm.model.activity.endTimeUtc || vm.model.activity.endTimeUtc<=0) {
- vm.actions.cancel = { doAction: () => { activityApi.cancelActivity(activityId); } };
+ $scope.actions.cancel = { doAction: () => { activityApi.cancelActivity(activityId); } };
}
$scope.workflowId = null; // if the task loads, force the workflow id to be found on it, otherwise ignore it
diff --git a/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.template.html b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.template.html
index 1efc6860..08b4b889 100644
--- a/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.template.html
+++ b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/detail.template.html
@@ -45,27 +45,27 @@
<code><a ui-sref="main.inspect.activities.detail({entityId: vm.model.activity.blockingTask.metadata.entityId, activityId: vm.model.activity.blockingTask.metadata.id})">{{vm.model.activity.blockingTask.metadata.taskName}}</a></code> for <strong><a ui-sref="main.inspect.summary({entityId: vm.model.activity.blockingTask.metadata.entityId})">{{vm.model.activity.blockingTask.metadata.entityDisplayName}} entity</a></strong>
</div>
- <div style="float:right;" ng-if="vm.isNonEmpty(vm.actions)" class="btn-group dropdown-nested dropdown-menu-right" uib-dropdown-nested dropdown-append-to-body="true">
+ <div style="float:right;" ng-if="vm.isNonEmpty(actions)" class="btn-group dropdown-nested dropdown-menu-right" uib-dropdown-nested dropdown-append-to-body="true">
<br-button uib-dropdown-toggle-nested type="btn-primary">
Actions <span class="caret"></span>
</br-button>
<ul uib-dropdown-menu-nested class="dropdown-at-root dropdown-menu-right">
- <li><a href="" ng-if="vm.actions.cancel" ng-click="vm.actions.cancel.doAction()">Cancel</a></li>
+ <li><a href="" ng-if="actions.cancel" ng-click="actions.cancel.doAction()">Cancel</a></li>
- <li ng-if="vm.actions.workflowReplays" uib-dropdown-nested dropdown-append-to-body="true">
+ <li ng-if="actions.workflowReplays" uib-dropdown-nested dropdown-append-to-body="true">
<a href="" uib-dropdown-toggle-nested>Replay workflow <span class="caret"></span></a>
<ul class="dropdown-submenu-left dropdown-at-root dropdown-menu-right" uib-dropdown-menu-nested>
- <li ng-repeat="replay in vm.actions.workflowReplays track by $index" id="replay {{ replay[0] }}">
- <a class="dropdown-item" href="" ng-click="replay[2]()">From {{ replay[0] }}</a>
+ <li ng-repeat="replay in actions.workflowReplays track by $index" id="replay-{{replay.targetId}}">
+ <a class="dropdown-item" href="" ng-click="replay.action()">{{ replay.targetName }}</a>
</li>
</ul>
</li>
- <li><a href="" ng-if="vm.actions.invokeAgain" ng-click="vm.actions.invokeAgain.doAction()">Reinvoke effector</a></li>
- <li><a href="" ng-if="vm.actions.effector" ui-sref="main.inspect.effectors({search: vm.actions.effector.effectorName})">Open in effector tab</a></li>
+ <li><a href="" ng-if="actions.invokeAgain" ng-click="actions.invokeAgain.doAction()">Reinvoke effector</a></li>
+ <li><a href="" ng-if="actions.effector" ui-sref="main.inspect.effectors({search: actions.effector.effectorName})">Open in effector tab</a></li>
</ul>
</div>