1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 23:51:09 +03:00

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
This commit is contained in:
Marliana Lara 2018-10-06 19:41:12 -04:00
parent f6cc351f7f
commit e20d8c8e81
No known key found for this signature in database
GPG Key ID: 38C73B40DFA809EE
12 changed files with 317 additions and 130 deletions

View File

@ -102,6 +102,7 @@ function TemplatesStrings (BaseString) {
ALWAYS: t.s('Always'), ALWAYS: t.s('Always'),
PROJECT_SYNC: t.s('Project Sync'), PROJECT_SYNC: t.s('Project Sync'),
INVENTORY_SYNC: t.s('Inventory Sync'), INVENTORY_SYNC: t.s('Inventory Sync'),
WORKFLOW: t.s('Workflow'),
WARNING: t.s('Warning'), WARNING: t.s('Warning'),
TOTAL_TEMPLATES: t.s('TOTAL TEMPLATES'), TOTAL_TEMPLATES: t.s('TOTAL TEMPLATES'),
ADD_A_TEMPLATE: t.s('ADD A TEMPLATE'), ADD_A_TEMPLATE: t.s('ADD A TEMPLATE'),

View File

@ -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 { .List-actionsInner {
display: flex; display: flex;
} }

View File

@ -1,3 +1,4 @@
<div class="List-infoCell"> <div class="List-infoCell" ng-if="job_template.type === 'job_template'">
<span class="List-infoCell--badge" aw-pop-over="<dl><dt>{{ 'INVENTORY' | translate }}</dt><dd>{{(job_template.summary_fields.inventory.name | sanitize) || ('NONE SELECTED' | translate)}}</dd></dl><dl><dt>{{ 'PROJECT' | translate }}</dt><dd>{{job_template.summary_fields.project.name | sanitize}}</dd></dl><dl><dt>{{ 'PLAYBOOK' | translate }}</dt><dd>{{job_template.playbook| sanitize}}</dd></dl><dl><dt>{{ 'CREDENTIAL' | translate }}</dt> <dd>{{(job_template.summary_fields.credential.name | sanitize) || ('NONE SELECTED' | translate)}}</dd></dl>" data-popover-title="{{job_template.name| sanitize}}" translate>INFO</span> <span class="Key-icon Key-icon--circle Key-icon--default" aw-pop-over="<dl><dt>{{ 'INVENTORY' | translate }}</dt><dd>{{(job_template.summary_fields.inventory.name | sanitize) || ('NONE SELECTED' | translate)}}</dd></dl><dl><dt>{{ 'PROJECT' | translate }}</dt><dd>{{job_template.summary_fields.project.name | sanitize}}</dd></dl><dl><dt>{{ 'PLAYBOOK' | translate }}</dt><dd>{{job_template.playbook| sanitize}}</dd></dl><dl><dt>{{ 'CREDENTIAL' | translate }}</dt> <dd>{{(job_template.summary_fields.credential.name | sanitize) || ('NONE SELECTED' | translate)}}</dd></dl>"
data-popover-title="{{job_template.name| sanitize}}">?</span>
</div> </div>

View File

@ -696,6 +696,13 @@ angular.module('GeneratorHelpers', [systemStatus.name])
if (options.mode !== 'lookup' && field.badgeIcon && field.badgePlacement && field.badgePlacement !== 'left') { if (options.mode !== 'lookup' && field.badgeIcon && field.badgePlacement && field.badgePlacement !== 'left') {
html += Badge(field); html += Badge(field);
} }
// Field Tag
if (field.tag) {
html += `<span class="at-RowItem-tag" ng-show="${field.showTag}">
${field.tag}
</span>`;
}
} }
} }

View File

