diff --git a/awx/main/migrations/0083_v360_workflow_approval.py b/awx/main/migrations/0084_v360_workflow_approval.py similarity index 98% rename from awx/main/migrations/0083_v360_workflow_approval.py rename to awx/main/migrations/0084_v360_workflow_approval.py index eeb73d83b6..2f4f3fc812 100644 --- a/awx/main/migrations/0083_v360_workflow_approval.py +++ b/awx/main/migrations/0084_v360_workflow_approval.py @@ -8,7 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('main', '0082_v360_webhook_http_method'), + ('main', '0083_v360_job_branch_overrirde'), ] operations = [ diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js index 86c440482f..4bd9590869 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js @@ -6,11 +6,11 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService', 'Rest', '$q', 'TemplatesStrings', 'CreateSelect2', 'Empty', 'QuerySet', '$filter', - 'GetBasePath', 'TemplateList', 'ProjectList', 'InventorySourcesList', 'ProcessErrors', + 'GetBasePath', 'WorkflowNodeFormService', 'ProcessErrors', 'i18n', 'ParseTypeChange', 'WorkflowJobTemplateModel', function($scope, TemplatesService, JobTemplate, PromptService, Rest, $q, TemplatesStrings, CreateSelect2, Empty, qs, $filter, - GetBasePath, TemplateList, ProjectList, InventorySourcesList, ProcessErrors, + GetBasePath, WorkflowNodeFormService, ProcessErrors, i18n, ParseTypeChange, WorkflowJobTemplate ) { @@ -32,60 +32,14 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService $scope.strings = TemplatesStrings; $scope.editNodeHelpMessage = null; - $scope.pauseNode = {}; - let templateList = _.cloneDeep(TemplateList); - delete templateList.actions; - delete templateList.fields.type; - delete templateList.fields.description; - delete templateList.fields.smart_status; - delete templateList.fields.labels; - delete templateList.fieldActions; - templateList.name = 'wf_maker_templates'; - templateList.iterator = 'wf_maker_template'; - templateList.fields.name.columnClass = "col-md-8"; - templateList.fields.name.tag = i18n._('WORKFLOW'); - templateList.fields.name.showTag = "{{wf_maker_template.type === 'workflow_job_template'}}"; - templateList.disableRow = "{{ readOnly }}"; - templateList.disableRowValue = 'readOnly'; - templateList.basePath = 'unified_job_templates'; - templateList.fields.info = { - ngInclude: "'/static/partials/job-template-details.html'", - type: 'template', - columnClass: 'col-md-3', - infoHeaderClass: 'col-md-3', - label: '', - nosort: true - }; - templateList.maxVisiblePages = 5; - templateList.searchBarFullWidth = true; - $scope.templateList = templateList; - - let inventorySourceList = _.cloneDeep(InventorySourcesList); - inventorySourceList.name = 'wf_maker_inventory_sources'; - inventorySourceList.iterator = 'wf_maker_inventory_source'; - inventorySourceList.maxVisiblePages = 5; - inventorySourceList.searchBarFullWidth = true; - inventorySourceList.disableRow = "{{ readOnly }}"; - inventorySourceList.disableRowValue = 'readOnly'; - $scope.inventorySourceList = inventorySourceList; - - let projectList = _.cloneDeep(ProjectList); - delete projectList.fields.status; - delete projectList.fields.scm_type; - delete projectList.fields.last_updated; - projectList.name = 'wf_maker_projects'; - projectList.iterator = 'wf_maker_project'; - projectList.fields.name.columnClass = "col-md-11"; - projectList.maxVisiblePages = 5; - projectList.searchBarFullWidth = true; - projectList.disableRow = "{{ readOnly }}"; - projectList.disableRowValue = 'readOnly'; - $scope.projectList = projectList; + $scope.templateList = WorkflowNodeFormService.templateListDefinition(); + $scope.inventorySourceList = WorkflowNodeFormService.inventorySourceListDefinition(); + $scope.projectList = WorkflowNodeFormService.projectListDefinition(); const checkCredentialsForRequiredPasswords = () => { let credentialRequiresPassword = false; - $scope.promptData.prompts.credentials.value.forEach((credential) => { + $scope.jobNodeState.promptData.prompts.credentials.value.forEach((credential) => { if ((credential.passwords_needed && credential.passwords_needed.length > 0) || (_.has(credential, 'inputs.vault_password') && @@ -95,86 +49,143 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService } }); - $scope.credentialRequiresPassword = credentialRequiresPassword; + $scope.jobNodeState.credentialRequiresPassword = credentialRequiresPassword; }; const watchForPromptChanges = () => { let promptDataToWatch = [ - 'promptData.prompts.inventory.value', - 'promptData.prompts.verbosity.value', - 'missingSurveyValue' + 'jobNodeState.promptData.prompts.inventory.value', + 'jobNodeState.promptData.prompts.verbosity.value', + 'jobNodeState.missingSurveyValue' ]; promptWatcher = $scope.$watchGroup(promptDataToWatch, () => { - const templateType = _.get($scope, 'promptData.templateType'); + const templateType = _.get($scope, 'jobNodeState.promptData.templateType'); let missingPromptValue = false; - if ($scope.missingSurveyValue) { + if ($scope.jobNodeState.missingSurveyValue) { missingPromptValue = true; } if (templateType !== "workflow_job_template") { - if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + if (!$scope.jobNodeState.promptData.prompts.inventory.value || !$scope.jobNodeState.promptData.prompts.inventory.value.id) { missingPromptValue = true; } } - $scope.promptModalMissingReqFields = missingPromptValue; + $scope.jobNodeState.promptModalMissingReqFields = missingPromptValue; }); - if ($scope.promptData.launchConf.ask_credential_on_launch && $scope.credentialRequiresPassword) { - credentialsWatcher = $scope.$watch('promptData.prompts.credentials', () => { + if ($scope.jobNodeState.promptData.launchConf.ask_credential_on_launch && $scope.jobNodeState.credentialRequiresPassword) { + credentialsWatcher = $scope.$watch('jobNodeState.promptData.prompts.credentials', () => { checkCredentialsForRequiredPasswords(); }); } }; - const finishConfiguringAdd = () => { - $scope.selectedTemplate = null; - $scope.activeTab = "jobs"; - const alwaysOption = { - label: $scope.strings.get('workflow_maker.ALWAYS'), - value: 'always' - }; - const successOption = { - label: $scope.strings.get('workflow_maker.ON_SUCCESS'), - value: 'success' - }; - const failureOption = { - label: $scope.strings.get('workflow_maker.ON_FAILURE'), - value: 'failure' - }; - $scope.edgeTypeOptions = [alwaysOption]; - switch($scope.nodeConfig.newNodeIsRoot) { - case true: - $scope.edgeType = alwaysOption; - break; - case false: - $scope.edgeType = successOption; - $scope.edgeTypeOptions.push(successOption, failureOption); - break; + const clearWatchers = () => { + if (promptWatcher) { + promptWatcher(); } + + if (surveyQuestionWatcher) { + surveyQuestionWatcher(); + } + + if (credentialsWatcher) { + credentialsWatcher(); + } + }; + + const select2ifyDropdowns = () => { + CreateSelect2({ + element: '#workflow-node-types', + multiple: false + }); CreateSelect2({ element: '#workflow_node_edge', multiple: false }); + }; - $scope.nodeFormDataLoaded = true; + const formatPopOverDetails = (model) => { + const popOverDetails = {}; + popOverDetails.playbook = model.playbook || i18n._('NONE SELECTED'); + Object.keys(model.summary_fields).forEach(field => { + if (field === 'project') { + popOverDetails.project = model.summary_fields[field].name || i18n._('NONE SELECTED'); + } + if (field === 'inventory') { + popOverDetails.inventory = model.summary_fields[field].name || i18n._('NONE SELECTED'); + } + if (field === 'credentials') { + if (model.summary_fields[field].length <= 0) { + popOverDetails.credentials = i18n._('NONE SELECTED'); + } + else { + const credentialNames = model.summary_fields[field].map(({name}) => name); + popOverDetails.credentials = credentialNames.join('
'); + } + } + }); + model.popOver = ` + + + + + + + + + + + + + + + + + +
${i18n._('INVENTORY')} ${$filter('sanitize')(popOverDetails.inventory)}
${i18n._('PROJECT')} ${$filter('sanitize')(popOverDetails.project)}
${i18n._('PLAYBOOK')} ${$filter('sanitize')(popOverDetails.playbook)}
${i18n._('CREDENTIAL')} ${$filter('sanitize')(popOverDetails.credentials)}
+ `; + }; + + const updateSelectedRow = () => { + let unifiedJobTemplateId; + switch($scope.activeTab) { + case 'templates': + unifiedJobTemplateId = _.get($scope, 'jobNodeState.selectedTemplate.id') || null; + $scope.wf_maker_templates.forEach((row, i) => { + if (row.type === 'job_template') { + formatPopOverDetails(row); + } + $scope.wf_maker_templates[i].checked = (row.id === unifiedJobTemplateId) ? 1 : 0; + }); + break; + case 'project_syncs': + unifiedJobTemplateId = _.get($scope, 'projectNodeState.selectedTemplate.id') || null; + $scope.wf_maker_projects.forEach((row, i) => { + $scope.wf_maker_projects[i].checked = (row.id === unifiedJobTemplateId) ? 1 : 0; + }); + break; + case 'inventory_syncs': + unifiedJobTemplateId = _.get($scope, 'inventoryNodeState.selectedTemplate.id') || null; + $scope.wf_maker_inventory_sources.forEach((row, i) => { + $scope.wf_maker_inventory_sources[i].checked = (row.id === unifiedJobTemplateId) ? 1 : 0; + }); + break; + } }; const getEditNodeHelpMessage = (selectedTemplate, workflowJobTemplateObj) => { if (selectedTemplate) { if (selectedTemplate.type === "workflow_job_template") { - if (workflowJobTemplateObj.inventory) { - if (selectedTemplate.ask_inventory_on_launch) { - return $scope.strings.get('workflow_maker.INVENTORY_WILL_OVERRIDE'); - } + if (workflowJobTemplateObj.inventory && selectedTemplate.ask_inventory_on_launch) { + return $scope.strings.get('workflow_maker.INVENTORY_WILL_OVERRIDE'); } - if (workflowJobTemplateObj.ask_inventory_on_launch) { - if (selectedTemplate.ask_inventory_on_launch) { - return $scope.strings.get('workflow_maker.INVENTORY_PROMPT_WILL_OVERRIDE'); - } + if (workflowJobTemplateObj.ask_inventory_on_launch && selectedTemplate.ask_inventory_on_launch) { + return $scope.strings.get('workflow_maker.INVENTORY_PROMPT_WILL_OVERRIDE'); } } @@ -210,19 +221,19 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService let jobTemplate = templateType === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate(); if (_.get($scope, 'nodeConfig.node.promptData') && !_.isEmpty($scope.nodeConfig.node.promptData)) { - $scope.promptData = _.cloneDeep($scope.nodeConfig.node.promptData); - const launchConf = $scope.promptData.launchConf; + $scope.jobNodeState.promptData = _.cloneDeep($scope.nodeConfig.node.promptData); + const launchConf = $scope.jobNodeState.promptData.launchConf; if (!shouldShowPromptButton(launchConf)) { - $scope.showPromptButton = false; - $scope.promptModalMissingReqFields = false; + $scope.jobNodeState.showPromptButton = false; + $scope.jobNodeState.promptModalMissingReqFields = false; } else { - $scope.showPromptButton = true; + $scope.jobNodeState.showPromptButton = true; if (templateType !== "workflow_job_template" && launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) { - $scope.promptModalMissingReqFields = true; + $scope.jobNodeState.promptModalMissingReqFields = true; } else { - $scope.promptModalMissingReqFields = false; + $scope.jobNodeState.promptModalMissingReqFields = false; } } watchForPromptChanges(); @@ -286,9 +297,9 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService ((!$scope.nodeConfig.node.fullUnifiedJobTemplateObject.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeConfig.node.fullUnifiedJobTemplateObject.project) ) { - $scope.selectedTemplateInvalid = true; + $scope.jobNodeState.selectedTemplateInvalid = true; } else { - $scope.selectedTemplateInvalid = false; + $scope.jobNodeState.selectedTemplateInvalid = false; } let credentialRequiresPassword = false; @@ -307,19 +318,19 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService } }); - $scope.credentialRequiresPassword = credentialRequiresPassword; + $scope.jobNodeState.credentialRequiresPassword = credentialRequiresPassword; if (!shouldShowPromptButton(launchConf)) { - $scope.showPromptButton = false; - $scope.promptModalMissingReqFields = false; + $scope.jobNodeState.showPromptButton = false; + $scope.jobNodeState.promptModalMissingReqFields = false; $scope.nodeFormDataLoaded = true; } else { - $scope.showPromptButton = true; + $scope.jobNodeState.showPromptButton = true; if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.inventory')) { - $scope.promptModalMissingReqFields = true; + $scope.jobNodeState.promptModalMissingReqFields = true; } else { - $scope.promptModalMissingReqFields = false; + $scope.jobNodeState.promptModalMissingReqFields = false; } if (responses[1].data.survey_enabled) { @@ -332,7 +343,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService extra_data: jsyaml.safeLoad(prompts.variables.value) }); - $scope.missingSurveyValue = processed.missingSurveyValue; + $scope.jobNodeState.missingSurveyValue = processed.missingSurveyValue; $scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data); @@ -351,14 +362,14 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService template: $scope.nodeConfig.node.fullUnifiedJobTemplateObject.id }; - surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { + surveyQuestionWatcher = $scope.$watch('jobNodeState.promptData.surveyQuestions', () => { let missingSurveyValue = false; - _.each($scope.promptData.surveyQuestions, (question) => { + _.each($scope.jobNodeState.promptData.surveyQuestions, (question) => { if (question.required && (Empty(question.model) || question.model === [])) { missingSurveyValue = true; } }); - $scope.missingSurveyValue = missingSurveyValue; + $scope.jobNodeState.missingSurveyValue = missingSurveyValue; }, true); checkCredentialsForRequiredPasswords(); @@ -368,7 +379,7 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService $scope.nodeFormDataLoaded = true; }); } else { - $scope.nodeConfig.node.promptData = $scope.promptData = { + $scope.nodeConfig.node.promptData = $scope.jobNodeState.promptData = { launchConf: launchConf, launchOptions: launchOptions, prompts: prompts, @@ -389,39 +400,46 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService } if (_.get($scope, 'nodeConfig.node.fullUnifiedJobTemplateObject')) { - $scope.selectedTemplate = $scope.nodeConfig.node.fullUnifiedJobTemplateObject; + const selectedTemplate = $scope.nodeConfig.node.fullUnifiedJobTemplateObject; - if ($scope.selectedTemplate.unified_job_type) { - switch ($scope.selectedTemplate.unified_job_type) { + if (selectedTemplate.unified_job_type) { + switch (selectedTemplate.unified_job_type) { case "job": - $scope.activeTab = "jobs"; + $scope.activeTab = "templates"; + $scope.jobNodeState.selectedTemplate = selectedTemplate; break; case "project_update": $scope.activeTab = "project_syncs"; + $scope.projectNodeState.selectedTemplate = selectedTemplate; break; case "inventory_update": $scope.activeTab = "inventory_syncs"; + $scope.inventoryNodeState.selectedTemplate = selectedTemplate; break; } - } else if ($scope.selectedTemplate.type) { - switch ($scope.selectedTemplate.type) { + } else if (selectedTemplate.type) { + switch (selectedTemplate.type) { case "job_template": - $scope.activeTab = "jobs"; - break; case "workflow_job_template": - $scope.activeTab = "jobs"; + $scope.activeTab = "templates"; + $scope.jobNodeState.selectedTemplate = selectedTemplate; break; case "project": $scope.activeTab = "project_syncs"; + $scope.projectNodeState.selectedTemplate = selectedTemplate; break; case "inventory_source": $scope.activeTab = "inventory_syncs"; + $scope.inventoryNodeState.selectedTemplate = selectedTemplate; break; } } + updateSelectedRow(); } else { - $scope.activeTab = "jobs"; + $scope.activeTab = "templates"; } + + select2ifyDropdowns(); } else { $scope.jobTags = $scope.nodeConfig.node.originalNodeObject.job_tags ? $scope.nodeConfig.node.originalNodeObject.job_tags.split(',').map((tag) => (tag)) : []; $scope.skipTags = $scope.nodeConfig.node.originalNodeObject.skip_tags ? $scope.nodeConfig.node.originalNodeObject.skip_tags.split(',').map((tag) => (tag)) : []; @@ -449,121 +467,30 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService }; - const templateManuallySelected = (selectedTemplate) => { - - if (promptWatcher) { - promptWatcher(); - } - - if (surveyQuestionWatcher) { - surveyQuestionWatcher(); - } - - if (credentialsWatcher) { - credentialsWatcher(); - } - - $scope.promptData = null; - $scope.pauseNode = {}; - $scope.editNodeHelpMessage = getEditNodeHelpMessage(selectedTemplate, $scope.workflowJobTemplateObj); - - 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; - - let credentialRequiresPassword = false; - let selectedTemplateInvalid = false; - - if (selectedTemplate.type !== "workflow_job_template") { - if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) { - selectedTemplateInvalid = true; - } - - if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) { - credentialRequiresPassword = true; - } - } - - $scope.credentialRequiresPassword = credentialRequiresPassword; - $scope.selectedTemplateInvalid = selectedTemplateInvalid; - $scope.selectedTemplate = angular.copy(selectedTemplate); - - if (!shouldShowPromptButton(launchConf)) { - $scope.showPromptButton = false; - $scope.promptModalMissingReqFields = false; - } else { - $scope.showPromptButton = true; - $scope.promptModalMissingReqFields = false; - - if (selectedTemplate.type !== "workflow_job_template") { - if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { - $scope.promptModalMissingReqFields = true; - } - } - - if (launchConf.survey_enabled) { - // go out and get the survey questions - jobTemplate.getSurveyQuestions(selectedTemplate.id) - .then((surveyQuestionRes) => { - - let processed = PromptService.processSurveyQuestions({ - surveyQuestions: surveyQuestionRes.data.spec - }); - - $scope.missingSurveyValue = processed.missingSurveyValue; - - $scope.promptData = { - launchConf: responses[1].data, - launchOptions: responses[0].data, - surveyQuestions: processed.surveyQuestions, - template: selectedTemplate.id, - templateType: selectedTemplate.type, - prompts: PromptService.processPromptValues({ - launchConf: responses[1].data, - launchOptions: responses[0].data - }), - }; - - surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => { - let missingSurveyValue = false; - _.each($scope.promptData.surveyQuestions, (question) => { - if (question.required && (Empty(question.model) || question.model === [])) { - missingSurveyValue = true; - } - }); - $scope.missingSurveyValue = missingSurveyValue; - }, true); - - watchForPromptChanges(); - }); - } else { - $scope.promptData = { - launchConf: responses[1].data, - launchOptions: responses[0].data, - template: selectedTemplate.id, - templateType: selectedTemplate.type, - prompts: PromptService.processPromptValues({ - launchConf: responses[1].data, - launchOptions: responses[0].data - }), - }; - - watchForPromptChanges(); - } - } - }); - } else { - $scope.selectedTemplate = angular.copy(selectedTemplate); - $scope.selectedTemplateInvalid = false; - $scope.showPromptButton = false; - $scope.promptModalMissingReqFields = false; - } - }; - const setupNodeForm = () => { + $scope.jobNodeState = { + credentialRequiresPassword: false, + missingSurveyValue: false, + promptData: null, + promptModalMissingReqFields: false, + searchTags: [], + selectedTemplate: null, + selectedTemplateInvalid: false, + showPromptButton: false + }; + $scope.projectNodeState = { + searchTags: [], + selectedTemplate: null + }; + $scope.inventoryNodeState = { + searchTags: [], + selectedTemplate: null, + }; + $scope.approvalNodeState = { + name: null, + description: null, + timeout: 0 + }; $scope.nodeFormDataLoaded = false; $scope.wf_maker_template_queryset = { page_size: '10', @@ -618,32 +545,23 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService }) ); - CreateSelect2({ - element: '#workflow-node-types', - multiple: false - }); - $q.all(listPromises) .then(() => { if ($scope.nodeConfig.mode === "edit") { if ($scope.nodeConfig.node.unifiedJobTemplate && $scope.nodeConfig.node.unifiedJobTemplate.unified_job_type === "workflow_approval") { - $scope.selectedTemplate = null; - $scope.activeTab = "pause"; - CreateSelect2({ - element: '#workflow_node_edge', - multiple: false - }); + $scope.activeTab = "approval"; + select2ifyDropdowns(); - $scope.pauseNode = { - isPauseNode: true, + $scope.approvalNodeState = { name: $scope.nodeConfig.node.unifiedJobTemplate.name, description: $scope.nodeConfig.node.unifiedJobTemplate.description, + timeout: $scope.nodeConfig.node.unifiedJobTemplate.timeout }; - + $scope.nodeFormDataLoaded = true; } else { // Make sure that we have the full unified job template object - if (!$scope.nodeConfig.node.fullUnifiedJobTemplateObject) { + if (!$scope.nodeConfig.node.fullUnifiedJobTemplateObject && _.has($scope, 'nodeConfig.node.originalNodeObject.summary_fields.unified_job_template')) { // 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 TemplatesService.getUnifiedJobTemplate($scope.nodeConfig.node.originalNodeObject.summary_fields.unified_job_template.id) @@ -662,104 +580,211 @@ export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService } } } else { - finishConfiguringAdd(); + $scope.activeTab = "templates"; + const alwaysOption = { + label: $scope.strings.get('workflow_maker.ALWAYS'), + value: 'always' + }; + const successOption = { + label: $scope.strings.get('workflow_maker.ON_SUCCESS'), + value: 'success' + }; + const failureOption = { + label: $scope.strings.get('workflow_maker.ON_FAILURE'), + value: 'failure' + }; + $scope.edgeTypeOptions = [alwaysOption]; + switch($scope.nodeConfig.newNodeIsRoot) { + case true: + $scope.edgeType = alwaysOption; + break; + case false: + $scope.edgeType = successOption; + $scope.edgeTypeOptions.push(successOption, failureOption); + break; + } + select2ifyDropdowns(); + + $scope.nodeFormDataLoaded = true; } }); }; - const formatPopOverDetails = (model) => { - const popOverDetails = {}; - popOverDetails.playbook = model.playbook || i18n._('NONE SELECTED'); - Object.keys(model.summary_fields).forEach(field => { - if (field === 'project') { - popOverDetails.project = model.summary_fields[field].name || i18n._('NONE SELECTED'); - } - if (field === 'inventory') { - popOverDetails.inventory = model.summary_fields[field].name || i18n._('NONE SELECTED'); - } - if (field === 'credentials') { - if (model.summary_fields[field].length <= 0) { - popOverDetails.credentials = i18n._('NONE SELECTED'); - } - else { - const credentialNames = model.summary_fields[field].map(({name}) => name); - popOverDetails.credentials = credentialNames.join('
'); - } - } - }); - model.popOver = ` -
-
${i18n._('INVENTORY')}
-
${$filter('sanitize')(popOverDetails.inventory)}
-
-
-
${i18n._('PROJECT')}
-
${$filter('sanitize')(popOverDetails.project)}
-
-
-
${i18n._('PLAYBOOK')}
-
${$filter('sanitize')(popOverDetails.playbook)}
-
-
-
${i18n._('CREDENTIAL')}
-
${$filter('sanitize')(popOverDetails.credentials)}
-
- `; + $scope.confirmNodeForm = () => { + const nodeFormData = { + edgeType: $scope.edgeType + }; + + if ($scope.activeTab === "approval") { + nodeFormData.selectedTemplate = { + name: $scope.approvalNodeState.name, + description: $scope.approvalNodeState.description, + timeout: $scope.approvalNodeState.timeout, + unified_job_type: "workflow_approval" + }; + } else if($scope.activeTab === "templates") { + nodeFormData.selectedTemplate = $scope.jobNodeState.selectedTemplate; + nodeFormData.promptData = $scope.jobNodeState.promptData; + } else if($scope.activeTab === "project_syncs") { + nodeFormData.selectedTemplate = $scope.projectNodeState.selectedTemplate; + } else if($scope.activeTab === "inventory_syncs") { + nodeFormData.selectedTemplate = $scope.inventoryNodeState.selectedTemplate; + } + + $scope.select({ nodeFormData }); }; $scope.openPromptModal = () => { - $scope.promptData.triggerModalOpen = true; + $scope.jobNodeState.promptData.triggerModalOpen = true; }; - $scope.toggle_row = (selectedRow) => { + $scope.selectIsDisabled = () => { + if($scope.activeTab === "templates") { + return !($scope.jobNodeState.selectedTemplate) || + $scope.jobNodeState.promptModalMissingReqFields || + $scope.jobNodeState.credentialRequiresPassword || + $scope.jobNodeState.selectedTemplateInvalid; + } else if($scope.activeTab === "project_syncs") { + return !$scope.projectNodeState.selectedTemplate; + } else if($scope.activeTab === "inventory_syncs") { + return !$scope.inventoryNodeState.selectedTemplate; + } else if ($scope.activeTab === "approval") { + return !($scope.approvalNodeState.name && $scope.approvalNodeState.name !== "") || $scope.workflow_approval.pauseTimeout.$error.min; + } + }; + + $scope.selectTemplate = (selectedTemplate) => { if (!$scope.readOnly) { - templateManuallySelected(selectedRow); + clearWatchers(); + + $scope.approvalNodeState = { + name: null, + description: null, + timeout: 0 + }; + $scope.editNodeHelpMessage = getEditNodeHelpMessage(selectedTemplate, $scope.workflowJobTemplateObj); + + if (selectedTemplate.type === "job_template" || selectedTemplate.type === "workflow_job_template") { + let jobTemplate = selectedTemplate.type === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate(); + $scope.jobNodeState.promptData = null; + + $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) + .then((responses) => { + let launchConf = responses[1].data; + + let credentialRequiresPassword = false; + let selectedTemplateInvalid = false; + + if (selectedTemplate.type !== "workflow_job_template") { + if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) { + selectedTemplateInvalid = true; + } + + if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) { + credentialRequiresPassword = true; + } + } + + $scope.jobNodeState.credentialRequiresPassword = credentialRequiresPassword; + $scope.jobNodeState.selectedTemplateInvalid = selectedTemplateInvalid; + $scope.jobNodeState.selectedTemplate = angular.copy(selectedTemplate); + updateSelectedRow(); + + if (!launchConf.survey_enabled && + !launchConf.ask_inventory_on_launch && + !launchConf.ask_credential_on_launch && + !launchConf.ask_verbosity_on_launch && + !launchConf.ask_job_type_on_launch && + !launchConf.ask_limit_on_launch && + !launchConf.ask_tags_on_launch && + !launchConf.ask_skip_tags_on_launch && + !launchConf.ask_diff_mode_on_launch && + !launchConf.credential_needed_to_start && + !launchConf.ask_variables_on_launch && + launchConf.variables_needed_to_start.length === 0) { + $scope.jobNodeState.showPromptButton = false; + $scope.jobNodeState.promptModalMissingReqFields = false; + } else { + $scope.jobNodeState.showPromptButton = true; + $scope.jobNodeState.promptModalMissingReqFields = false; + + if (selectedTemplate.type !== "workflow_job_template") { + if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) { + $scope.jobNodeState.promptModalMissingReqFields = true; + } + } + + if (launchConf.survey_enabled) { + // go out and get the survey questions + jobTemplate.getSurveyQuestions(selectedTemplate.id) + .then((surveyQuestionRes) => { + + let processed = PromptService.processSurveyQuestions({ + surveyQuestions: surveyQuestionRes.data.spec + }); + + $scope.jobNodeState.missingSurveyValue = processed.missingSurveyValue; + + $scope.jobNodeState.promptData = { + launchConf: responses[1].data, + launchOptions: responses[0].data, + surveyQuestions: processed.surveyQuestions, + template: selectedTemplate.id, + templateType: selectedTemplate.type, + prompts: PromptService.processPromptValues({ + launchConf: responses[1].data, + launchOptions: responses[0].data + }), + }; + + surveyQuestionWatcher = $scope.$watch('jobNodeState.promptData.surveyQuestions', () => { + let missingSurveyValue = false; + _.each($scope.jobNodeState.promptData.surveyQuestions, (question) => { + if (question.required && (Empty(question.model) || question.model === [])) { + missingSurveyValue = true; + } + }); + $scope.jobNodeState.missingSurveyValue = missingSurveyValue; + }, true); + + watchForPromptChanges(); + }); + } else { + $scope.jobNodeState.promptData = { + launchConf: responses[1].data, + launchOptions: responses[0].data, + template: selectedTemplate.id, + templateType: selectedTemplate.type, + prompts: PromptService.processPromptValues({ + launchConf: responses[1].data, + launchOptions: responses[0].data + }), + }; + + watchForPromptChanges(); + } + } + }); + } else { + if (selectedTemplate.type === "project") { + $scope.projectNodeState.selectedTemplate = angular.copy(selectedTemplate); + } else if (selectedTemplate.type === "inventory_source") { + $scope.inventoryNodeState.selectedTemplate = angular.copy(selectedTemplate); + } + updateSelectedRow(); + } } }; $scope.$watch('nodeConfig.nodeId', (newNodeId, oldNodeId) => { if (newNodeId !== oldNodeId) { + clearWatchers(); setupNodeForm(); } }); - $scope.$watchGroup(['wf_maker_templates', 'wf_maker_projects', 'wf_maker_inventory_sources', 'activeTab', 'selectedTemplate.id'], () => { - const unifiedJobTemplateId = _.get($scope, 'selectedTemplate.id') || null; - switch($scope.activeTab) { - case 'jobs': - $scope.wf_maker_templates.forEach((row, i) => { - if (row.type === 'job_template') { - formatPopOverDetails(row); - } - if(row.id === unifiedJobTemplateId) { - $scope.wf_maker_templates[i].checked = 1; - } - else { - $scope.wf_maker_templates[i].checked = 0; - } - }); - break; - case 'project_syncs': - $scope.wf_maker_projects.forEach((row, i) => { - if(row.id === unifiedJobTemplateId) { - $scope.wf_maker_projects[i].checked = 1; - } - else { - $scope.wf_maker_projects[i].checked = 0; - } - }); - break; - case 'inventory_syncs': - $scope.wf_maker_inventory_sources.forEach((row, i) => { - if(row.id === unifiedJobTemplateId) { - $scope.wf_maker_inventory_sources[i].checked = 1; - } - else { - $scope.wf_maker_inventory_sources[i].checked = 0; - } - }); - break; - } + $scope.$watchGroup(['wf_maker_templates', 'wf_maker_projects', 'wf_maker_inventory_sources', 'activeTab'], () => { + updateSelectedRow(); }); setupNodeForm(); diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html index b01876a044..88626d9fdc 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html @@ -8,38 +8,38 @@ name="activeTab" aria-hidden="true" > - + - +
-
-
- +
+
+
-
+
No records matched your search.
-
PLEASE ADD ITEMS TO THIS LIST
+
PLEASE ADD ITEMS TO THIS LIST
-
+
- +
-
+
{{wf_maker_template.name}} {{:: strings.get('workflow_maker.WORKFLOW') }} @@ -53,29 +53,29 @@
-
- +
+
-
+
No records matched your search.
-
No Projects Have Been Created
+
No Projects Have Been Created
-
+
- +
-
{{ wf_maker_project.name }}
+
{{ wf_maker_project.name }}
@@ -83,67 +83,70 @@
-
- +
+
-
+
No records matched your search.
-
PLEASE ADD ITEMS TO THIS LIST
+
PLEASE ADD ITEMS TO THIS LIST
-
+
- +
-
{{ wf_maker_inventory_source.name }}
+
{{ wf_maker_inventory_source.name }}
-
- -
-
- -
- -
+
+
+ -
-
- -
- -
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
Please enter a number greater than or equal to 0.
-
+
-
+
{{:: strings.get('workflows.INVALID_JOB_TEMPLATE') }} @@ -154,101 +157,105 @@ {{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}
-
-
- -
- -
-
-
-
- {{:: strings.get('workflow_maker.READ_ONLY_PROMPT_VALUES')}} -
-
- {{:: strings.get('workflow_maker.READ_ONLY_NO_PROMPT_VALUES')}} -
-
-
{{:: strings.get('prompt.JOB_TYPE') }}
-
- {{:: strings.get('prompt.PLAYBOOK_RUN') }} - {{:: strings.get('prompt.CHECK') }} +
+
+ + {{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}
-
-
{{:: strings.get('prompt.INVENTORY') }}
-
-
-
-
{{:: strings.get('prompt.LIMIT') }}
-
-
-
-
{{:: strings.get('prompt.VERBOSITY') }}
-
-
-
-
- {{:: strings.get('prompt.JOB_TAGS') }}  - - - - +
+ +
+
-
-
-
- {{tag}} +
+
+
+ {{:: strings.get('workflow_maker.READ_ONLY_PROMPT_VALUES')}} +
+
+ {{:: strings.get('workflow_maker.READ_ONLY_NO_PROMPT_VALUES')}} +
+
+
{{:: strings.get('prompt.JOB_TYPE') }}
+
+ {{:: strings.get('prompt.PLAYBOOK_RUN') }} + {{:: strings.get('prompt.CHECK') }} +
+
+
+
{{:: strings.get('prompt.INVENTORY') }}
+
+
+
+
{{:: strings.get('prompt.LIMIT') }}
+
+
+
+
{{:: strings.get('prompt.VERBOSITY') }}
+
+
+
+
+ {{:: strings.get('prompt.JOB_TAGS') }}  + + + + +
+
+
+
+ {{tag}} +
-
-
-
- {{:: strings.get('prompt.SKIP_TAGS') }}  - - - - -
-
-
-
- {{tag}} +
+
+ {{:: strings.get('prompt.SKIP_TAGS') }}  + + + + +
+
+
+
+ {{tag}} +
@@ -256,8 +263,8 @@
{{:: strings.get('prompt.SHOW_CHANGES') }}
- {{:: strings.get('ON') }} - {{:: strings.get('OFF') }} + {{:: strings.get('ON') }} + {{:: strings.get('OFF') }}
@@ -269,16 +276,16 @@
+
+
+
+
+ + + + +
+
-
-
-
- - - - - -
-
\ No newline at end of file