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'),
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'),

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 {
display: flex;
}

View File

@ -1,3 +1,4 @@
<div class="List-infoCell">
<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>
<div class="List-infoCell" ng-if="job_template.type === 'job_template'">
<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>

View File

@ -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 += `<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 = {
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
};

View File

@ -385,7 +385,12 @@ 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")
@ -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,
});
}
};

View File

@ -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;

View File

@ -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,11 +679,15 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
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;
}
let credentialRequiresPassword = 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,7 +866,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
.then(function (data) {
$scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
finishConfiguringEdit();
}, 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', {
@ -982,14 +1000,13 @@ 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 {
@ -1001,6 +1018,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} else {
$scope.credentialRequiresPassword = false;
}
}
$scope.selectedTemplate = angular.copy(selectedTemplate);
@ -1021,11 +1039,15 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
} else {
$scope.showPromptButton = 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;
}
if (launchConf.survey_enabled) {
// go out and get the survey questions
@ -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', {

View File

@ -62,6 +62,10 @@
<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>
</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">
<div class="Key-icon Key-icon--circle Key-icon--warning">!</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.get('workflow_maker.WARNING')}}</p>
@ -95,7 +99,9 @@
</div>
<span ng-show="selectedTemplate &&
((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.unified_job_type === 'inventory_update' && workflowMakerFormConfig.activeTab === 'inventory_sync') ||
(selectedTemplate.type === 'inventory_source' && workflowMakerFormConfig.activeTab === 'inventory_sync'))">
<div ng-if="selectedTemplate && selectedTemplateInvalid">
<div class="WorkflowMaker-invalidJobTemplateWarning">

View File

@ -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;
}

View File

@ -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();

View File

@ -75,6 +75,33 @@
</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 -->
<div class="WorkflowResults-resultRow"
ng-show="workflow.started">
@ -160,6 +187,19 @@
</div>
</div>
<!-- PARENT WORKFLOW DETAIL -->
<div class="WorkflowResults-resultRow"
ng-if="workflow.summary_fields.source_workflow_job">
<label class="WorkflowResults-resultRowLabel">
{{ strings.labels.SOURCE_WORKFLOW_JOB }}
</label>
<div class="WorkflowResults-resultRowText">
<a href="{{ source_workflow_job_link }}">
{{ workflow.summary_fields.source_workflow_job.name }}
</a>
</div>
</div>
<!-- EXTRA VARIABLES DETAIL -->
<at-code-mirror
ng-if="variables"
@ -268,27 +308,36 @@
<workflow-status-bar></workflow-status-bar>
<div class="WorkflowLegend-details">
<div class="WorkflowLegend-details--left">
<div class="WorkflowLegend-legendItem">{{ strings.legend.KEY }}:</div>
<div class="WorkflowLegend-legendItem">
<div class="WorkflowLegend-onSuccessLegend"></div>
<div>{{ strings.legend.ON_SUCCESS }}</div>
</div>
<div class="WorkflowLegend-legendItem">
<div class="WorkflowLegend-onFailLegend"></div>
<div>{{ strings.legend.ON_FAIL }}</div>
</div>
<div class="WorkflowLegend-legendItem">
<div class="WorkflowLegend-alwaysLegend"></div>
<div>{{ strings.legend.ALWAYS }}</div>
</div>
<div class="WorkflowLegend-legendItem">
<div class="WorkflowLegend-letterCircle">P</div>
<div>{{ strings.legend.PROJECT_SYNC }}</div>
</div>
<div class="WorkflowLegend-legendItem">
<div class="WorkflowLegend-letterCircle">I</div>
<div>{{ strings.legend.INVENTORY_SYNC }}</div>
</div>
<i ng-class="{{ keyClassList }}" class="fa fa-key Key-menuIcon" ng-click="toggleKey()"></i>
<ul ng-show="showKey" class="Key-list noselect">
<li class="Key-listItem">
<p class="Key-heading">{{strings.legend.KEY}}</p>
</li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--success"></div>
<p class="Key-listItemContent">{{strings.legend.ON_SUCCESS}}</p>
</li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--fail"></div>
<p class="Key-listItemContent">{{strings.legend.ON_FAILURE}}</p>
</li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--always"></div>
<p class="Key-listItemContent">{{strings.legend.ALWAYS}}</p>
</li>
<li class="Key-listItem">
<div class="Key-icon Key-icon--circle Key-icon--default">P</div>
<p class="Key-listItemContent Key-listItemContent--circle">{{strings.legend.PROJECT_SYNC}}</p>
</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 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>