From b37ee2f611ebdb229b78aacc984e9ff0caee8ad5 Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 17 Apr 2018 16:57:28 -0400 Subject: [PATCH] Show warning on wfjt form when the workflow contains deleted job templates --- awx/ui/client/lib/theme/index.less | 1 + awx/ui/client/src/shared/form-generator.js | 2 + awx/ui/client/src/templates/main.js | 9 + awx/ui/client/src/templates/workflows.form.js | 10 + .../edit-workflow/workflow-edit.controller.js | 236 ++++++++++-------- .../workflow-maker.controller.js | 85 ++++--- .../templates/workflows/workflow.block.less | 13 + 7 files changed, 210 insertions(+), 146 deletions(-) create mode 100644 awx/ui/client/src/templates/workflows/workflow.block.less diff --git a/awx/ui/client/lib/theme/index.less b/awx/ui/client/lib/theme/index.less index a0f7738272..caf02e2882 100644 --- a/awx/ui/client/lib/theme/index.less +++ b/awx/ui/client/lib/theme/index.less @@ -126,6 +126,7 @@ @import '../../src/templates/survey-maker/survey-maker.block.less'; @import '../../src/templates/survey-maker/shared/survey-controls.block.less'; @import '../../src/templates/survey-maker/survey-maker.block.less'; +@import '../../src/templates/workflows/workflow.block.less'; @import '../../src/templates/workflows/workflow-chart/workflow-chart.block.less'; @import '../../src/templates/workflows/workflow-controls/workflow-controls.block.less'; @import '../../src/templates/workflows/workflow-maker/workflow-maker.block.less'; diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 106bc3642d..574e8e8342 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -545,6 +545,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "' "; html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : ""; html += " class='ScheduleToggle-switch' ng-click='" + field.ngClick + "' translate>" + i18n._("OFF") + ""; + } else if (field.type === 'html') { + html += field.html; } return html; }, diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index dc83056fe7..5a77520a5c 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -377,6 +377,15 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p response.status }); }); + }], + workflowLaunch: ['$stateParams', 'WorkflowJobTemplateModel', + function($stateParams, WorkflowJobTemplate) { + let workflowJobTemplate = new WorkflowJobTemplate(); + + return workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id) + .then(({data}) => { + return data; + }); }] } } diff --git a/awx/ui/client/src/templates/workflows.form.js b/awx/ui/client/src/templates/workflows.form.js index ac2a6a7d5a..7be0a70472 100644 --- a/awx/ui/client/src/templates/workflows.form.js +++ b/awx/ui/client/src/templates/workflows.form.js @@ -27,6 +27,16 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) { detailsClick: "$state.go('templates.editWorkflowJobTemplate')", include: ['/static/partials/survey-maker-modal.html'], + headerFields: { + missingTemplates: { + type: 'html', + html: `
+ ` + + i18n._("Missing Job Templates found in the Workflow Editor") + + `
` + } + }, + fields: { name: { label: i18n._('Name'), diff --git a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js index a67cad1156..4d1bc567a7 100644 --- a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js +++ b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js @@ -10,10 +10,15 @@ export default [ 'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification', 'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'i18n', + 'workflowLaunch', '$transitions', 'WorkflowJobTemplateModel', function($scope, $stateParams, WorkflowForm, GenerateForm, Alert, - ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty, - ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString, - TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n) { + ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty, + ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString, + TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n, + workflowLaunch, $transitions, WorkflowJobTemplate + ) { + + $scope.missingTemplates = _.has(workflowLaunch, 'node_templates_missing') && workflowLaunch.node_templates_missing.length > 0 ? true : false; $scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) { if (val === false) { @@ -21,6 +26,25 @@ export default [ } }); + const criteriaObj = { + from: (state) => state.name === 'templates.editWorkflowJobTemplate.workflowMaker', + to: (state) => state.name === 'templates.editWorkflowJobTemplate' + }; + + $transitions.onSuccess(criteriaObj, function() { + if ($scope.missingTemplates) { + // Go out and check the new launch response to see if the user has fixed the + // missing node templates + + let workflowJobTemplate = new WorkflowJobTemplate(); + + workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id) + .then(({data}) => { + $scope.missingTemplates = _.has(data, 'node_templates_missing') && data.node_templates_missing.length > 0 ? true : false; + }); + } + }); + // Inject dynamic view let form = WorkflowForm(), generator = GenerateForm, @@ -30,119 +54,24 @@ export default [ $scope.parseType = 'yaml'; $scope.includeWorkflowMaker = false; - function init() { + $scope.openWorkflowMaker = function() { + $state.go('.workflowMaker'); + }; - // Select2-ify the lables input - CreateSelect2({ - element:'#workflow_job_template_labels', - multiple: true, - addNew: true - }); + $scope.formSave = function () { + let fld, data = {}; + $scope.invalid_survey = false; - SurveyControllerInit({ - scope: $scope, - parent_scope: $scope, - id: id, - templateType: 'workflow_job_template' - }); - - $scope.labelOptions = availableLabels - .map((i) => ({label: i.name, value: i.id})); - - var opts = selectedLabels - .map(i => ({id: i.id + "", - test: i.name})); - - CreateSelect2({ - element:'#workflow_job_template_labels', - multiple: true, - addNew: true, - opts: opts - }); - - $scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor."); - $scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.'); - - $scope.workflow_job_template_obj = workflowJobTemplateData; - $scope.name = workflowJobTemplateData.name; - $scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit; - let fld, i; - for (fld in form.fields) { - if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) { - if (form.fields[fld].type === 'select') { - if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) { - for (i = 0; i < $scope[fld + '_options'].length; i++) { - if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) { - $scope[fld] = $scope[fld + '_options'][i]; - } - } - } else { - $scope[fld] = workflowJobTemplateData[fld]; - } - } else { - $scope[fld] = workflowJobTemplateData[fld]; - if(!Empty(workflowJobTemplateData.summary_fields.survey)) { - $scope.survey_exists = true; - } - } - } - if (fld === 'variables') { - // Parse extra_vars, converting to YAML. - $scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars); - - ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' }); - } - if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) { - $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - } - if (form.fields[fld].type === 'checkbox_group') { - for(var j=0; j ({label: i.name, value: i.id})); + + var opts = selectedLabels + .map(i => ({id: i.id + "", + test: i.name})); + + CreateSelect2({ + element:'#workflow_job_template_labels', + multiple: true, + addNew: true, + opts: opts + }); + + $scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor."); + $scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.'); + + $scope.workflow_job_template_obj = workflowJobTemplateData; + $scope.name = workflowJobTemplateData.name; + $scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit; + let fld, i; + for (fld in form.fields) { + if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) { + if (form.fields[fld].type === 'select') { + if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) { + for (i = 0; i < $scope[fld + '_options'].length; i++) { + if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) { + $scope[fld] = $scope[fld + '_options'][i]; + } + } + } else { + $scope[fld] = workflowJobTemplateData[fld]; + } + } else { + $scope[fld] = workflowJobTemplateData[fld]; + if(!Empty(workflowJobTemplateData.summary_fields.survey)) { + $scope.survey_exists = true; + } + } + } + if (fld === 'variables') { + // Parse extra_vars, converting to YAML. + $scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars); + + ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' }); + } + if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) { + $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; + } + } + + if(workflowJobTemplateData.organization) { + OrgAdminLookup.checkForRoleLevelAdminAccess(workflowJobTemplateData.organization, 'workflow_admin_role') + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + } + else { + $scope.canEditOrg = true; + } + + $scope.url = workflowJobTemplateData.url; + $scope.survey_enabled = workflowJobTemplateData.survey_enabled; + + $scope.includeWorkflowMaker = true; + + $scope.$on('SurveySaved', function() { + Wait('stop'); + $scope.survey_exists = true; + $scope.invalid_survey = false; + }); } ]; 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 51c5f31e22..c27ecde1d7 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 @@ -76,15 +76,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // Create the node let sendableNodeData = { unified_job_template: params.node.unifiedJobTemplate.id, - credential: _.get(params, 'node.originalNodeObj.credential') || null + extra_data: {}, + inventory: null, + job_type: null, + job_tags: null, + skip_tags: null, + limit: null, + diff_mode: null, + verbosity: null, + credential: null }; if (_.has(params, 'node.promptData.extraVars')) { if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) { - if (!sendableNodeData.extra_data) { - sendableNodeData.extra_data = {}; - } - const defaultVars = jsyaml.safeLoad(params.node.promptData.launchConf.defaults.extra_vars); // Only include extra vars that differ from the template default vars @@ -674,11 +678,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', launchConf.passwords_needed_to_start.length === 0 && launchConf.variables_needed_to_start.length === 0) { $scope.showPromptButton = false; + $scope.promptModalMissingReqFields = false; } else { $scope.showPromptButton = true; if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) { $scope.promptModalMissingReqFields = true; + } else { + $scope.promptModalMissingReqFields = false; } if (responses[1].data.survey_enabled) { @@ -728,38 +735,42 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); } - if ($scope.nodeBeingEdited.unifiedJobTemplate.type === "job_template") { + if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) { + if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template") { + $scope.workflowMakerFormConfig.activeTab = "jobs"; + } + + $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate; + + if ($scope.selectedTemplate.unified_job_type) { + switch ($scope.selectedTemplate.unified_job_type) { + case "job": + $scope.workflowMakerFormConfig.activeTab = "jobs"; + break; + case "project_update": + $scope.workflowMakerFormConfig.activeTab = "project_sync"; + break; + case "inventory_update": + $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; + break; + } + } else if ($scope.selectedTemplate.type) { + switch ($scope.selectedTemplate.type) { + case "job_template": + $scope.workflowMakerFormConfig.activeTab = "jobs"; + break; + case "project": + $scope.workflowMakerFormConfig.activeTab = "project_sync"; + break; + case "inventory_source": + $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; + break; + } + } + } else { $scope.workflowMakerFormConfig.activeTab = "jobs"; } - $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate; - - if ($scope.selectedTemplate.unified_job_type) { - switch ($scope.selectedTemplate.unified_job_type) { - case "job": - $scope.workflowMakerFormConfig.activeTab = "jobs"; - break; - case "project_update": - $scope.workflowMakerFormConfig.activeTab = "project_sync"; - break; - case "inventory_update": - $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; - break; - } - } else if ($scope.selectedTemplate.type) { - switch ($scope.selectedTemplate.type) { - case "job_template": - $scope.workflowMakerFormConfig.activeTab = "jobs"; - break; - case "project": - $scope.workflowMakerFormConfig.activeTab = "project_sync"; - break; - case "inventory_source": - $scope.workflowMakerFormConfig.activeTab = "inventory_sync"; - break; - } - } - let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ tree: $scope.treeData.data, parentId: parent.id, @@ -771,7 +782,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', switch($scope.nodeBeingEdited.edgeType) { case "always": $scope.edgeType = {label: "Always", value: "always"}; - if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) { + if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always") || $scope.nodeBeingEdited.isRoot) { edgeDropdownOptions = ["always"]; } break; @@ -987,11 +998,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', launchConf.passwords_needed_to_start.length === 0 && launchConf.variables_needed_to_start.length === 0) { $scope.showPromptButton = false; + $scope.promptModalMissingReqFields = false; } else { $scope.showPromptButton = true; if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { $scope.promptModalMissingReqFields = true; + } else { + $scope.promptModalMissingReqFields = false; } if (launchConf.survey_enabled) { @@ -1048,6 +1062,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplate = angular.copy(selectedTemplate); $scope.selectedTemplateInvalid = false; $scope.showPromptButton = false; + $scope.promptModalMissingReqFields = false; } }; diff --git a/awx/ui/client/src/templates/workflows/workflow.block.less b/awx/ui/client/src/templates/workflows/workflow.block.less new file mode 100644 index 0000000000..66f3f76ec4 --- /dev/null +++ b/awx/ui/client/src/templates/workflows/workflow.block.less @@ -0,0 +1,13 @@ +.Workflow-warning { + float: right; + margin-right: 20px; + color: @default-interface-txt; +} +.Workflow-warningIcon { + color: @default-warning; + margin-right: 5px; +} +.Workflow-warningLink { + color: @default-link; + cursor: pointer; +}