From e20d8c8e81bacde3e04c0551952fcfbc59e59f22 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Sat, 6 Oct 2018 19:41:12 -0400 Subject: [PATCH] Show workflow badge Add Workflow tags Hookup workflow details link Add parent workflow and job explanation fields Add workflow key icon to WF maker and WF results Hookup wf prompting Add wf key dropdown and hide wf info badge --- .../features/templates/templates.strings.js | 1 + awx/ui/client/legacy/styles/lists.less | 15 --- .../src/partials/job-template-details.html | 5 +- awx/ui/client/src/shared/generator-helpers.js | 7 ++ awx/ui/client/src/templates/main.js | 42 +++---- .../workflow-chart.directive.js | 92 ++++++++++++-- .../workflow-maker/workflow-maker.block.less | 11 +- .../workflow-maker.controller.js | 114 +++++++++++------- .../workflow-maker.partial.html | 6 + .../workflow-results.block.less | 10 ++ .../workflow-results.controller.js | 35 +++++- .../workflow-results.partial.html | 109 ++++++++++++----- 12 files changed, 317 insertions(+), 130 deletions(-) diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js index 08f4ae8926..c22d1805f7 100644 --- a/awx/ui/client/features/templates/templates.strings.js +++ b/awx/ui/client/features/templates/templates.strings.js @@ -102,6 +102,7 @@ function TemplatesStrings (BaseString) { ALWAYS: t.s('Always'), PROJECT_SYNC: t.s('Project Sync'), INVENTORY_SYNC: t.s('Inventory Sync'), + WORKFLOW: t.s('Workflow'), WARNING: t.s('Warning'), TOTAL_TEMPLATES: t.s('TOTAL TEMPLATES'), ADD_A_TEMPLATE: t.s('ADD A TEMPLATE'), diff --git a/awx/ui/client/legacy/styles/lists.less b/awx/ui/client/legacy/styles/lists.less index 28f9440438..47a3a9bf28 100644 --- a/awx/ui/client/legacy/styles/lists.less +++ b/awx/ui/client/legacy/styles/lists.less @@ -493,21 +493,6 @@ table, tbody { } } -.List-infoCell--badge { - height: 15px; - color: @default-interface-txt; - background-color: @default-list-header-bg; - border-radius: 5px; - font-size: 10px; - padding-left: 10px; - padding-right: 10px; - margin-left: 10px; - text-transform: uppercase; - font-weight: 100; - margin-top: 2.25px; - outline: none; -} - .List-actionsInner { display: flex; } diff --git a/awx/ui/client/src/partials/job-template-details.html b/awx/ui/client/src/partials/job-template-details.html index 26221941b0..c5a7410bf9 100644 --- a/awx/ui/client/src/partials/job-template-details.html +++ b/awx/ui/client/src/partials/job-template-details.html @@ -1,3 +1,4 @@ -
- INFO +
+ ?
diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js index da9ede3e06..2e5a0da0e9 100644 --- a/awx/ui/client/src/shared/generator-helpers.js +++ b/awx/ui/client/src/shared/generator-helpers.js @@ -696,6 +696,13 @@ angular.module('GeneratorHelpers', [systemStatus.name]) if (options.mode !== 'lookup' && field.badgeIcon && field.badgePlacement && field.badgePlacement !== 'left') { html += Badge(field); } + + // Field Tag + if (field.tag) { + html += ` + ${field.tag} + `; + } } } diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index 85e3d08f59..f468d2e193 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -409,9 +409,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p workflowMaker = { name: 'templates.editWorkflowJobTemplate.workflowMaker', url: '/workflow-maker', - // ncyBreadcrumb: { - // label: 'WORKFLOW MAKER' - // }, data: { formChildState: true }, @@ -468,14 +465,14 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p $scope[`${list.iterator}_dataset`] = Dataset.data; $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - $scope.$watch('job_templates', function(){ + $scope.$watch('templates', function(){ if($scope.selectedTemplate){ - $scope.job_templates.forEach(function(row, i) { + $scope.templates.forEach(function(row, i) { if(row.id === $scope.selectedTemplate.id) { - $scope.job_templates[i].checked = 1; + $scope.templates[i].checked = 1; } else { - $scope.job_templates[i].checked = 0; + $scope.templates[i].checked = 0; } }); } @@ -484,9 +481,9 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p $scope.toggle_row = function(selectedRow) { if ($scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) { - $scope.job_templates.forEach(function(row, i) { + $scope.templates.forEach(function(row, i) { if (row.id === selectedRow.id) { - $scope.job_templates[i].checked = 1; + $scope.templates[i].checked = 1; $scope.selection[list.iterator] = { id: row.id, name: row.name @@ -499,27 +496,27 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p }; $scope.$watch('selectedTemplate', () => { - $scope.job_templates.forEach(function(row, i) { - if(_.hasIn($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { - $scope.job_templates[i].checked = 1; + $scope.templates.forEach(function(row, i) { + if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { + $scope.templates[i].checked = 1; } else { - $scope.job_templates[i].checked = 0; + $scope.templates[i].checked = 0; } }); }); $scope.$watch('activeTab', () => { if(!$scope.activeTab || $scope.activeTab !== "jobs") { - $scope.job_templates.forEach(function(row, i) { - $scope.job_templates[i].checked = 0; + $scope.templates.forEach(function(row, i) { + $scope.templates[i].checked = 0; }); } }); $scope.$on('clearWorkflowLists', function() { - $scope.job_templates.forEach(function(row, i) { - $scope.job_templates[i].checked = 0; + $scope.templates.forEach(function(row, i) { + $scope.templates[i].checked = 0; }); }); } @@ -699,8 +696,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p return qs.search(path, $stateParams[`${list.iterator}_search`]); } ], - WorkflowMakerJobTemplateList: ['TemplateList', - (TemplateList) => { + WorkflowMakerJobTemplateList: ['TemplateList', 'i18n', + (TemplateList, i18n) => { let list = _.cloneDeep(TemplateList); delete list.actions; delete list.fields.type; @@ -709,15 +706,18 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p delete list.fields.labels; delete list.fieldActions; list.fields.name.columnClass = "col-md-8"; + list.fields.name.tag = i18n._('WORKFLOW'); + list.fields.name.showTag = "{{template.type === 'workflow_job_template'}}"; list.disableRow = "{{ !workflowJobTemplateObj.summary_fields.user_capabilities.edit }}"; list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit'; - list.iterator = 'job_template'; - list.name = 'job_templates'; + list.iterator = 'template'; + list.name = 'templates'; list.basePath = 'unified_job_templates'; list.fields.info = { ngInclude: "'/static/partials/job-template-details.html'", type: 'template', columnClass: 'col-md-3', + infoHeaderClass: 'col-md-3', label: '', nosort: true }; diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js index 8880c86321..174e3c3f11 100644 --- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js +++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js @@ -385,8 +385,13 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G .attr("r", 10) .attr("class", "WorkflowChart-nodeTypeCircle") .style("display", function (d) { - return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; - }); + return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || + d.unifiedJobTemplate.unified_job_type === "project_update" || + d.unifiedJobTemplate.type === "inventory_source" || + d.unifiedJobTemplate.unified_job_type === "inventory_update" || + d.unifiedJobTemplate.type === "workflow_job_template" || + d.unifiedJobTemplate.unified_job_type === "workflow_job") ? null : "none"; + }); thisNode.append("text") .attr("y", nodeH) @@ -394,10 +399,42 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G .attr("text-anchor", "middle") .attr("class", "WorkflowChart-nodeTypeLetter") .text(function (d) { - return (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update")) ? "P" : (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? "I" : ""); + let nodeTypeLetter = ""; + if (d.unifiedJobTemplate && d.unifiedJobTemplate.type) { + switch (d.unifiedJobTemplate.type) { + case "project": + nodeTypeLetter = "P"; + break; + case "inventory_source": + nodeTypeLetter = "I"; + break; + case "workflow_job_template": + nodeTypeLetter = "W"; + break; + } + } else if (d.unifiedJobTemplate && d.unifiedJobTemplate.unified_job_type) { + switch (d.unifiedJobTemplate.unified_job_type) { + case "project_update": + nodeTypeLetter = "P"; + break; + case "inventory_update": + nodeTypeLetter = "I"; + break; + case "workflow_job": + nodeTypeLetter = "W"; + break; + } + } + return nodeTypeLetter; }) .style("display", function (d) { - return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; + return d.unifiedJobTemplate && + (d.unifiedJobTemplate.type === "project" || + d.unifiedJobTemplate.unified_job_type === "project_update" || + d.unifiedJobTemplate.type === "inventory_source" || + d.unifiedJobTemplate.unified_job_type === "inventory_update" || + d.unifiedJobTemplate.type === "workflow_job_template" || + d.unifiedJobTemplate.unified_job_type === "workflow_job") ? null : "none"; }); thisNode.append("rect") @@ -828,15 +865,52 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G t.selectAll(".WorkflowChart-nodeTypeCircle") .style("display", function (d) { - return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; + return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || + d.unifiedJobTemplate.unified_job_type === "project_update" || + d.unifiedJobTemplate.type === "inventory_source" || + d.unifiedJobTemplate.unified_job_type === "inventory_update" || + d.unifiedJobTemplate.type === "workflow_job_template" || + d.unifiedJobTemplate.unified_job_type === "workflow_job") ? null : "none"; }); t.selectAll(".WorkflowChart-nodeTypeLetter") .text(function (d) { - return (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update")) ? "P" : (d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? "I" : ""); + let nodeTypeLetter = ""; + if (d.unifiedJobTemplate && d.unifiedJobTemplate.type) { + switch (d.unifiedJobTemplate.type) { + case "project": + nodeTypeLetter = "P"; + break; + case "inventory_source": + nodeTypeLetter = "I"; + break; + case "workflow_job_template": + nodeTypeLetter = "W"; + break; + } + } else if (d.unifiedJobTemplate && d.unifiedJobTemplate.unified_job_type) { + switch (d.unifiedJobTemplate.unified_job_type) { + case "project_update": + nodeTypeLetter = "P"; + break; + case "inventory_update": + nodeTypeLetter = "I"; + break; + case "workflow_job": + nodeTypeLetter = "W"; + break; + } + } + return nodeTypeLetter; }) .style("display", function (d) { - return d.unifiedJobTemplate && (d.unifiedJobTemplate.type === "project" || d.unifiedJobTemplate.unified_job_type === "project_update" || d.unifiedJobTemplate.type === "inventory_source" || d.unifiedJobTemplate.unified_job_type === "inventory_update") ? null : "none"; + return d.unifiedJobTemplate && + (d.unifiedJobTemplate.type === "project" || + d.unifiedJobTemplate.unified_job_type === "project_update" || + d.unifiedJobTemplate.type === "inventory_source" || + d.unifiedJobTemplate.unified_job_type === "inventory_update" || + d.unifiedJobTemplate.type === "workflow_job_template" || + d.unifiedJobTemplate.unified_job_type === "workflow_job") ? null : "none"; }); t.selectAll(".WorkflowChart-nodeStatus") @@ -1011,6 +1085,10 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G id: d.job.id, type: 'project' }); + } else if (job_type === 'workflow_job') { + $state.go('workflowResults', { + id: d.job.id, + }); } }; diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less index a189cfae12..65f7a9c2a9 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less @@ -202,23 +202,17 @@ line-height: 20px; } .WorkflowLegend-details { + align-items: center; display: flex; height: 40px; line-height: 40px; - padding-left: 20px; margin-top:10px; border: 1px solid @d7grey; border-top-left-radius: 5px; border-top-right-radius: 5px; } -.WorkflowLegend-legendItem { - display: flex; -} -.WorkflowLegend-legendItem:not(:last-child) { - padding-right: 20px; -} .WorkflowLegend-details--left { - display: flex; + display: block; flex: 1 0 auto; } .WorkflowLegend-details--right { @@ -304,6 +298,7 @@ height: 3px; width: 20px; margin: 9px 5px 9px 0px; + outline: none; } .Key-heading { font-weight: 700; diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index f94c1f3422..0c8f4fbf1b 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -5,10 +5,10 @@ *************************************************/ export default ['$scope', 'WorkflowService', 'TemplatesService', - 'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel', + 'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel', 'WorkflowJobTemplateModel', 'Empty', 'PromptService', 'Rest', 'TemplatesStrings', '$timeout', function ($scope, WorkflowService, TemplatesService, - ProcessErrors, CreateSelect2, $q, JobTemplate, + ProcessErrors, CreateSelect2, $q, JobTemplate, WorkflowJobTemplate, Empty, PromptService, Rest, TemplatesStrings, $timeout) { let promptWatcher, surveyQuestionWatcher, credentialsWatcher; @@ -110,7 +110,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', // Check to see if the user has provided any prompt values that are different // from the defaults in the job template - if (params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) { + if ((params.node.unifiedJobTemplate.type === "job_template" || params.node.unifiedJobTemplate.type === "workflow_job_template") && params.node.promptData) { sendableNodeData = PromptService.bundlePromptDataForSaving({ promptData: params.node.promptData, dataToSave: sendableNodeData @@ -188,7 +188,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', params.node.isNew = false; continueRecursing(data.data.id); - }, function ({ data, config, status }) { + }, function ({ + data, + config, + status + }) { ProcessErrors($scope, data, status, null, { hdr: $scope.strings.get('error.HEADER'), msg: $scope.strings.get('error.CALL', { @@ -339,7 +343,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', let missingPromptValue = false; if ($scope.missingSurveyValue) { missingPromptValue = true; - } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + } else if ($scope.selectedTemplate.type === 'job_template' && (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id)) { missingPromptValue = true; } $scope.promptModalMissingReqFields = missingPromptValue; @@ -481,7 +485,8 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', $scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate; $scope.placeholderNode.edgeType = $scope.edgeType.value; - if ($scope.placeholderNode.unifiedJobTemplate.type === 'job_template') { + if ($scope.placeholderNode.unifiedJobTemplate.type === 'job_template' || + $scope.placeholderNode.unifiedJobTemplate.type === 'workflow_job_template') { $scope.placeholderNode.promptData = _.cloneDeep($scope.promptData); } $scope.placeholderNode.canEdit = true; @@ -498,8 +503,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) { $scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate; $scope.nodeBeingEdited.edgeType = $scope.edgeType.value; - - if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template') { + if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template' || $scope.nodeBeingEdited.unifiedJobTemplate.type === 'workflow_job_template') { $scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData); } @@ -591,9 +595,8 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', $scope.nodeBeingEdited.isActiveEdit = true; let finishConfiguringEdit = function () { - - let jobTemplate = new JobTemplate(); - + let templateType = $scope.nodeBeingEdited.unifiedJobTemplate.type; + let jobTemplate = templateType === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate(); if (!_.isEmpty($scope.nodeBeingEdited.promptData)) { $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData); const launchConf = $scope.promptData.launchConf; @@ -615,15 +618,17 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', } else { $scope.showPromptButton = true; - if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { + if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template' && launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { $scope.promptModalMissingReqFields = true; } else { $scope.promptModalMissingReqFields = false; } } } else if ( - _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job_template' || - _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'job_template' + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job' || + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'job_template' || + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'workflow_job' || + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'workflow_job_template' ) { let promises = [jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id)]; @@ -674,8 +679,12 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides); - if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) { - $scope.selectedTemplateInvalid = true; + if ($scope.nodeBeingEdited.unifiedJobTemplate.unified_job_template === 'job') { + if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) { + $scope.selectedTemplateInvalid = true; + } else { + $scope.selectedTemplateInvalid = false; + } } else { $scope.selectedTemplateInvalid = false; } @@ -774,7 +783,9 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', } if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) { - if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template") { + + if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template" || + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "workflow_job_template") { $scope.workflowMakerFormConfig.activeTab = "jobs"; } @@ -783,6 +794,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', if ($scope.selectedTemplate.unified_job_type) { switch ($scope.selectedTemplate.unified_job_type) { case "job": + case "workflow_job": $scope.workflowMakerFormConfig.activeTab = "jobs"; break; case "project_update": @@ -795,6 +807,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', } else if ($scope.selectedTemplate.type) { switch ($scope.selectedTemplate.type) { case "job_template": + case "workflow_job_template": $scope.workflowMakerFormConfig.activeTab = "jobs"; break; case "project": @@ -843,8 +856,9 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', // Determine whether or not we need to go out and GET this nodes unified job template // in order to determine whether or not prompt fields are needed - - if (!$scope.nodeBeingEdited.isNew && !$scope.nodeBeingEdited.edited && $scope.nodeBeingEdited.unifiedJobTemplate && $scope.nodeBeingEdited.unifiedJobTemplate.unified_job_type && $scope.nodeBeingEdited.unifiedJobTemplate.unified_job_type === 'job') { + if (!$scope.nodeBeingEdited.isNew && !$scope.nodeBeingEdited.edited && + (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job' || + _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'workflow_job')) { // This is a node that we got back from the api with an incomplete // unified job template so we're going to pull down the whole object @@ -852,15 +866,19 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', .then(function (data) { $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]); finishConfiguringEdit(); - }, function ({ data, status, config }) { - ProcessErrors($scope, data, status, null, { - hdr: $scope.strings.get('error.HEADER'), - msg: $scope.strings.get('error.CALL', { - path: `${config.url}`, - action: `${config.method}`, - status - }) - }); + }, function ({ + data, + status, + config + }) { + ProcessErrors($scope, data, status, null, { + hdr: $scope.strings.get('error.HEADER'), + msg: $scope.strings.get('error.CALL', { + path: `${config.url}`, + action: `${config.method}`, + status + }) + }); }); } else { finishConfiguringEdit(); @@ -982,24 +1000,24 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', } $scope.promptData = null; - - if (selectedTemplate.type === "job_template") { - let jobTemplate = new JobTemplate(); + if (selectedTemplate.type === "job_template" || selectedTemplate.type === "workflow_job_template") { + let jobTemplate = selectedTemplate.type === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate(); $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) .then((responses) => { let launchConf = responses[1].data; + if (selectedTemplate.type === 'job_template') { + if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) { + $scope.selectedTemplateInvalid = true; + } else { + $scope.selectedTemplateInvalid = false; + } - if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) { - $scope.selectedTemplateInvalid = true; - } else { - $scope.selectedTemplateInvalid = false; - } - - if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) { - $scope.credentialRequiresPassword = true; - } else { - $scope.credentialRequiresPassword = false; + if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) { + $scope.credentialRequiresPassword = true; + } else { + $scope.credentialRequiresPassword = false; + } } $scope.selectedTemplate = angular.copy(selectedTemplate); @@ -1021,8 +1039,12 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', } else { $scope.showPromptButton = true; - if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { - $scope.promptModalMissingReqFields = true; + if (selectedTemplate.type === 'job_template') { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { + $scope.promptModalMissingReqFields = true; + } else { + $scope.promptModalMissingReqFields = false; + } } else { $scope.promptModalMissingReqFields = false; } @@ -1156,7 +1178,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService', // This is the last page buildTreeFromNodes(); } - }, function ({ data, status, config }) { + }, function ({ + data, + status, + config + }) { ProcessErrors($scope, data, status, null, { hdr: $scope.strings.get('error.HEADER'), msg: $scope.strings.get('error.CALL', { diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html index 3f3fb5c14f..e92cc0e9bf 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html @@ -62,6 +62,10 @@
I

