Prompt bug cleanup. Filter workflow_approval jobs out of jobs list. Add initial support for timeout.

This commit is contained in:
mabashian
2019-07-30 13:34:50 -04:00
committed by Ryan Petrello
parent 3357c96774
commit 013792f0f8
11 changed files with 103 additions and 115 deletions

View File

@@ -15,6 +15,7 @@ export default {
job_search: { job_search: {
value: { value: {
not__launch_type: 'sync', not__launch_type: 'sync',
not__type: 'workflow_approval',
order_by: '-finished' order_by: '-finished'
}, },
dynamic: true, dynamic: true,

View File

@@ -148,7 +148,8 @@ function TemplatesStrings (BaseString) {
EXIT: t.s('EXIT'), EXIT: t.s('EXIT'),
CANCEL: t.s('CANCEL'), CANCEL: t.s('CANCEL'),
SAVE_AND_EXIT: t.s('SAVE & EXIT'), SAVE_AND_EXIT: t.s('SAVE & EXIT'),
APPROVAL: t.s('Approval') APPROVAL: t.s('Approval'),
TIMEOUT_POPOVER: t.s('The amount of time (in seconds) to wait before this approval step is automatically denied. Defaults to 0 for no timeout.')
}; };
} }

View File

@@ -15,8 +15,6 @@
height: 100%; height: 100%;
width: 540px; width: 540px;
background-color: @default-bg; background-color: @default-bg;
animation-name: slidein;
animation-duration: 250ms;
padding: 20px; padding: 20px;
overflow-y: scroll; overflow-y: scroll;
} }
@@ -63,14 +61,4 @@
opacity: 1; opacity: 1;
} }
} }
}
@keyframes slidein {
from {
width: 0px;
}
to {
width: 540px;
}
} }

View File

@@ -28,7 +28,6 @@
<div class="at-Row-items"> <div class="at-Row-items">
<div class="at-Row-container"> <div class="at-Row-container">
<div class="at-Row-container"> <div class="at-Row-container">
<!-- TODO: translate header tag -->
<at-row-item <at-row-item
header-value="{{ approval.summary_fields.source_workflow_job.name }}" header-value="{{ approval.summary_fields.source_workflow_job.name }}"
header-state="workflowResults({id: {{approval.summary_fields.source_workflow_job.id}}})" header-state="workflowResults({id: {{approval.summary_fields.source_workflow_job.id}}})"
@@ -40,10 +39,12 @@
<at-row-item <at-row-item
value-bind-html="{{ approval.created | longDate }}"> value-bind-html="{{ approval.created | longDate }}">
</at-row-item> </at-row-item>
<!-- todo: clean this up if it becomes a real thing-->
<at-row-item <at-row-item
style="color: red;" style="color: red;"
value-bind-html="Expires: Never"> value-bind-html="Expires: Never">
</at-row-item> </at-row-item>
<!-- todo: hook this up when timeout is active-->
<!-- <at-row-item <!-- <at-row-item
style="color: red;" style="color: red;"
value-bind-html="Expires {{ approval.created | longDate }}"> value-bind-html="Expires {{ approval.created | longDate }}">

View File

@@ -92,8 +92,8 @@
margin-left: 10px; margin-left: 10px;
padding: 5px; padding: 5px;
border-radius: 3px; border-radius: 3px;
background-color: red; background-color: @at-red-bright;
color: white; color: @at-white;
height: 15px; height: 15px;
font-size: 10px; font-size: 10px;
} }

View File