@ -409,9 +409,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
workflowMaker = { workflowMaker = {
name: 'templates.editWorkflowJobTemplate.workflowMaker', name: 'templates.editWorkflowJobTemplate.workflowMaker',
url: '/workflow-maker', url: '/workflow-maker',
// ncyBreadcrumb: {
// label: 'WORKFLOW MAKER'
// },
data: { data: {
formChildState: true formChildState: true
}, },
@ -468,14 +465,14 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
$scope[`${list.iterator}_dataset`] = Dataset.data; $scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results; $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$scope.$watch('job_templates', function(){ $scope.$watch('templates', function(){
if($scope.selectedTemplate){ if($scope.selectedTemplate){
$scope.job_templates.forEach(function(row, i) { $scope.templates.forEach(function(row, i) {
if(row.id === $scope.selectedTemplate.id) { if(row.id === $scope.selectedTemplate.id) {
$scope.job_templates[i].checked = 1; $scope.templates[i].checked = 1;
} }
else { 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) { $scope.toggle_row = function(selectedRow) {
if ($scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) { 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) { if (row.id === selectedRow.id) {
$scope.job_templates[i].checked = 1; $scope.templates[i].checked = 1;
$scope.selection[list.iterator] = { $scope.selection[list.iterator] = {
id: row.id, id: row.id,
name: row.name name: row.name
@ -499,27 +496,27 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
}; };
$scope.$watch('selectedTemplate', () => { $scope.$watch('selectedTemplate', () => {
$scope.job_templates.forEach(function(row, i) { $scope.templates.forEach(function(row, i) {
if(_.hasIn($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) {
$scope.job_templates[i].checked = 1; $scope.templates[i].checked = 1;
} }
else { else {
$scope.job_templates[i].checked = 0; $scope.templates[i].checked = 0;
} }
}); });
}); });
$scope.$watch('activeTab', () => { $scope.$watch('activeTab', () => {
if(!$scope.activeTab || $scope.activeTab !== "jobs") { if(!$scope.activeTab || $scope.activeTab !== "jobs") {
$scope.job_templates.forEach(function(row, i) { $scope.templates.forEach(function(row, i) {
$scope.job_templates[i].checked = 0; $scope.templates[i].checked = 0;
}); });
} }
}); });
$scope.$on('clearWorkflowLists', function() { $scope.$on('clearWorkflowLists', function() {
$scope.job_templates.forEach(function(row, i) { $scope.templates.forEach(function(row, i) {
$scope.job_templates[i].checked = 0; $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`]); return qs.search(path, $stateParams[`${list.iterator}_search`]);
} }
], ],
WorkflowMakerJobTemplateList: ['TemplateList', WorkflowMakerJobTemplateList: ['TemplateList', 'i18n',
(TemplateList) => { (TemplateList, i18n) => {
let list = _.cloneDeep(TemplateList); let list = _.cloneDeep(TemplateList);
delete list.actions; delete list.actions;
delete list.fields.type; delete list.fields.type;
@ -709,15 +706,18 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
delete list.fields.labels; delete list.fields.labels;
delete list.fieldActions; delete list.fieldActions;
list.fields.name.columnClass = "col-md-8"; 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.disableRow = "{{ !workflowJobTemplateObj.summary_fields.user_capabilities.edit }}";
list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit'; list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit';
list.iterator = 'job_template'; list.iterator = 'template';
list.name = 'job_templates'; list.name = 'templates';
list.basePath = 'unified_job_templates'; list.basePath = 'unified_job_templates';
list.fields.info = { list.fields.info = {
ngInclude: "'/static/partials/job-template-details.html'", ngInclude: "'/static/partials/job-template-details.html'",
type: 'template', type: 'template',
columnClass: 'col-md-3', columnClass: 'col-md-3',
infoHeaderClass: 'col-md-3',
label: '', label: '',
nosort: true nosort: true
}; };

View File

@ -385,8 +385,13 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G
.attr("r", 10) .attr("r", 10)
.attr("class", "WorkflowChart-nodeTypeCircle") .attr("class", "WorkflowChart-nodeTypeCircle")
.style("display", function (d) { .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") thisNode.append("text")
.attr("y", nodeH) .attr("y", nodeH)
@ -394,10 +399,42 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("class", "WorkflowChart-nodeTypeLetter") .attr("class", "WorkflowChart-nodeTypeLetter")
.text(function (d) { .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) { .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") thisNode.append("rect")
@ -828,15 +865,52 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G
t.selectAll(".WorkflowChart-nodeTypeCircle") t.selectAll(".WorkflowChart-nodeTypeCircle")
.style("display", function (d) { .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") t.selectAll(".WorkflowChart-nodeTypeLetter")
.text(function (d) { .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) { .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") t.selectAll(".WorkflowChart-nodeStatus")
@ -1011,6 +1085,10 @@ export default ['$state', 'moment', '$timeout', '$window', '$filter', 'Rest', 'G
id: d.job.id, id: d.job.id,
type: 'project' type: 'project'
}); });
} else if (job_type === 'workflow_job') {
$state.go('workflowResults', {
id: d.job.id,
});
} }
}; };

View File

@ -202,23 +202,17 @@
line-height: 20px; line-height: 20px;
} }
.WorkflowLegend-details { .WorkflowLegend-details {
align-items: center;
display: flex; display: flex;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
padding-left: 20px;
margin-top:10px; margin-top:10px;
border: 1px solid @d7grey; border: 1px solid @d7grey;
border-top-left-radius: 5px; border-top-left-radius: 5px;
border-top-right-radius: 5px; border-top-right-radius: 5px;
} }
.WorkflowLegend-legendItem {
display: flex;
}
.WorkflowLegend-legendItem:not(:last-child) {
padding-right: 20px;
}
.WorkflowLegend-details--left { .WorkflowLegend-details--left {
display: flex; display: block;
flex: 1 0 auto; flex: 1 0 auto;
} }
.WorkflowLegend-details--right { .WorkflowLegend-details--right {
@ -304,6 +298,7 @@
height: 3px; height: 3px;
width: 20px; width: 20px;
margin: 9px 5px 9px 0px; margin: 9px 5px 9px 0px;
outline: none;
} }
.Key-heading { .Key-heading {
font-weight: 700; font-weight: 700;

View File

@ -5,10 +5,10 @@
*************************************************/ *************************************************/
export default ['$scope', 'WorkflowService', 'TemplatesService', export default ['$scope', 'WorkflowService', 'TemplatesService',
'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel', 'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel', 'WorkflowJobTemplateModel',
'Empty', 'PromptService', 'Rest', 'TemplatesStrings', '$timeout', 'Empty', 'PromptService', 'Rest', 'TemplatesStrings', '$timeout',
function ($scope, WorkflowService, TemplatesService, function ($scope, WorkflowService, TemplatesService,
ProcessErrors, CreateSelect2, $q, JobTemplate, ProcessErrors, CreateSelect2, $q, JobTemplate, WorkflowJobTemplate,
Empty, PromptService, Rest, TemplatesStrings, $timeout) { Empty, PromptService, Rest, TemplatesStrings, $timeout) {
let promptWatcher, surveyQuestionWatcher, credentialsWatcher; 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 // Check to see if the user has provided any prompt values that are different
// from the defaults in the job template // 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({ sendableNodeData = PromptService.bundlePromptDataForSaving({
promptData: params.node.promptData, promptData: params.node.promptData,
dataToSave: sendableNodeData dataToSave: sendableNodeData
@ -188,7 +188,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
params.node.isNew = false; params.node.isNew = false;
continueRecursing(data.data.id); continueRecursing(data.data.id);
}, function ({ data, config, status }) { }, function ({
data,
config,
status
}) {
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {
hdr: $scope.strings.get('error.HEADER'), hdr: $scope.strings.get('error.HEADER'),
msg: $scope.strings.get('error.CALL', { msg: $scope.strings.get('error.CALL', {
@ -339,7 +343,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
let missingPromptValue = false; let missingPromptValue = false;
if ($scope.missingSurveyValue) { if ($scope.missingSurveyValue) {
missingPromptValue = true; 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; missingPromptValue = true;
} }
$scope.promptModalMissingReqFields = missingPromptValue; $scope.promptModalMissingReqFields = missingPromptValue;
@ -481,7 +485,8 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate; $scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate;
$scope.placeholderNode.edgeType = $scope.edgeType.value; $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.promptData = _.cloneDeep($scope.promptData);
} }
$scope.placeholderNode.canEdit = true; $scope.placeholderNode.canEdit = true;
@ -498,8 +503,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) { if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) {
$scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate; $scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate;
$scope.nodeBeingEdited.edgeType = $scope.edgeType.value; $scope.nodeBeingEdited.edgeType = $scope.edgeType.value;
if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template' || $scope.nodeBeingEdited.unifiedJobTemplate.type === 'workflow_job_template') {
if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template') {
$scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData); $scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData);
} }
@ -591,9 +595,8 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.nodeBeingEdited.isActiveEdit = true; $scope.nodeBeingEdited.isActiveEdit = true;
let finishConfiguringEdit = function () { let finishConfiguringEdit = function () {
let templateType = $scope.nodeBeingEdited.unifiedJobTemplate.type;
let jobTemplate = new JobTemplate(); let jobTemplate = templateType === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate();
if (!_.isEmpty($scope.nodeBeingEdited.promptData)) { if (!_.isEmpty($scope.nodeBeingEdited.promptData)) {
$scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData); $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData);
const launchConf = $scope.promptData.launchConf; const launchConf = $scope.promptData.launchConf;
@ -615,15 +618,17 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} else { } else {
$scope.showPromptButton = true; $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; $scope.promptModalMissingReqFields = true;
} else { } else {
$scope.promptModalMissingReqFields = false; $scope.promptModalMissingReqFields = false;
} }
} }
} else if ( } else if (
_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job_template' || _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job' ||
_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'job_template' _.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)]; 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); prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) { if ($scope.nodeBeingEdited.unifiedJobTemplate.unified_job_template === 'job') {
$scope.selectedTemplateInvalid = true; if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) {
$scope.selectedTemplateInvalid = true;
} else {
$scope.selectedTemplateInvalid = false;
}
} else { } else {
$scope.selectedTemplateInvalid = false; $scope.selectedTemplateInvalid = false;
} }
@ -774,7 +783,9 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} }
if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) { 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"; $scope.workflowMakerFormConfig.activeTab = "jobs";
} }
@ -783,6 +794,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
if ($scope.selectedTemplate.unified_job_type) { if ($scope.selectedTemplate.unified_job_type) {
switch ($scope.selectedTemplate.unified_job_type) { switch ($scope.selectedTemplate.unified_job_type) {
case "job": case "job":
case "workflow_job":
$scope.workflowMakerFormConfig.activeTab = "jobs"; $scope.workflowMakerFormConfig.activeTab = "jobs";
break; break;
case "project_update": case "project_update":
@ -795,6 +807,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} else if ($scope.selectedTemplate.type) { } else if ($scope.selectedTemplate.type) {
switch ($scope.selectedTemplate.type) { switch ($scope.selectedTemplate.type) {
case "job_template": case "job_template":
case "workflow_job_template":
$scope.workflowMakerFormConfig.activeTab = "jobs"; $scope.workflowMakerFormConfig.activeTab = "jobs";
break; break;
case "project": 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 // 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 // in order to determine whether or not prompt fields are needed
if (!$scope.nodeBeingEdited.isNew && !$scope.nodeBeingEdited.edited &&
if (!$scope.nodeBeingEdited.isNew && !$scope.nodeBeingEdited.edited && $scope.nodeBeingEdited.unifiedJobTemplate && $scope.nodeBeingEdited.unifiedJobTemplate.unified_job_type && $scope.nodeBeingEdited.unifiedJobTemplate.unified_job_type === 'job') { (_.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 // 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 // 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) { .then(function (data) {
$scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]); $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
finishConfiguringEdit(); finishConfiguringEdit();
}, function ({ data, status, config }) { }, function ({
ProcessErrors($scope, data, status, null, { data,
hdr: $scope.strings.get('error.HEADER'), status,
msg: $scope.strings.get('error.CALL', { config
path: `${config.url}`, }) {
action: `${config.method}`, ProcessErrors($scope, data, status, null, {
status hdr: $scope.strings.get('error.HEADER'),
}) msg: $scope.strings.get('error.CALL', {
}); path: `${config.url}`,
action: `${config.method}`,
status
})
});
}); });
} else { } else {
finishConfiguringEdit(); finishConfiguringEdit();
@ -982,24 +1000,24 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} }
$scope.promptData = null; $scope.promptData = null;
if (selectedTemplate.type === "job_template" || selectedTemplate.type === "workflow_job_template") {
if (selectedTemplate.type === "job_template") { let jobTemplate = selectedTemplate.type === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate();
let jobTemplate = new JobTemplate();
$q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)])
.then((responses) => { .then((responses) => {
let launchConf = responses[1].data; 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) { if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) {
$scope.selectedTemplateInvalid = true; $scope.credentialRequiresPassword = true;
} else { } else {
$scope.selectedTemplateInvalid = false; $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); $scope.selectedTemplate = angular.copy(selectedTemplate);
@ -1021,8 +1039,12 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} else { } else {
$scope.showPromptButton = true; $scope.showPromptButton = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { if (selectedTemplate.type === 'job_template') {
$scope.promptModalMissingReqFields = true; if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
$scope.promptModalMissingReqFields = true;
} else {
$scope.promptModalMissingReqFields = false;
}
} else { } else {
$scope.promptModalMissingReqFields = false; $scope.promptModalMissingReqFields = false;
} }
@ -1156,7 +1178,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
// This is the last page // This is the last page
buildTreeFromNodes(); buildTreeFromNodes();
} }
}, function ({ data, status, config }) { }, function ({
data,
status,
config
}) {
ProcessErrors($scope, data, status, null, { ProcessErrors($scope, data, status, null, {
hdr: $scope.strings.get('error.HEADER'), hdr: $scope.strings.get('error.HEADER'),
msg: $scope.strings.get('error.CALL', { msg: $scope.strings.get('error.CALL', {

View File

@ -62,6 +62,10 @@
<div class="Key-icon Key-icon--circle Key-icon--default">I</div> <div class="Key-icon Key-icon--circle Key-icon--default">I</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</p> <p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</p>
</li> </li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--circle Key-icon--default">W</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.WORKFLOW')}}</p>
</li>
<li class="Key-listItem"> <li class="Key-listItem">
<div class="Key-icon Key-icon--circle Key-icon--warning">!</div> <div class="Key-icon Key-icon--circle Key-icon--warning">!</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.WARNING')}}</p> <p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.WARNING')}}</p>
@ -95,7 +99,9 @@
</div> </div>
<span ng-show="selectedTemplate && <span ng-show="selectedTemplate &&
((selectedTemplate.type === 'job_template' || selectedTemplate.type === 'workflow_job_template' && workflowMakerFormConfig.activeTab === 'jobs') || ((selectedTemplate.type === 'job_template' || selectedTemplate.type === 'workflow_job_template' && workflowMakerFormConfig.activeTab === 'jobs') ||
(selectedTemplate.unified_job_type === 'job' || selectedTemplate.unified_job_type === 'workflow_job' && workflowMakerFormConfig.activeTab === 'jobs') ||
(selectedTemplate.type === 'project' && workflowMakerFormConfig.activeTab === 'project_sync') || (selectedTemplate.type === 'project' && workflowMakerFormConfig.activeTab === 'project_sync') ||
(selectedTemplate.unified_job_type === 'inventory_update' && workflowMakerFormConfig.activeTab === 'inventory_sync') ||
(selectedTemplate.type === 'inventory_source' && workflowMakerFormConfig.activeTab === 'inventory_sync'))"> (selectedTemplate.type === 'inventory_source' && workflowMakerFormConfig.activeTab === 'inventory_sync'))">
<div ng-if="selectedTemplate && selectedTemplateInvalid"> <div ng-if="selectedTemplate && selectedTemplateInvalid">
<div class="WorkflowMaker-invalidJobTemplateWarning"> <div class="WorkflowMaker-invalidJobTemplateWarning">

View File

@ -141,3 +141,13 @@
.WorkflowResults-extraVarsLabel { .WorkflowResults-extraVarsLabel {
font-size:14px!important; 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;
}

View File

@ -1,9 +1,9 @@
export default ['workflowData', 'workflowResultsService', 'workflowDataOptions', export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
'jobLabels', 'workflowNodes', '$scope', 'ParseTypeChange', 'jobLabels', 'workflowNodes', '$scope', 'ParseTypeChange',
'ParseVariableString', 'WorkflowService', 'count', '$state', 'i18n', 'ParseVariableString', 'WorkflowService', 'count', '$state', 'i18n',
'moment', function(workflowData, workflowResultsService, 'moment', '$filter', function(workflowData, workflowResultsService,
workflowDataOptions, jobLabels, workflowNodes, $scope, ParseTypeChange, workflowDataOptions, jobLabels, workflowNodes, $scope, ParseTypeChange,
ParseVariableString, WorkflowService, count, $state, i18n, moment) { ParseVariableString, WorkflowService, count, $state, i18n, moment, $filter) {
var runTimeElapsedTimer = null; var runTimeElapsedTimer = null;
var getLinks = function() { var getLinks = function() {
@ -50,13 +50,17 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
STARTED: i18n._('Started'), STARTED: i18n._('Started'),
FINISHED: i18n._('Finished'), FINISHED: i18n._('Finished'),
LABELS: i18n._('Labels'), LABELS: i18n._('Labels'),
STATUS: i18n._('Status'),
SLICE_TEMPLATE: i18n._('Slice Job Template'), SLICE_TEMPLATE: i18n._('Slice Job Template'),
STATUS: i18n._('Status') JOB_EXPLANATION: i18n._('Explanation'),
SOURCE_WORKFLOW_JOB: i18n._('Parent Workflow')
}, },
details: { details: {
HEADER: i18n._('DETAILS'), HEADER: i18n._('DETAILS'),
NOT_FINISHED: i18n._('Not Finished'), NOT_FINISHED: i18n._('Not Finished'),
NOT_STARTED: i18n._('Not Started'), NOT_STARTED: i18n._('Not Started'),
SHOW_LESS: i18n._('Show Less'),
SHOW_MORE: i18n._('Show More'),
}, },
results: { results: {
TOTAL_JOBS: i18n._('Total Jobs'), TOTAL_JOBS: i18n._('Total Jobs'),
@ -68,6 +72,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
ALWAYS: i18n._('Always'), ALWAYS: i18n._('Always'),
PROJECT_SYNC: i18n._('Project Sync'), PROJECT_SYNC: i18n._('Project Sync'),
INVENTORY_SYNC: i18n._('Inventory Sync'), INVENTORY_SYNC: i18n._('Inventory Sync'),
WORKFLOW: i18n._('Workflow'),
KEY: i18n._('KEY'), KEY: i18n._('KEY'),
} }
}; };
@ -100,6 +105,9 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
$scope.labels = jobLabels; $scope.labels = jobLabels;
$scope.count = count.val; $scope.count = count.val;
$scope.showManualControls = false; $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 // Start elapsed time updater for job known to be running
if ($scope.workflow.started !== null && $scope.workflow.status === '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}`; $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 // turn related api browser routes into front end routes
getLinks(); getLinks();

View File

@ -75,6 +75,33 @@
</div> </div>
</div> </div>
<!-- EXPLANATION DETAIL -->
<div class="WorkflowResults-resultRow" ng-show="workflow.job_explanation">
<label class="WorkflowResults-resultRowLabel">
{{ strings.labels.JOB_EXPLANATION }}
</label>
<div class="WorkflowResults-resultRowText"
ng-show="!job_explanation.showMore">
{{ job_explanation.less }}
<span ng-show="job_explanation.hasMoreToShow">...</span>
<span ng-show="job_explanation.hasMoreToShow"
class="WorkflowResults-seeMoreLess"
ng-click="job_explanation.showMore = true">
{{ strings.details.SHOW_MORE }}
</span>
</div>
<div class="WorkflowResults-resultRowText"
ng-show="job_explanation.showMore">
{{ job_explanation.more }}
<span class="WorkflowResults-seeMoreLess"
ng-click="job_explanation.showMore = false">
{{ strings.details.SHOW_LESS }}
</span>
</div>
</div>
<!-- START TIME DETAIL --> <!-- START TIME DETAIL -->
<div class="WorkflowResults-resultRow" <div class="WorkflowResults-resultRow"
ng-show="workflow.started"> ng-show="workflow.started">
@ -144,18 +171,31 @@
</div> </div>
</div> </div>
<!-- SLIIIIIICE --> <!-- SLIIIIIICE -->
<div class="WorkflowResults-resultRow"
ng-show="workflow.summary_fields.job_template.name">
<label
class="WorkflowResults-resultRowLabel">
{{ strings.labels.SLICE_TEMPLATE }}
</label>
<div class="WorkflowResults-resultRowText">
<a href="{{ slice_job_template_link }}"
aw-tool-tip="{{ strings.tooltips.EDIT_SLICE_TEMPLATE }}"
data-placement="top">
{{ workflow.summary_fields.job_template.name }}
</a>
</div>
</div>
<!-- PARENT WORKFLOW DETAIL -->
<div class="WorkflowResults-resultRow" <div class="WorkflowResults-resultRow"
ng-show="workflow.summary_fields.job_template.name"> ng-if="workflow.summary_fields.source_workflow_job">
<label <label class="WorkflowResults-resultRowLabel">
class="WorkflowResults-resultRowLabel"> {{ strings.labels.SOURCE_WORKFLOW_JOB }}
{{ strings.labels.SLICE_TEMPLATE }}
</label> </label>
<div class="WorkflowResults-resultRowText"> <div class="WorkflowResults-resultRowText">
<a href="{{ slice_job_template_link }}" <a href="{{ source_workflow_job_link }}">
aw-tool-tip="{{ strings.tooltips.EDIT_SLICE_TEMPLATE }}" {{ workflow.summary_fields.source_workflow_job.name }}
data-placement="top">
{{ workflow.summary_fields.job_template.name }}
</a> </a>
</div> </div>
</div> </div>
@ -268,27 +308,36 @@
<workflow-status-bar></workflow-status-bar> <workflow-status-bar></workflow-status-bar>
<div class="WorkflowLegend-details"> <div class="WorkflowLegend-details">
<div class="WorkflowLegend-details--left"> <div class="WorkflowLegend-details--left">
<div class="WorkflowLegend-legendItem">{{ strings.legend.KEY }}:</div> <i ng-class="{{ keyClassList }}" class="fa fa-key Key-menuIcon" ng-click="toggleKey()"></i>
<div class="WorkflowLegend-legendItem"> <ul ng-show="showKey" class="Key-list noselect">
<div class="WorkflowLegend-onSuccessLegend"></div> <li class="Key-listItem">
<div>{{ strings.legend.ON_SUCCESS }}</div> <p class="Key-heading">{{strings.legend.KEY}}</p>
</div> </li>
<div class="WorkflowLegend-legendItem"> <li class="Key-listItem">
<div class="WorkflowLegend-onFailLegend"></div> <div class="Key-icon Key-icon--success"></div>
<div>{{ strings.legend.ON_FAIL }}</div> <p class="Key-listItemContent">{{strings.legend.ON_SUCCESS}}</p>
</div> </li>
<div class="WorkflowLegend-legendItem"> <li class="Key-listItem">
<div class="WorkflowLegend-alwaysLegend"></div> <div class="Key-icon Key-icon--fail"></div>
<div>{{ strings.legend.ALWAYS }}</div> <p class="Key-listItemContent">{{strings.legend.ON_FAILURE}}</p>
</div> </li>
<div class="WorkflowLegend-legendItem"> <li class="Key-listItem">
<div class="WorkflowLegend-letterCircle">P</div> <div class="Key-icon Key-icon--always"></div>
<div>{{ strings.legend.PROJECT_SYNC }}</div> <p class="Key-listItemContent">{{strings.legend.ALWAYS}}</p>
</div> </li>
<div class="WorkflowLegend-legendItem"> <li class="Key-listItem">
<div class="WorkflowLegend-letterCircle">I</div> <div class="Key-icon Key-icon--circle Key-icon--default">P</div>
<div>{{ strings.legend.INVENTORY_SYNC }}</div> <p class="Key-listItemContent Key-listItemContent--circle">{{strings.legend.PROJECT_SYNC}}</p>
</div> </li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--circle Key-icon--default">I</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.legend.INVENTORY_SYNC}}</p>
</li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--circle Key-icon--default">W</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.legend.WORKFLOW}}</p>
</li>
</ul>
</div> </div>
<div class="WorkflowLegend-details--right"> <div class="WorkflowLegend-details--right">
<i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i> <i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i>