{{strings.get('workflow_maker.INVENTORY_SYNC')}}

+
  • +
    W
    +

    {{strings.get('workflow_maker.WORKFLOW')}}

    +
  • !

    {{strings.get('workflow_maker.WARNING')}}

    @@ -95,7 +99,9 @@
  • diff --git a/awx/ui/client/src/workflow-results/workflow-results.block.less b/awx/ui/client/src/workflow-results/workflow-results.block.less index d2af14d1f0..4e1bd89088 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.block.less +++ b/awx/ui/client/src/workflow-results/workflow-results.block.less @@ -141,3 +141,13 @@ .WorkflowResults-extraVarsLabel { font-size:14px!important; } + +.WorkflowResults-seeMoreLess { + color: #337AB7; + margin: 4px 0px; + text-transform: uppercase; + padding: 2px 0px; + cursor: pointer; + border-radius: 5px; + font-size: 11px; +} diff --git a/awx/ui/client/src/workflow-results/workflow-results.controller.js b/awx/ui/client/src/workflow-results/workflow-results.controller.js index fe9d42ebcb..ec4e804714 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.controller.js +++ b/awx/ui/client/src/workflow-results/workflow-results.controller.js @@ -1,9 +1,9 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', 'jobLabels', 'workflowNodes', '$scope', 'ParseTypeChange', 'ParseVariableString', 'WorkflowService', 'count', '$state', 'i18n', - 'moment', function(workflowData, workflowResultsService, + 'moment', '$filter', function(workflowData, workflowResultsService, workflowDataOptions, jobLabels, workflowNodes, $scope, ParseTypeChange, - ParseVariableString, WorkflowService, count, $state, i18n, moment) { + ParseVariableString, WorkflowService, count, $state, i18n, moment, $filter) { var runTimeElapsedTimer = null; var getLinks = function() { @@ -50,13 +50,17 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', STARTED: i18n._('Started'), FINISHED: i18n._('Finished'), LABELS: i18n._('Labels'), + STATUS: i18n._('Status'), SLICE_TEMPLATE: i18n._('Slice Job Template'), - STATUS: i18n._('Status') + JOB_EXPLANATION: i18n._('Explanation'), + SOURCE_WORKFLOW_JOB: i18n._('Parent Workflow') }, details: { HEADER: i18n._('DETAILS'), NOT_FINISHED: i18n._('Not Finished'), NOT_STARTED: i18n._('Not Started'), + SHOW_LESS: i18n._('Show Less'), + SHOW_MORE: i18n._('Show More'), }, results: { TOTAL_JOBS: i18n._('Total Jobs'), @@ -68,6 +72,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', ALWAYS: i18n._('Always'), PROJECT_SYNC: i18n._('Project Sync'), INVENTORY_SYNC: i18n._('Inventory Sync'), + WORKFLOW: i18n._('Workflow'), KEY: i18n._('KEY'), } }; @@ -100,6 +105,9 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', $scope.labels = jobLabels; $scope.count = count.val; $scope.showManualControls = false; + $scope.showKey = false; + $scope.toggleKey = () => $scope.showKey = !$scope.showKey; + $scope.keyClassList = `{ 'Key-menuIcon--active': showKey }`; // Start elapsed time updater for job known to be running if ($scope.workflow.started !== null && $scope.workflow.status === 'running') { @@ -116,6 +124,27 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', $scope.slice_job_template_link = `/#/templates/job_template/${$scope.workflow.summary_fields.job_template.id}`; } + if (_.get(workflowData, 'summary_fields.source_workflow_job.id')) { + $scope.source_workflow_job_link = `/#/workflows/${workflowData.summary_fields.source_workflow_job.id}`; + } + + if (workflowData.job_explanation) { + const limit = 150; + const more = workflowData.job_explanation; + const less = $filter('limitTo')(more, limit); + const showMore = false; + const hasMoreToShow = more.length > limit; + + const job_explanation = { + more: more, + less: less, + showMore: showMore, + hasMoreToShow: hasMoreToShow + }; + + $scope.job_explanation = job_explanation; + } + // turn related api browser routes into front end routes getLinks(); diff --git a/awx/ui/client/src/workflow-results/workflow-results.partial.html b/awx/ui/client/src/workflow-results/workflow-results.partial.html index 474fac1d6e..b17a277074 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.partial.html +++ b/awx/ui/client/src/workflow-results/workflow-results.partial.html @@ -75,6 +75,33 @@
    + +
    + + +
    + {{ job_explanation.less }} + ... + + {{ strings.details.SHOW_MORE }} + +
    + +
    + {{ job_explanation.more }} + + {{ strings.details.SHOW_LESS }} + +
    +
    +
    @@ -144,18 +171,31 @@
    - + +
    + + +
    + +
    -
    @@ -268,27 +308,36 @@
    -
    {{ strings.legend.KEY }}:
    -
    -
    -
    {{ strings.legend.ON_SUCCESS }}
    -
    -
    -
    -
    {{ strings.legend.ON_FAIL }}
    -
    -
    -
    -
    {{ strings.legend.ALWAYS }}
    -
    -
    -
    P
    -
    {{ strings.legend.PROJECT_SYNC }}
    -
    -
    -
    I
    -
    {{ strings.legend.INVENTORY_SYNC }}
    -
    + +
      +
    • +

      {{strings.legend.KEY}}

      +
    • +
    • +
      +

      {{strings.legend.ON_SUCCESS}}

      +
    • +
    • +
      +

      {{strings.legend.ON_FAILURE}}

      +
    • +
    • +
      +

      {{strings.legend.ALWAYS}}

      +
    • +
    • +
      P
      +

      {{strings.legend.PROJECT_SYNC}}

      +
    • +
    • +
      I
      +

      {{strings.legend.INVENTORY_SYNC}}

      +
    • +
    • +
      W
      +

      {{strings.legend.WORKFLOW}}

      +
    • +