@@ -165,12 +165,12 @@ angular
'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
'LoadConfig', 'Store', 'pendoService', 'Rest', 'LoadConfig', 'Store', 'pendoService', 'Rest',
'$state', 'GetBasePath', 'ConfigService', '$state', 'GetBasePath', 'ConfigService',
'SocketService', 'AppStrings', '$transitions', 'SocketService', 'AppStrings', '$transitions', 'i18n',
function($q, $cookies, $rootScope, $log, $stateParams, function($q, $cookies, $rootScope, $log, $stateParams,
CheckLicense, $location, Authorization, LoadBasePaths, Timer, CheckLicense, $location, Authorization, LoadBasePaths, Timer,
LoadConfig, Store, pendoService, Rest, LoadConfig, Store, pendoService, Rest,
$state, GetBasePath, ConfigService, $state, GetBasePath, ConfigService,
SocketService, AppStrings, $transitions) { SocketService, AppStrings, $transitions, i18n) {
$rootScope.$state = $state; $rootScope.$state = $state;
$rootScope.$state.matches = function(stateName) { $rootScope.$state.matches = function(stateName) {
@@ -393,8 +393,11 @@ angular
.then(({data}) => { .then(({data}) => {
$rootScope.pendingApprovalCount = data.count; $rootScope.pendingApprovalCount = data.count;
}) })
.catch(() => { .catch(({data, status}) => {
// TODO: handle this ProcessErrors($scope, data, status, null, {
hdr: i18n._('Error!'),
msg: i18n._('Failed to get workflow jobs pending approval. GET returned status: ') + status
});
}); });
} }
} }

View File

@@ -42,11 +42,11 @@
export default ['$log', '$cookies', '$rootScope', export default ['$log', '$cookies', '$rootScope',
'$location', 'Authorization', 'Alert', 'Wait', 'Timer', '$location', 'Authorization', 'Alert', 'Wait', 'Timer',
'Empty', '$scope', 'pendoService', 'ConfigService', 'Empty', '$scope', 'pendoService', 'ConfigService',
'CheckLicense', 'SocketService', 'Rest', 'GetBasePath', 'CheckLicense', 'SocketService', 'Rest', 'GetBasePath', 'i18n',
function ($log, $cookies, $rootScope, function ($log, $cookies, $rootScope,
$location, Authorization, Alert, Wait, Timer, $location, Authorization, Alert, Wait, Timer,
Empty, scope, pendoService, ConfigService, Empty, scope, pendoService, ConfigService,
CheckLicense, SocketService, Rest, GetBasePath) { CheckLicense, SocketService, Rest, GetBasePath, i18n) {
var lastPath, lastUser, sessionExpired, loginAgain, preAuthUrl; var lastPath, lastUser, sessionExpired, loginAgain, preAuthUrl;
loginAgain = function() { loginAgain = function() {
@@ -145,8 +145,11 @@ export default ['$log', '$cookies', '$rootScope',
.then(({data}) => { .then(({data}) => {
$rootScope.pendingApprovalCount = data.count; $rootScope.pendingApprovalCount = data.count;
}) })
.catch(() => { .catch(({data, status}) => {
// TODO: handle this ProcessErrors($scope, data, status, null, {
hdr: i18n._('Error!'),
msg: i18n._('Failed to get workflow jobs pending approval. GET returned status: ') + status
});
}); });
}); });

View File

@@ -218,6 +218,7 @@ export default [ 'ProcessErrors', 'CredentialTypeModel', 'TemplatesStrings', '$f
if(vm.steps[step].tab) { if(vm.steps[step].tab) {
if(vm.steps[step].tab.order === currentTab.order) { if(vm.steps[step].tab.order === currentTab.order) {
vm.steps[step].tab._active = false; vm.steps[step].tab._active = false;
vm.steps[step].tab._disabled = true;
} else if(vm.steps[step].tab.order === currentTab.order + 1) { } else if(vm.steps[step].tab.order === currentTab.order + 1) {
activeTab = currentTab; activeTab = currentTab;
vm.steps[step].tab._active = true; vm.steps[step].tab._active = true;

View File

@@ -45,7 +45,7 @@
<div class="Prompt-footer"> <div class="Prompt-footer">
<button id="prompt_cancel" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="!vm.readOnlyPrompts">{{:: vm.strings.get('CANCEL') }}</button> <button id="prompt_cancel" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="!vm.readOnlyPrompts">{{:: vm.strings.get('CANCEL') }}</button>
<button id="prompt_close" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="vm.readOnlyPrompts">{{:: vm.strings.get('CLOSE') }}</button> <button id="prompt_close" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="vm.readOnlyPrompts">{{:: vm.strings.get('CLOSE') }}</button>
<button id="prompt_inventory_next" class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="vm.promptData.templateType === 'workflow_job_template' && !vm.promptDataClone.prompts.inventory.value.id && vm.promptDataClone.launchConf.defaults.inventory.id && !vm.readOnlyPrompts">{{:: vm.strings.get('NEXT') }}</button> <button id="prompt_inventory_next" class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="((vm.promptData.templateType === 'job_template' && !vm.promptDataClone.prompts.inventory.value.id) || (vm.promptData.templateType === 'workflow_job_template' && !vm.promptDataClone.prompts.inventory.value.id && vm.promptDataClone.launchConf.defaults.inventory.id)) && !vm.readOnlyPrompts">{{:: vm.strings.get('NEXT') }}</button>
<button id="prompt_credential_next" class="Prompt-actionButton" <button id="prompt_credential_next" class="Prompt-actionButton"
ng-show="vm.steps.credential.tab._active" ng-show="vm.steps.credential.tab._active"
ng-click="vm.next(vm.steps.credential.tab)" ng-click="vm.next(vm.steps.credential.tab)"

View File

@@ -290,15 +290,6 @@ export default ['Rest', 'GetBasePath', '$q', 'NextPage', function(Rest, GetBaseP
Rest.setUrl(url); Rest.setUrl(url);
return Rest.post(params.data); return Rest.post(params.data);
},
createApprovalTemplate: ({url, data}) => {
data = data || {};
Rest.setUrl(url);
return Rest.post(data);
},
patchApprovalTemplate: ({id, data}) => {
Rest.setUrl(`/api/v2/workflow_approval_templates/${id}`);
return Rest.patch(data);
} }
}; };
}]; }];

