From c98e7f6ecdef6d427320ae082e26bc575b1a8d36 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 8 Mar 2018 18:42:29 -0500 Subject: [PATCH] Implemented workflow node prompting --- awx/ui/client/src/templates/main.js | 375 +++----- .../src/templates/prompt/prompt.partial.html | 2 +- .../src/templates/prompt/prompt.service.js | 2 +- .../inventory/prompt-inventory.directive.js | 22 +- .../prompt-other-prompts.controller.js | 12 +- .../client/src/templates/templates.service.js | 9 + .../workflow-maker.controller.js | 901 +++++++++++------- .../workflow-maker.partial.html | 27 +- 8 files changed, 745 insertions(+), 605 deletions(-) diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index 144d597e2e..456c37fc78 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -37,7 +37,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p .config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider', function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) { let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow, - workflowMaker, inventoryLookup, credentialLookup, + workflowMaker, stateDefinitions = stateDefinitionsProvider.$get(), stateExtender = $stateExtenderProvider.$get(); @@ -401,8 +401,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p job_template_search: { value: { page_size: '5', - order_by: 'name', - inventory__isnull: false + order_by: 'name' }, squash: false, dynamic: true @@ -474,30 +473,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p name: row.name }; - $scope.templateSelected(row); + $scope.templateManuallySelected(row); } }); }; - $scope.$on('templateSelected', function(e, options) { - if(options.activeTab !== 'jobs') { + $scope.$watch('selectedTemplate', () => { + $scope.job_templates.forEach(function(row, i) { + if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { + $scope.job_templates[i].checked = 1; + } + else { + $scope.job_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; }); } - else { - if($scope.selectedTemplate){ - $scope.job_templates.forEach(function(row, i) { - if(row.id === $scope.selectedTemplate.id) { - $scope.job_templates[i].checked = 1; - } - else { - $scope.job_templates[i].checked = 0; - } - }); - } - } }); $scope.$on('clearWorkflowLists', function() { @@ -552,30 +550,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p name: row.name }; - $scope.templateSelected(row); + $scope.templateManuallySelected(row); } }); }; - $scope.$on('templateSelected', function(e, options) { - if(options.activeTab !== 'inventory_sync') { + $scope.$watch('selectedTemplate', () => { + $scope.workflow_inventory_sources.forEach(function(row, i) { + if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { + $scope.workflow_inventory_sources[i].checked = 1; + } + else { + $scope.workflow_inventory_sources[i].checked = 0; + } + }); + }); + + $scope.$watch('activeTab', () => { + if(!$scope.activeTab || $scope.activeTab !== "inventory_sync") { $scope.workflow_inventory_sources.forEach(function(row, i) { $scope.workflow_inventory_sources[i].checked = 0; }); } - else { - if($scope.selectedTemplate){ - $scope.workflow_inventory_sources.forEach(function(row, i) { - if(row.id === $scope.selectedTemplate.id) { - $scope.workflow_inventory_sources[i].checked = 1; - } - else { - $scope.workflow_inventory_sources[i].checked = 0; - } - }); - } - } }); $scope.$on('clearWorkflowLists', function() { @@ -630,30 +627,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p name: row.name }; - $scope.templateSelected(row); + $scope.templateManuallySelected(row); } }); }; - $scope.$on('templateSelected', function(e, options) { - if(options.activeTab !== 'project_sync') { + $scope.$watch('selectedTemplate', () => { + $scope.projects.forEach(function(row, i) { + if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) { + $scope.projects[i].checked = 1; + } + else { + $scope.projects[i].checked = 0; + } + }); + }); + + $scope.$watch('activeTab', () => { + if(!$scope.activeTab || $scope.activeTab !== "project_sync") { $scope.projects.forEach(function(row, i) { $scope.projects[i].checked = 0; }); } - else { - if($scope.selectedTemplate){ - $scope.projects.forEach(function(row, i) { - if(row.id === $scope.selectedTemplate.id) { - $scope.projects[i].checked = 1; - } - else { - $scope.projects[i].checked = 0; - } - }); - } - } }); $scope.$on('clearWorkflowLists', function() { @@ -663,69 +659,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p }); } ] - }, - 'workflowForm@templates.editWorkflowJobTemplate.workflowMaker': { - templateProvider: function(WorkflowMakerForm, GenerateForm) { - let form = WorkflowMakerForm(); - let html = GenerateForm.buildHTML(form, { - mode: 'add', - related: false, - noPanel: true - }); - return html; - }, - controller: ['$scope', '$timeout', 'CreateSelect2', - function($scope, $timeout, CreateSelect2) { - function resetPromptFields() { - $scope.credential = null; - $scope.credential_name = null; - $scope.inventory = null; - $scope.inventory_name = null; - $scope.job_type = null; - $scope.limit = null; - $scope.job_tags = null; - $scope.skip_tags = null; - } - - $scope.saveNodeForm = function(){ - // Gather up all of our form data - then let the main scope know what - // the new data is - - $scope.confirmNodeForm({ - skip_tags: $scope.skip_tags, - job_tags: $scope.job_tags, - limit: $scope.limit, - credential: $scope.credential, - credential_name: $scope.credential_name, - inventory: $scope.inventory, - inventory_name: $scope.inventory_name, - edgeType: $scope.edgeType, - job_type: $scope.job_type - }); - }; - - $scope.$on('templateSelected', function(e, options) { - - resetPromptFields(); - // Loop across the preset values and attach them to scope - _.forOwn(options.presetValues, function(value, key) { - $scope[key] = value; - }); - - // The default needs to be in place before we can select2-ify the dropdown - $timeout(function() { - CreateSelect2({ - element: '#workflow_maker_job_type', - multiple: false - }); - }); - }); - - $scope.$on('setEdgeType', function(e, edgeType) { - $scope.edgeType = edgeType; - }); - } - ] } }, resolve: { @@ -798,114 +731,114 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p } }; - inventoryLookup = { - searchPrefix: 'inventory', - name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory', - url: '/inventory', - data: { - formChildState: true - }, - params: { - inventory_search: { - value: { - page_size: '5' - }, - squash: true, - dynamic: true - } - }, - ncyBreadcrumb: { - skip: true - }, - views: { - 'related': { - templateProvider: function(ListDefinition, generateList) { - let list_html = generateList.build({ - mode: 'lookup', - list: ListDefinition, - input_type: 'radio' - }); - return `${list_html}`; - - } - } - }, - resolve: { - ListDefinition: ['InventoryList', function(InventoryList) { - // mutate the provided list definition here - let list = _.cloneDeep(InventoryList); - list.lookupConfirmText = 'SELECT'; - return list; - }], - Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', - (list, qs, $stateParams, GetBasePath) => { - let path = GetBasePath(list.name) || GetBasePath(list.basePath); - return qs.search(path, $stateParams[`${list.iterator}_search`]); - } - ] - }, - onExit: function($state) { - if ($state.transition) { - $('#form-modal').modal('hide'); - $('.modal-backdrop').remove(); - $('body').removeClass('modal-open'); - } - }, - }; - - credentialLookup = { - searchPrefix: 'credential', - name: 'templates.editWorkflowJobTemplate.workflowMaker.credential', - url: '/credential', - data: { - formChildState: true - }, - params: { - credential_search: { - value: { - page_size: '5' - }, - squash: true, - dynamic: true - } - }, - ncyBreadcrumb: { - skip: true - }, - views: { - 'related': { - templateProvider: function(ListDefinition, generateList) { - let list_html = generateList.build({ - mode: 'lookup', - list: ListDefinition, - input_type: 'radio' - }); - return `${list_html}`; - - } - } - }, - resolve: { - ListDefinition: ['CredentialList', function(CredentialList) { - let list = _.cloneDeep(CredentialList); - list.lookupConfirmText = 'SELECT'; - return list; - }], - Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', - (list, qs, $stateParams, GetBasePath) => { - let path = GetBasePath(list.name) || GetBasePath(list.basePath); - return qs.search(path, $stateParams[`${list.iterator}_search`]); - } - ] - }, - onExit: function($state) { - if ($state.transition) { - $('#form-modal').modal('hide'); - $('.modal-backdrop').remove(); - $('body').removeClass('modal-open'); - } - }, - }; + // inventoryLookup = { + // searchPrefix: 'inventory', + // name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory', + // url: '/inventory', + // data: { + // formChildState: true + // }, + // params: { + // inventory_search: { + // value: { + // page_size: '5' + // }, + // squash: true, + // dynamic: true + // } + // }, + // ncyBreadcrumb: { + // skip: true + // }, + // views: { + // 'related': { + // templateProvider: function(ListDefinition, generateList) { + // let list_html = generateList.build({ + // mode: 'lookup', + // list: ListDefinition, + // input_type: 'radio' + // }); + // return `${list_html}`; + // + // } + // } + // }, + // resolve: { + // ListDefinition: ['InventoryList', function(InventoryList) { + // // mutate the provided list definition here + // let list = _.cloneDeep(InventoryList); + // list.lookupConfirmText = 'SELECT'; + // return list; + // }], + // Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', + // (list, qs, $stateParams, GetBasePath) => { + // let path = GetBasePath(list.name) || GetBasePath(list.basePath); + // return qs.search(path, $stateParams[`${list.iterator}_search`]); + // } + // ] + // }, + // onExit: function($state) { + // if ($state.transition) { + // $('#form-modal').modal('hide'); + // $('.modal-backdrop').remove(); + // $('body').removeClass('modal-open'); + // } + // }, + // }; + // + // credentialLookup = { + // searchPrefix: 'credential', + // name: 'templates.editWorkflowJobTemplate.workflowMaker.credential', + // url: '/credential', + // data: { + // formChildState: true + // }, + // params: { + // credential_search: { + // value: { + // page_size: '5' + // }, + // squash: true, + // dynamic: true + // } + // }, + // ncyBreadcrumb: { + // skip: true + // }, + // views: { + // 'related': { + // templateProvider: function(ListDefinition, generateList) { + // let list_html = generateList.build({ + // mode: 'lookup', + // list: ListDefinition, + // input_type: 'radio' + // }); + // return `${list_html}`; + // + // } + // } + // }, + // resolve: { + // ListDefinition: ['CredentialList', function(CredentialList) { + // let list = _.cloneDeep(CredentialList); + // list.lookupConfirmText = 'SELECT'; + // return list; + // }], + // Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', + // (list, qs, $stateParams, GetBasePath) => { + // let path = GetBasePath(list.name) || GetBasePath(list.basePath); + // return qs.search(path, $stateParams[`${list.iterator}_search`]); + // } + // ] + // }, + // onExit: function($state) { + // if ($state.transition) { + // $('#form-modal').modal('hide'); + // $('.modal-backdrop').remove(); + // $('body').removeClass('modal-open'); + // } + // }, + // }; return Promise.all([ @@ -920,8 +853,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p }, [ stateExtender.buildDefinition(listRoute), stateExtender.buildDefinition(workflowMaker), - stateExtender.buildDefinition(inventoryLookup), - stateExtender.buildDefinition(credentialLookup) + // stateExtender.buildDefinition(inventoryLookup), + // stateExtender.buildDefinition(credentialLookup) ]) }; }); diff --git a/awx/ui/client/src/templates/prompt/prompt.partial.html b/awx/ui/client/src/templates/prompt/prompt.partial.html index 8a99a5f7b0..87e3f83a08 100644 --- a/awx/ui/client/src/templates/prompt/prompt.partial.html +++ b/awx/ui/client/src/templates/prompt/prompt.partial.html @@ -1,5 +1,5 @@
- + {{:: vm.strings.get('prompt.INVENTORY') }} {{:: vm.strings.get('prompt.CREDENTIAL') }} diff --git a/awx/ui/client/src/templates/prompt/prompt.service.js b/awx/ui/client/src/templates/prompt/prompt.service.js index 9090bfda05..5ab5002c27 100644 --- a/awx/ui/client/src/templates/prompt/prompt.service.js +++ b/awx/ui/client/src/templates/prompt/prompt.service.js @@ -19,7 +19,7 @@ function PromptService (Empty, $filter) { let skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : ""); let jobTags = _.has(params, 'currentValues.job_tags') && params.currentValues.job_tags ? params.currentValues.job_tags : (_.has(params, 'launchConf.defaults.job_tags') ? params.launchConf.defaults.job_tags : ""); - prompts.variables.value = _.has(params, 'launchConf.defaults.extra_vars') ? params.launchConf.defaults.extra_vars : "---"; + prompts.variables.value = _.has(params, 'launchConf.defaults.extra_vars') && params.launchConf.defaults.extra_vars !== "" ? params.launchConf.defaults.extra_vars : "---"; prompts.verbosity.choices = _.get(params, 'launchOptions.actions.POST.verbosity.choices', []).map(c => ({label: c[1], value: c[0]})); prompts.verbosity.value = _.has(params, 'currentValues.verbosity') && params.currentValues.verbosity ? _.find(prompts.verbosity.choices, item => item.value === params.currentValues.verbosity) : _.find(prompts.verbosity.choices, item => item.value === params.launchConf.defaults.verbosity); prompts.jobType.choices = _.get(params, 'launchOptions.actions.POST.job_type.choices', []).map(c => ({label: c[1], value: c[0]})); diff --git a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js index 826981c189..e3b1d24823 100644 --- a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js +++ b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js @@ -54,17 +54,17 @@ export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$com $('#prompt-inventory').append($compile(html)(scope)); scope.$watch('promptData.prompts.inventory.value', () => { - if(scope.promptData.prompts.inventory.value && scope.promptData.prompts.inventory.value.id) { - // Loop across the inventories and see if one of them should be "checked" - scope.inventories.forEach((row, i) => { - if (row.id === scope.promptData.prompts.inventory.value.id) { - scope.inventories[i].checked = 1; - } - else { - scope.inventories[i].checked = 0; - } - }); - } + scope.inventories.forEach((row, i) => { + if ( + _.has(scope, 'promptData.prompts.inventory.value.id') && + row.id === scope.promptData.prompts.inventory.value.id + ) { + scope.inventories[i].checked = 1; + } + else { + scope.inventories[i].checked = 0; + } + }); }); }); } diff --git a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js index 127c412d88..96a087218d 100644 --- a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js @@ -5,7 +5,7 @@ *************************************************/ export default - ['ParseTypeChange', 'CreateSelect2', 'TemplatesStrings', function(ParseTypeChange, CreateSelect2, strings) { + ['ParseTypeChange', 'CreateSelect2', 'TemplatesStrings', '$timeout', function(ParseTypeChange, CreateSelect2, strings, $timeout) { const vm = this; vm.strings = strings; @@ -30,10 +30,12 @@ export default let codemirrorExtraVars = () => { if(scope.promptData.launchConf.ask_variables_on_launch && !scope.promptData.prompts.variables.ignore) { - ParseTypeChange({ - scope: scope, - variable: 'extraVariables', - field_id: 'job_launch_variables' + $timeout(() => { + ParseTypeChange({ + scope: scope, + variable: 'extraVariables', + field_id: 'job_launch_variables' + }); }); } }; diff --git a/awx/ui/client/src/templates/templates.service.js b/awx/ui/client/src/templates/templates.service.js index cc322a0fb1..1a63cb6553 100644 --- a/awx/ui/client/src/templates/templates.service.js +++ b/awx/ui/client/src/templates/templates.service.js @@ -281,6 +281,15 @@ export default ['Rest', 'GetBasePath', '$q', 'NextPage', function(Rest, GetBaseP }); return deferred.promise; + }, + postWorkflowNodeCredential: function(params) { + // params.id + // params.data + + var url = GetBasePath('workflow_job_template_nodes') + params.id + '/credentials'; + + Rest.setUrl(url); + return Rest.post(params.data); } }; }]; 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 9bbe6e8996..eac600ec93 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -5,11 +5,14 @@ *************************************************/ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', - '$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q', + '$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q', 'JobTemplateModel', + 'Empty', 'PromptService', 'Rest', function($scope, WorkflowService, GetBasePath, TemplatesService, $state, - ProcessErrors, CreateSelect2, WorkflowMakerForm, $q) { + ProcessErrors, CreateSelect2, WorkflowMakerForm, $q, JobTemplate, + Empty, PromptService, Rest) { let form = WorkflowMakerForm(); + let promptWatcher; $scope.workflowMakerFormConfig = { nodeMode: "idle", @@ -26,80 +29,39 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }]; $scope.edgeFlags = { - conflict: false, - typeRestriction: null, - showTypeOptions: false + conflict: false }; - $scope.editRequests = []; - $scope.associateRequests = []; - $scope.disassociateRequests = []; + $scope.edgeTypeOptions = [ + { + label: 'Always', + value: 'always' + }, + { + label: 'On Success', + value: 'success' + }, + { + label: 'On Failure', + value: 'failure' + } + ]; + + let editRequests = []; + let associateRequests = []; + let disassociateRequests = []; + let credentialRequests = []; $scope.showKey = false; $scope.toggleKey = () => $scope.showKey = !$scope.showKey; $scope.keyClassList = `{ 'Key-menuIcon--active': showKey }`; - function init() { - - let allNodes = []; - let page = 1; - - let buildTreeFromNodes = function(){ - WorkflowService.buildTree({ - workflowNodes: allNodes - }).then(function(data){ - $scope.treeData = data; - - // TODO: I think that the workflow chart directive (and eventually d3) is meddling with - // this treeData object and removing the children object for some reason (?) - // This happens on occasion and I think is a race condition (?) - if(!$scope.treeData.data.children) { - $scope.treeData.data.children = []; - } - - $scope.treeData.workflow_job_template_obj = $scope.workflowJobTemplateObj; - - $scope.treeDataMaster = angular.copy($scope.treeData.data); - $scope.showManualControls = false; - }); - }; - - let getNodes = function(){ - // Get the workflow nodes - TemplatesService.getWorkflowJobTemplateNodes($scope.workflowJobTemplateObj.id, page) - .then(function(data){ - for(var i=0; i a.value).sort())) ? null : params.node.promptData.prompts.tags.value.map(a => a.value).join(); } - if(params.node.unifiedJobTemplate.ask_job_type_on_launch) { - sendableNodeData.job_type = !params.node.promptValues.job_type || params.node.unifiedJobTemplate.job_type !== params.node.promptValues.job_type ? params.node.promptValues.job_type : null; + if(_.has(params, 'node.promptData.prompts.skipTags.value') && _.get(params, 'node.promptData.launchConf.ask_skip_tags_on_launch')){ + let templateDefaultSkipTags = params.node.promptData.prompts.skipTags.templateDefault.split(','); + sendableNodeData.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), params.node.promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : params.node.promptData.prompts.skipTags.value.map(a => a.value).join(); } - if(params.node.unifiedJobTemplate.ask_tags_on_launch) { - sendableNodeData.job_tags = !params.node.promptValues.job_tags || params.node.unifiedJobTemplate.job_tags !== params.node.promptValues.job_tags ? params.node.promptValues.job_tags : null; + if(_.has(params, 'node.promptData.prompts.limit.value') && _.get(params, 'node.promptData.launchConf.ask_limit_on_launch')){ + sendableNodeData.limit = params.node.promptData.prompts.limit.templateDefault === params.node.promptData.prompts.limit.value ? null : params.node.promptData.prompts.limit.value; } - if(params.node.unifiedJobTemplate.ask_skip_tags_on_launch) { - sendableNodeData.skip_tags = !params.node.promptValues.skip_tags || params.node.unifiedJobTemplate.skip_tags !== params.node.promptValues.skip_tags ? params.node.promptValues.skip_tags : null; + if(_.has(params, 'node.promptData.prompts.verbosity.value.value') && _.get(params, 'node.promptData.launchConf.ask_verbosity_on_launch')){ + sendableNodeData.verbosity = params.node.promptData.prompts.verbosity.templateDefault === params.node.promptData.prompts.verbosity.value.value ? null : params.node.promptData.prompts.verbosity.value.value; + } + if(_.has(params, 'node.promptData.prompts.inventory.value') && _.get(params, 'node.promptData.launchConf.ask_inventory_on_launch')){ + sendableNodeData.inventory = _.has(params, 'node.promptData.prompts.inventory.templateDefault.id') && params.node.promptData.prompts.inventory.templateDefault.id === params.node.promptData.prompts.inventory.value.id ? null : params.node.promptData.prompts.inventory.value.id; + } + if(_.has(params, 'node.promptData.prompts.diffMode.value') && _.get(params, 'node.promptData.launchConf.ask_diff_mode_on_launch')){ + sendableNodeData.diff_mode = params.node.promptData.prompts.diffMode.templateDefault === params.node.promptData.prompts.diffMode.value ? null : params.node.promptData.prompts.diffMode.value; } } @@ -184,13 +179,33 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', .then(function(data) { if(!params.node.isRoot) { - $scope.associateRequests.push({ + associateRequests.push({ parentId: params.parentId, nodeId: data.data.id, edge: params.node.edgeType }); } + if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + // This finds the credentials that were selected in the prompt but don't occur + // in the template defaults + let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { + let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : []; + return !defaultCreds.some(function(defaultCred) { + return credFromPrompt.id === defaultCred.id; + }); + }); + + credentialsToPost.forEach((credentialToPost) => { + credentialRequests.push({ + id: data.data.id, + data: { + id: credentialToPost.id + } + }); + }); + } + params.node.isNew = false; continueRecursing(data.data.id); }, function(error) { @@ -207,11 +222,51 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', if(params.node.edited) { - $scope.editRequests.push({ + editRequests.push({ id: params.node.nodeId, data: buildSendableNodeData() }); + if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){ + let credentialsNotInPriorCredentials = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) { + let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : []; + return !defaultCreds.some(function(defaultCred) { + return credFromPrompt.id === defaultCred.id; + }); + }); + + let credentialsToAdd = credentialsNotInPriorCredentials.filter(function(credNotInPrior) { + return !params.node.promptData.prompts.credentials.previousOverrides.some(function(priorCred) { + return credNotInPrior.id === priorCred.id; + }); + }); + + let credentialsToRemove = params.node.promptData.prompts.credentials.previousOverrides.filter(function(priorCred) { + return !credentialsNotInPriorCredentials.some(function(credNotInPrior) { + return priorCred.id === credNotInPrior.id; + }); + }); + + credentialsToAdd.forEach((credentialToAdd) => { + credentialRequests.push({ + id: params.node.nodeId, + data: { + id: credentialToAdd.id + } + }); + }); + + credentialsToRemove.forEach((credentialToRemove) => { + credentialRequests.push({ + id: params.node.nodeId, + data: { + id: credentialToRemove.id, + disassociate: true + } + }); + }); + } + } if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep @@ -225,7 +280,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); if(!parentIsDeleted) { - $scope.disassociateRequests.push({ + disassociateRequests.push({ parentId: params.node.originalParentId, nodeId: params.node.nodeId, edge: params.node.originalEdge @@ -236,7 +291,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', // If we don't have a parent then this is a root node // and the act of disassociating will make it a root node if(params.parentId) { - $scope.associateRequests.push({ + associateRequests.push({ parentId: params.parentId, nodeId: params.node.nodeId, edge: params.node.edgeType @@ -246,7 +301,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } else if(!params.node.originalParentId && params.parentId) { // This used to be a root node but is now not a root node - $scope.associateRequests.push({ + associateRequests.push({ parentId: params.parentId, nodeId: params.node.nodeId, edge: params.node.edgeType @@ -259,12 +314,68 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } } - $scope.lookUpInventory = function(){ - $state.go('.inventory'); + let updateEdgeDropdownOptions = (optionsToInclude) => { + // Not passing optionsToInclude will include all by default + if(!optionsToInclude) { + $scope.edgeTypeOptions = [ + { + label: 'Always', + value: 'always' + }, + { + label: 'On Success', + value: 'success' + }, + { + label: 'On Failure', + value: 'failure' + } + ]; + } else { + $scope.edgeTypeOptions = []; + + optionsToInclude.forEach((optionToInclude) => { + if(optionToInclude === "always") { + $scope.edgeTypeOptions.push({ + label: 'Always', + value: 'always' + }); + } else if(optionToInclude === "success") { + $scope.edgeTypeOptions.push({ + label: 'On Success', + value: 'success' + }); + } else if(optionToInclude === "failure") { + $scope.edgeTypeOptions.push({ + label: 'On Failure', + value: 'failure' + }); + } + }); + } + + CreateSelect2({ + element: '#workflow_node_edge', + multiple: false + }); }; - $scope.lookUpCredential = function(){ - $state.go('.credential'); + let watchForPromptChanges = () => { + let promptDataToWatch = [ + 'promptData.prompts.inventory.value', + 'promptData.prompts.verbosity.value', + 'missingSurveyValue' + ]; + + promptWatcher = $scope.$watchGroup(promptDataToWatch, function() { + let missingPromptValue = false; + if($scope.missingSurveyValue) { + missingPromptValue = true; + } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) { + missingPromptValue = true; + } + $scope.promptModalMissingReqFields = missingPromptValue; + }); }; $scope.closeWorkflowMaker = function() { @@ -280,7 +391,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) { let completionCallback = function() { - let disassociatePromises = $scope.disassociateRequests.map(function(request) { + let disassociatePromises = disassociateRequests.map(function(request) { return TemplatesService.disassociateWorkflowNode({ parentId: request.parentId, nodeId: request.nodeId, @@ -288,7 +399,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }); - let editNodePromises = $scope.editRequests.map(function(request) { + let credentialPromises = credentialRequests.map(function(request) { + return TemplatesService.postWorkflowNodeCredential({ + id: request.id, + data: request.data + }); + }); + + let editNodePromises = editRequests.map(function(request) { return TemplatesService.editWorkflowNode({ id: request.id, data: request.data @@ -299,10 +417,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', return TemplatesService.deleteWorkflowJobTemplateNode(nodeId); }); - $q.all(disassociatePromises.concat(editNodePromises, deletePromises)) + $q.all(disassociatePromises.concat(editNodePromises, deletePromises, credentialPromises)) .then(function() { - let associatePromises = $scope.associateRequests.map(function(request) { + let associatePromises = associateRequests.map(function(request) { return TemplatesService.associateWorkflowNode({ parentId: request.parentId, nodeId: request.nodeId, @@ -364,59 +482,41 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); // Set the default to success - let edgeType = "success"; + let edgeType = {label: "On Success", value: "success"}; if (parent && ((betweenTwoNodes && parent.source.isStartNode) || (!betweenTwoNodes && parent.isStartNode))) { // We don't want to give the user the option to select // a type as this node will always be executed - edgeType = "always"; - $scope.edgeFlags.showTypeOptions = false; + updateEdgeDropdownOptions(["always"]); + edgeType = {label: "Always", value: "always"}; } else { - if ($scope.placeholderNode.edgeConflict) { - // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving - $scope.edgeFlags.typeRestriction = null; - } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { - $scope.edgeFlags.typeRestriction = "successFailure"; - edgeType = "success"; + if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { + updateEdgeDropdownOptions(["success", "failure"]); + edgeType = {label: "On Success", value: "success"}; } else if (_.includes(siblingConnectionTypes, "always")) { - $scope.edgeFlags.typeRestriction = "always"; - edgeType = "always"; + updateEdgeDropdownOptions(["always"]); + edgeType = {label: "Always", value: "always"}; } else { - $scope.edgeFlags.typeRestriction = null; + updateEdgeDropdownOptions(); } - - $scope.edgeFlags.showTypeOptions = true; } // Reset the edgeConflict flag resetEdgeConflict(); - $scope.$broadcast("setEdgeType", edgeType); + $scope.edgeType = edgeType; $scope.$broadcast("refreshWorkflowChart"); }; - $scope.confirmNodeForm = function(formValues) { + $scope.confirmNodeForm = function() { if ($scope.workflowMakerFormConfig.nodeMode === "add") { - if ($scope.selectedTemplate && formValues.edgeType) { + if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) { $scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate; - $scope.placeholderNode.edgeType = formValues.edgeType; + $scope.placeholderNode.edgeType = $scope.edgeType.value; if ($scope.placeholderNode.unifiedJobTemplate.type === 'job_template') { - $scope.placeholderNode.promptValues = { - credential: { - id: formValues.credential, - name: formValues.credential_name - }, - inventory: { - id: formValues.inventory, - name: formValues.inventory_name - }, - limit: formValues.limit, - job_type: formValues.job_type && formValues.job_type.value ? formValues.job_type.value : null, - job_tags: formValues.job_tags, - skip_tags: formValues.skip_tags - }; + $scope.placeholderNode.promptData = _.cloneDeep($scope.promptData); } $scope.placeholderNode.canEdit = true; @@ -429,25 +529,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } } else if ($scope.workflowMakerFormConfig.nodeMode === "edit") { - if ($scope.selectedTemplate && formValues.edgeType) { + if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) { $scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate; - $scope.nodeBeingEdited.edgeType = formValues.edgeType; + $scope.nodeBeingEdited.edgeType = $scope.edgeType.value; if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template') { - $scope.nodeBeingEdited.promptValues = { - credential: { - id: formValues.credential, - name: formValues.credential_name - }, - inventory: { - id: formValues.inventory, - name: formValues.inventory_name - }, - limit: formValues.limit, - job_type: formValues.job_type && formValues.job_type.value ? formValues.job_type.value : null, - job_tags: formValues.job_tags, - skip_tags: formValues.skip_tags - }; + $scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData); } $scope.nodeBeingEdited.isActiveEdit = false; @@ -458,6 +545,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } } + if(promptWatcher) { + promptWatcher(); + } + + $scope.promptData = null; + // Reset the edgeConflict flag resetEdgeConflict(); @@ -475,6 +568,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.nodeBeingEdited.isActiveEdit = false; } + if(promptWatcher) { + promptWatcher(); + } + + $scope.promptData = null; + // Reset the edgeConflict flag resetEdgeConflict(); @@ -515,90 +614,123 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', let finishConfiguringEdit = function() { - let formValues = {}; + let jobTemplate = new JobTemplate(); - if($scope.nodeBeingEdited.unifiedJobTemplate){ - // build any prompt values - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch) { - if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.credential) { - formValues.credential_name = $scope.nodeBeingEdited.promptValues.credential.name; - formValues.credential = $scope.nodeBeingEdited.promptValues.credential.id; - } else if ($scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential) { - formValues.credential_name = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.name ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.name : null; - formValues.credential = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.id ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.id : null; - } else { - formValues.credential_name = null; - formValues.credential = null; - } - } + Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials); - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch) { - if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.inventory) { - formValues.inventory_name = $scope.nodeBeingEdited.promptValues.inventory.name; - formValues.inventory = $scope.nodeBeingEdited.promptValues.inventory.id; - } else if ($scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory) { - formValues.inventory_name = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.name ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.name : null; - formValues.inventory = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.id ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.id : null; - } else { - formValues.inventory_name = null; - formValues.inventory = null; - } - } + if($scope.nodeBeingEdited.promptData) { + $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData); + }else if($scope.nodeBeingEdited.unifiedJobTemplate){ + $q.all([jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), Rest.get()]) + .then((responses) => { + let launchOptions = responses[0].data, + launchConf = responses[1].data, + workflowNodeCredentials = responses[2].data.results; - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_job_type_on_launch) { - if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.job_type) { - formValues.job_type = { - value: $scope.nodeBeingEdited.promptValues.job_type + let prompts = PromptService.processPromptValues({ + launchConf: responses[1].data, + launchOptions: responses[0].data, + currentValues: $scope.nodeBeingEdited.originalNodeObj + }); + + let defaultCredsWithoutOverrides = []; + + prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials); + + const credentialHasScheduleOverride = (templateDefaultCred) => { + let credentialHasOverride = false; + workflowNodeCredentials.forEach((scheduleCred) => { + if(templateDefaultCred.credential_type === scheduleCred.credential_type) { + if( + (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) || + (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id) + ) { + credentialHasOverride = true; + } + } + }); + + return credentialHasOverride; }; - } else if ($scope.nodeBeingEdited.originalNodeObj.job_type) { - formValues.job_type = { - value: $scope.nodeBeingEdited.originalNodeObj.job_type - }; - } else if ($scope.nodeBeingEdited.unifiedJobTemplate.job_type) { - formValues.job_type = { - value: $scope.nodeBeingEdited.unifiedJobTemplate.job_type - }; - } else { - formValues.job_type = { - value: null - }; - } - } + if(_.has(launchConf, 'defaults.credentials')) { + launchConf.defaults.credentials.forEach((defaultCred) => { + if(!credentialHasScheduleOverride(defaultCred)) { + defaultCredsWithoutOverrides.push(defaultCred); + } + }); + } - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_limit_on_launch) { - if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.limit === 'string') { - formValues.limit = $scope.nodeBeingEdited.promptValues.limit; - } else if (typeof $scope.nodeBeingEdited.originalNodeObj.limit === 'string') { - formValues.limit = $scope.nodeBeingEdited.originalNodeObj.limit; - } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.limit === 'string') { - formValues.limit = $scope.nodeBeingEdited.unifiedJobTemplate.limit; - } else { - formValues.limit = null; - } - } - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_skip_tags_on_launch) { - if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.skip_tags === 'string') { - formValues.skip_tags = $scope.nodeBeingEdited.promptValues.skip_tags; - } else if (typeof $scope.nodeBeingEdited.originalNodeObj.skip_tags === 'string') { - formValues.skip_tags = $scope.nodeBeingEdited.originalNodeObj.skip_tags; - } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.skip_tags === 'string') { - formValues.skip_tags = $scope.nodeBeingEdited.unifiedJobTemplate.skip_tags; - } else { - formValues.skip_tags = null; - } - } - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_tags_on_launch) { - if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.job_tags === 'string') { - formValues.job_tags = $scope.nodeBeingEdited.promptValues.job_tags; - } else if (typeof $scope.nodeBeingEdited.originalNodeObj.job_tags === 'string') { - formValues.job_tags = $scope.nodeBeingEdited.originalNodeObj.job_tags; - } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.job_tags === 'string') { - formValues.job_tags = $scope.nodeBeingEdited.unifiedJobTemplate.job_tags; - } else { - formValues.job_tags = null; - } - } + prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides); + + 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.survey_enabled && + !launchConf.credential_needed_to_start && + !launchConf.inventory_needed_to_start && + launchConf.passwords_needed_to_start.length === 0 && + launchConf.variables_needed_to_start.length === 0) { + $scope.showPromptButton = 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; + } + + if(responses[1].data.survey_enabled) { + // go out and get the survey questions + jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id) + .then((surveyQuestionRes) => { + + let processed = PromptService.processSurveyQuestions({ + surveyQuestions: surveyQuestionRes.data.spec, + extra_data: $scope.nodeBeingEdited.originalNodeObj.extra_data + }); + + $scope.missingSurveyValue = processed.missingSurveyValue; + + $scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data); + + $scope.promptData = { + launchConf: launchConf, + launchOptions: launchOptions, + prompts: prompts, + surveyQuestions: surveyQuestionRes.data.spec, + template: $scope.nodeBeingEdited.unifiedJobTemplate.id + }; + + $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: launchConf, + launchOptions: launchOptions, + prompts: prompts, + template: $scope.nodeBeingEdited.unifiedJobTemplate.id + }; + watchForPromptChanges(); + } + } + }); if ($scope.nodeBeingEdited.unifiedJobTemplate.type === "job_template") { $scope.workflowMakerFormConfig.activeTab = "jobs"; @@ -641,31 +773,30 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', childId: nodeToEdit.id }); - if (parent && parent.isStartNode) { - // We don't want to give the user the option to select - // a type as this node will always be executed - $scope.edgeFlags.showTypeOptions = false; - } else { - if (nodeToEdit.edgeConflict) { - // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving - $scope.edgeFlags.typeRestriction = null; - } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { - $scope.edgeFlags.typeRestriction = "successFailure"; - } else if (_.includes(siblingConnectionTypes, "always")) { - $scope.edgeFlags.typeRestriction = "always"; - } else { - $scope.edgeFlags.typeRestriction = null; - } + let edgeDropdownOptions = null; - $scope.edgeFlags.showTypeOptions = true; - } + switch($scope.nodeBeingEdited.edgeType) { + case "always": + $scope.edgeType = {label: "Always", value: "always"}; + if(siblingConnectionTypes.length === 0 || (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always"))) { + edgeDropdownOptions = ["always"]; + } + break; + case "success": + $scope.edgeType = {label: "On Success", value: "success"}; + if(siblingConnectionTypes.length === 0 || (!_.includes(siblingConnectionTypes, "always"))) { + edgeDropdownOptions = ["success", "failure"]; + } + break; + case "failure": + $scope.edgeType = {label: "On Failure", value: "failure"}; + if(siblingConnectionTypes.length === 0 || (!_.includes(siblingConnectionTypes, "always"))) { + edgeDropdownOptions = ["success", "failure"]; + } + break; + } - $scope.$broadcast('setEdgeType', $scope.nodeBeingEdited.edgeType); - - $scope.$broadcast('templateSelected', { - presetValues: formValues, - activeTab: $scope.workflowMakerFormConfig.activeTab - }); + updateEdgeDropdownOptions(edgeDropdownOptions); $scope.$broadcast("refreshWorkflowChart"); }; @@ -679,47 +810,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', TemplatesService.getUnifiedJobTemplate($scope.nodeBeingEdited.unifiedJobTemplate.id) .then(function(data) { - $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]); - - let defers = []; - let retrievingCredential = false; - let retrievingInventory = false; - - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch && $scope.nodeBeingEdited.originalNodeObj.credential) { - defers.push(TemplatesService.getCredential($scope.nodeBeingEdited.originalNodeObj.credential)); - retrievingCredential = true; - } - - if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch && $scope.nodeBeingEdited.originalNodeObj.inventory) { - defers.push(TemplatesService.getInventory($scope.nodeBeingEdited.originalNodeObj.inventory)); - retrievingInventory = true; - } - - $q.all(defers) - .then(function(responses) { - if (retrievingCredential) { - $scope.nodeBeingEdited.promptValues.credential = { - name: responses[0].data.name, - id: responses[0].data.id - }; - - if (retrievingInventory) { - $scope.nodeBeingEdited.promptValues.inventory = { - name: responses[1].data.name, - id: responses[1].data.id - }; - } - } else if (retrievingInventory) { - $scope.nodeBeingEdited.promptValues.inventory = { - name: responses[0].data.name, - id: responses[0].data.id - }; - } - finishConfiguringEdit(); - }); - - + finishConfiguringEdit(); }, function(error) { ProcessErrors($scope, error.data, error.status, form, { hdr: 'Error!', @@ -779,7 +871,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', if($scope.placeholderNode) { let edgeType = "success"; if($scope.placeholderNode.isRoot) { - $scope.edgeFlags.showTypeOptions = false; edgeType = "always"; } else { @@ -790,49 +881,45 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', childId: $scope.placeholderNode.id }); - if ($scope.placeholderNode.edgeConflict) { - // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving - $scope.edgeFlags.typeRestriction = null; - } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { - $scope.edgeFlags.typeRestriction = "successFailure"; + if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { + updateEdgeDropdownOptions(["success", "failure"]); } else if (_.includes(siblingConnectionTypes, "always")) { - $scope.edgeFlags.typeRestriction = "always"; + updateEdgeDropdownOptions(["always"]); edgeType = "always"; } else { - $scope.edgeFlags.typeRestriction = null; + updateEdgeDropdownOptions(); } - $scope.edgeFlags.showTypeOptions = true; - } - $scope.$broadcast("setEdgeType", edgeType); + $scope.edgeType = edgeType; + // $scope.$broadcast("setEdgeType", edgeType); } else if($scope.nodeBeingEdited) { - if($scope.nodeBeingEdited.isRoot) { - $scope.edgeFlags.showTypeOptions = false; + let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ + tree: $scope.treeData.data, + parentId: $scope.nodeBeingEdited.parent.id, + childId: $scope.nodeBeingEdited.id + }); + + if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { + updateEdgeDropdownOptions(["success", "failure"]); + } else if (_.includes(siblingConnectionTypes, "always") && $scope.nodeBeingEdited.edgeType === "always") { + updateEdgeDropdownOptions(["always"]); + } else { + updateEdgeDropdownOptions(); } - else { - let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({ - tree: $scope.treeData.data, - parentId: $scope.nodeBeingEdited.parent.id, - childId: $scope.nodeBeingEdited.id - }); - if ($scope.nodeBeingEdited.edgeConflict) { - // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving - $scope.edgeFlags.typeRestriction = null; - } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) { - $scope.edgeFlags.typeRestriction = "successFailure"; - } else if (_.includes(siblingConnectionTypes, "always") && $scope.nodeBeingEdited.edgeType === "always") { - $scope.edgeFlags.typeRestriction = "always"; - } else { - $scope.edgeFlags.typeRestriction = null; - } - - $scope.edgeFlags.showTypeOptions = true; - - } - $scope.$broadcast("setEdgeType", $scope.nodeBeingEdited.edgeType); + switch($scope.nodeBeingEdited.edgeType) { + case "always": + $scope.edgeType = {label: "Always", value: "always"}; + break; + case "success": + $scope.edgeType = {label: "On Success", value: "success"}; + break; + case "failure": + $scope.edgeType = {label: "On Failure", value: "failure"}; + break; + } } $scope.treeData.data.totalNodes--; @@ -846,63 +933,93 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } }; - $scope.templateSelected = function(selectedTemplate) { + $scope.templateManuallySelected = function(selectedTemplate) { $scope.selectedTemplate = angular.copy(selectedTemplate); - let formValues = {}; + if(selectedTemplate.type === "job_template") { + let jobTemplate = new JobTemplate(); - if ($scope.selectedTemplate.ask_credential_on_launch) { - if ($scope.selectedTemplate.summary_fields.credential) { - formValues.credential_name = $scope.selectedTemplate.summary_fields.credential.name ? $scope.selectedTemplate.summary_fields.credential.name : null; - formValues.credential = $scope.selectedTemplate.summary_fields.credential.id ? $scope.selectedTemplate.summary_fields.credential.id : null; - } else { - formValues.credential_name = null; - formValues.credential = null; - } + $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)]) + .then((responses) => { + let launchConf = responses[1].data; + + 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.survey_enabled && + !launchConf.credential_needed_to_start && + !launchConf.inventory_needed_to_start && + launchConf.passwords_needed_to_start.length === 0 && + launchConf.variables_needed_to_start.length === 0) { + $scope.showPromptButton = false; + } else { + $scope.showPromptButton = true; + + 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, + prompts: PromptService.processPromptValues({ + launchConf: responses[1].data, + launchOptions: responses[0].data + }), + }; + + $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, + prompts: PromptService.processPromptValues({ + launchConf: responses[1].data, + launchOptions: responses[0].data + }), + }; + + watchForPromptChanges(); + } + } + }); + } else { + // TODO - clear out prompt data? + $scope.showPromptButton = false; } - - if ($scope.selectedTemplate.ask_inventory_on_launch) { - if ($scope.selectedTemplate.summary_fields.inventory) { - formValues.inventory_name = $scope.selectedTemplate.summary_fields.inventory.name ? $scope.selectedTemplate.summary_fields.inventory.name : null; - formValues.inventory = $scope.selectedTemplate.summary_fields.inventory.id ? $scope.selectedTemplate.summary_fields.inventory.id : null; - } else { - formValues.inventory_name = null; - formValues.inventory = null; - } - } - - if ($scope.selectedTemplate.ask_job_type_on_launch) { - formValues.job_type = { - value: $scope.selectedTemplate.job_type ? $scope.selectedTemplate.job_type : null - }; - - // The default needs to be in place before we can select2-ify the dropdown - CreateSelect2({ - element: '#workflow_maker_job_type', - multiple: false - }); - } - - if ($scope.selectedTemplate.ask_limit_on_launch) { - formValues.limit = $scope.selectedTemplate.limit ? $scope.selectedTemplate.limit : null; - } - - if ($scope.selectedTemplate.ask_skip_tags_on_launch) { - formValues.skip_tags = $scope.selectedTemplate.skip_tags ? $scope.selectedTemplate.skip_tags : null; - } - - if ($scope.selectedTemplate.ask_tags_on_launch) { - formValues.job_tags = $scope.selectedTemplate.job_tags ? $scope.selectedTemplate.job_tags : null; - } - - // Communicate down the scope chain to our children that a template has been selected. This - // will handle populating the form properly as well as clearing out any previously selected - // templates in different lists - $scope.$broadcast('templateSelected', { - presetValues: formValues, - activeTab: $scope.workflowMakerFormConfig.activeTab - }); }; function resetEdgeConflict(){ @@ -944,7 +1061,61 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.$broadcast('zoomToFitChart'); }; - init(); + $scope.openPromptModal = function() { + $scope.promptData.triggerModalOpen = true; + }; + + let allNodes = []; + let page = 1; + + let buildTreeFromNodes = function(){ + WorkflowService.buildTree({ + workflowNodes: allNodes + }).then(function(data){ + $scope.treeData = data; + + // TODO: I think that the workflow chart directive (and eventually d3) is meddling with + // this treeData object and removing the children object for some reason (?) + // This happens on occasion and I think is a race condition (?) + if(!$scope.treeData.data.children) { + $scope.treeData.data.children = []; + } + + $scope.treeData.workflow_job_template_obj = $scope.workflowJobTemplateObj; + + $scope.treeDataMaster = angular.copy($scope.treeData.data); + $scope.showManualControls = false; + }); + }; + + let getNodes = function(){ + // Get the workflow nodes + TemplatesService.getWorkflowJobTemplateNodes($scope.workflowJobTemplateObj.id, page) + .then(function(data){ + for(var i=0; i
-
+
+
+ +
+ +
+
+
+ + + + +
+
@@ -101,4 +125,5 @@ +