View File

@@ -5,11 +5,11 @@
*************************************************/ *************************************************/
export default ['$scope', 'TemplatesService', export default ['$scope', 'TemplatesService',
'ProcessErrors', '$q', 'ProcessErrors', '$q', 'Rest',
'PromptService', 'TemplatesStrings', 'WorkflowChartService', 'PromptService', 'TemplatesStrings', 'WorkflowChartService',
'Wait', '$state', 'Wait', '$state',
function ($scope, TemplatesService, function ($scope, TemplatesService,
ProcessErrors, $q, ProcessErrors, $q, Rest,
PromptService, TemplatesStrings, WorkflowChartService, PromptService, TemplatesStrings, WorkflowChartService,
Wait, $state Wait, $state
) { ) {
@@ -145,7 +145,6 @@ export default ['$scope', 'TemplatesService',
let editPromises = []; let editPromises = [];
let credentialRequests = []; let credentialRequests = [];
// TODO: clean up data generation of approval template requests
Object.keys(nodeRef).map((workflowMakerNodeId) => { Object.keys(nodeRef).map((workflowMakerNodeId) => {
const node = nodeRef[workflowMakerNodeId]; const node = nodeRef[workflowMakerNodeId];
if (node.isNew) { if (node.isNew) {
@@ -153,16 +152,15 @@ export default ['$scope', 'TemplatesService',
addPromises.push(TemplatesService.addWorkflowNode({ addPromises.push(TemplatesService.addWorkflowNode({
url: $scope.workflowJobTemplateObj.related.workflow_nodes, url: $scope.workflowJobTemplateObj.related.workflow_nodes,
data: {} data: {}
}).then(({data}) => { }).then(({data: newNodeData}) => {
approvalTemplatePromises.push(TemplatesService.createApprovalTemplate({ Rest.setUrl(newNodeData.related.create_approval_template);
url: data.related.create_approval_template, approvalTemplatePromises.push(Rest.post({
data: { name: node.unifiedJobTemplate.name,
name: node.unifiedJobTemplate.name, description: node.unifiedJobTemplate.description,
description: node.unifiedJobTemplate.description timeout: node.unifiedJobTemplate.timeout
}
}).then(() => { }).then(() => {
node.originalNodeObject = data; node.originalNodeObject = newNodeData;
nodeIdToChartNodeIdMapping[data.id] = parseInt(workflowMakerNodeId); nodeIdToChartNodeIdMapping[newNodeData.id] = parseInt(workflowMakerNodeId);
}).catch(({ data, status }) => { }).catch(({ data, status }) => {
Wait('stop'); Wait('stop');
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {
@@ -179,9 +177,9 @@ export default ['$scope', 'TemplatesService',
addPromises.push(TemplatesService.addWorkflowNode({ addPromises.push(TemplatesService.addWorkflowNode({
url: $scope.workflowJobTemplateObj.related.workflow_nodes, url: $scope.workflowJobTemplateObj.related.workflow_nodes,
data: buildSendableNodeData(node) data: buildSendableNodeData(node)
}).then(({data}) => { }).then(({data: newNodeData}) => {
node.originalNodeObject = data; node.originalNodeObject = newNodeData;
nodeIdToChartNodeIdMapping[data.id] = parseInt(workflowMakerNodeId); nodeIdToChartNodeIdMapping[newNodeData.id] = parseInt(workflowMakerNodeId);
if (_.get(node, 'promptData.launchConf.ask_credential_on_launch')) { if (_.get(node, 'promptData.launchConf.ask_credential_on_launch')) {
// This finds the credentials that were selected in the prompt but don't occur // This finds the credentials that were selected in the prompt but don't occur
// in the template defaults // in the template defaults
@@ -194,7 +192,7 @@ export default ['$scope', 'TemplatesService',
credentialIdsToPost.forEach((credentialToPost) => { credentialIdsToPost.forEach((credentialToPost) => {
credentialRequests.push({ credentialRequests.push({
id: data.id, id: newNodeData.id,
data: { data: {
id: credentialToPost.id id: credentialToPost.id
} }
@@ -211,12 +209,11 @@ export default ['$scope', 'TemplatesService',
} else if (node.isEdited) { } else if (node.isEdited) {
if (node.unifiedJobTemplate && node.unifiedJobTemplate.unified_job_type === "workflow_approval") { if (node.unifiedJobTemplate && node.unifiedJobTemplate.unified_job_type === "workflow_approval") {
if (node.originalNodeObject.summary_fields.unified_job_template.unified_job_type === "workflow_approval") { if (node.originalNodeObject.summary_fields.unified_job_template.unified_job_type === "workflow_approval") {
approvalTemplatePromises.push(TemplatesService.patchApprovalTemplate({ Rest.setUrl(node.originalNodeObject.related.unified_job_template);
id: node.originalNodeObject.summary_fields.unified_job_template.id, approvalTemplatePromises.push(Rest.patch({
data: { name: node.unifiedJobTemplate.name,
name: node.unifiedJobTemplate.name, description: node.unifiedJobTemplate.description,
description: node.unifiedJobTemplate.description timeout: node.unifiedJobTemplate.timeout
}
}).catch(({ data, status }) => { }).catch(({ data, status }) => {
Wait('stop'); Wait('stop');
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {
@@ -224,12 +221,11 @@ export default ['$scope', 'TemplatesService',
}); });
})); }));
} else { } else {
approvalTemplatePromises.push(TemplatesService.createApprovalTemplate({ Rest.setUrl(newNodeData.related.create_approval_template);
url: node.originalNodeObject.related.create_approval_template, approvalTemplatePromises.push(Rest.post({
data: { name: node.unifiedJobTemplate.name,
name: node.unifiedJobTemplate.name, description: node.unifiedJobTemplate.description,
description: node.unifiedJobTemplate.description timeout: node.unifiedJobTemplate.timeout
}
}).catch(({ data, status }) => { }).catch(({ data, status }) => {
Wait('stop'); Wait('stop');
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {
@@ -301,7 +297,6 @@ export default ['$scope', 'TemplatesService',
$q.all(approvalTemplatePromises) $q.all(approvalTemplatePromises)
.then(() => { .then(() => {
let disassociatePromises = []; let disassociatePromises = [];
let associatePromises = [];
let linkMap = {}; let linkMap = {};
// Build a link map for easy access // Build a link map for easy access
@@ -376,59 +371,60 @@ export default ['$scope', 'TemplatesService',
} }
}); });
Object.keys(linkMap).map((sourceNodeId) => {
Object.keys(linkMap[sourceNodeId]).map((targetNodeId) => {
const sourceChartNodeId = nodeIdToChartNodeIdMapping[sourceNodeId];
const targetChartNodeId = nodeIdToChartNodeIdMapping[targetNodeId];
switch(linkMap[sourceNodeId][targetNodeId]) {
case "success":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "success"
})
);
}
break;
case "failure":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "failure"
})
);
}
break;
case "always":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "always"
})
);
}
break;
}
});
});
$q.all(disassociatePromises) $q.all(disassociatePromises)
.then(() => { .then(() => {
let associatePromises = [];
Object.keys(linkMap).map((sourceNodeId) => {
Object.keys(linkMap[sourceNodeId]).map((targetNodeId) => {
const sourceChartNodeId = nodeIdToChartNodeIdMapping[sourceNodeId];
const targetChartNodeId = nodeIdToChartNodeIdMapping[targetNodeId];
switch(linkMap[sourceNodeId][targetNodeId]) {
case "success":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.success_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "success"
})
);
}
break;
case "failure":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.failure_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "failure"
})
);
}
break;
case "always":
if (
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes ||
!nodeRef[sourceChartNodeId].originalNodeObject.always_nodes.includes(nodeRef[targetChartNodeId].originalNodeObject.id)
) {
associatePromises.push(
TemplatesService.associateWorkflowNode({
parentId: parseInt(sourceNodeId),
nodeId: parseInt(targetNodeId),
edge: "always"
})
);
}
break;
}
});
});
let credentialPromises = credentialRequests.map((request) => { let credentialPromises = credentialRequests.map((request) => {
return TemplatesService.postWorkflowNodeCredential({ return TemplatesService.postWorkflowNodeCredential({
id: request.id, id: request.id,
@@ -589,6 +585,7 @@ export default ['$scope', 'TemplatesService',
unifiedJobTemplate: { unifiedJobTemplate: {
name: selectedTemplate.name, name: selectedTemplate.name,
description: selectedTemplate.description, description: selectedTemplate.description,
timeout: selectedTemplate.timeout,
unified_job_type: "workflow_approval" unified_job_type: "workflow_approval"
}, },
isNew: true isNew: true
@@ -631,6 +628,7 @@ export default ['$scope', 'TemplatesService',
nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = { nodeRef[$scope.nodeConfig.nodeId].unifiedJobTemplate = {
name: selectedTemplate.name, name: selectedTemplate.name,
description: selectedTemplate.description, description: selectedTemplate.description,
timeout: selectedTemplate.timeout,
unified_job_type: "workflow_approval" unified_job_type: "workflow_approval"
}; };
nodeRef[$scope.nodeConfig.nodeId].isEdited = true; nodeRef[$scope.nodeConfig.nodeId].isEdited = true;
@@ -643,7 +641,8 @@ export default ['$scope', 'TemplatesService',
node.unifiedJobTemplate = { node.unifiedJobTemplate = {
unified_job_type: 'workflow_approval', unified_job_type: 'workflow_approval',
name: selectedTemplate.name, name: selectedTemplate.name,
description: selectedTemplate.description description: selectedTemplate.description,
timeout: selectedTemplate.timeout,
}; };
} else { } else {
node.unifiedJobTemplate = selectedTemplate; node.unifiedJobTemplate = selectedTemplate;