From 916d91cbc7e2ba23963ff706bc895791b0ab5cc7 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Sun, 7 Jan 2018 16:49:14 -0500 Subject: [PATCH] use updated credentials endpoint --- .../job-template-add.controller.js | 19 +- .../job-template-edit.controller.js | 125 ++--- .../job_templates/job-template.form.js | 4 +- .../multi-credential-modal.directive.js | 499 +++++++++--------- .../multi-credential-modal.partial.html | 51 +- .../multi-credential.directive.js | 118 ++--- .../multi-credential.partial.html | 14 +- .../multi-credential.service.js | 351 +++--------- .../multi-credential.service-test.js | 416 --------------- 9 files changed, 475 insertions(+), 1122 deletions(-) delete mode 100644 awx/ui/test/spec/multi-credential/multi-credential.service-test.js diff --git a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js index 379496fe0c..b213d3d498 100644 --- a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js +++ b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js @@ -48,6 +48,14 @@ CallbackHelpInit({ scope: $scope }); $scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.'); + + MultiCredentialService.getCredentialTypes() + .then(({ data }) => { + $scope.multiCredential = { + credentialTypes: data.results, + selectedCredentials: [] + }; + }); } callback = function() { @@ -328,7 +336,6 @@ Rest.setUrl(defaultUrl); Rest.post(data) .then(({data}) => { - if (data.related && data.related.callback) { Alert('Callback URL', `Host callbacks are enabled for this template. The callback URL is: @@ -347,12 +354,6 @@ null, true); } - MultiCredentialService - .saveExtraCredentials({ - creds: $scope.credentialsToPost, - url: data.related.credentials - }); - var orgDefer = $q.defer(); var associationDefer = $q.defer(); Rest.setUrl(data.related.labels); @@ -441,7 +442,9 @@ }); } - saveCompleted(data.id); + MultiCredentialService + .saveRelated(data, $scope.multiCredential.selectedCredentials) + .then(() => saveCompleted(data.id)); }); }); }); diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js index 19c2964e3b..c7cc71ce4b 100644 --- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js +++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js @@ -362,74 +362,37 @@ export default $scope.can_edit = jobTemplateData.summary_fields.user_capabilities.edit; - if($scope.job_template_obj.summary_fields.user_capabilities.edit) { - MultiCredentialService.loadCredentials(jobTemplateData) - .then(([selectedCredentials, credTypes, credTypeOptions, - credTags, credentialGetPermissionDenied]) => { - $scope.canGetAllRelatedResources = !projectGetPermissionDenied && !inventoryGetPermissionDenied && !credentialGetPermissionDenied ? true : false; - $scope.selectedCredentials = selectedCredentials; - $scope.credential_types = credTypes; - $scope.credentialTypeOptions = credTypeOptions; - $scope.credentialsToPost = credTags; - $scope.$emit('jobTemplateLoaded', master); - }); - } - else { - // if (jobTemplateData.summary_fields.credential) { - // $scope.selectedCredentials.machine = jobTemplateData.summary_fields.credential; - // } + const multiCredential = {}; + const credentialTypesPromise = MultiCredentialService.getCredentialTypes() + .then(({ data }) => { + multiCredential.credentialTypes = data.results; + }); + const multiCredentialPromises = [credentialTypesPromise]; - // if (jobTemplateData.summary_fields.vault_credential) { - // $scope.selectedCredentials.vault = jobTemplateData.summary_fields.vault_credential; - // } - - if (jobTemplateData.summary_fields.credentials) { - $scope.selectedCredentials.extra = jobTemplateData.summary_fields.credentials; - } - - MultiCredentialService.getCredentialTypes() - .then(({credential_types, credentialTypeOptions}) => { - let typesArray = Object.keys(credential_types).map(key => credential_types[key]); - let credTypeOptions = credentialTypeOptions; - - let machineAndVaultCreds = [], - extraCreds = []; - - //if($scope.selectedCredentials.machine) { - // machineAndVaultCreds.push($scope.selectedCredentials.machine); - //} - //if($scope.selectedCredentials.vault) { - // machineAndVaultCreds.push($scope.selectedCredentials.vault); - //} - - machineAndVaultCreds.map(cred => ({ - name: cred.name, - id: cred.id, - postType: cred.postType, - kind: typesArray - .filter(type => { - return cred.kind === type.kind || parseInt(cred.credential_type) === type.value; - })[0].name + ":" - })); - - if($scope.selectedCredentials.extra && $scope.selectedCredentials.extra.length > 0) { - extraCreds = extraCreds.concat($scope.selectedCredentials.extra).map(cred => ({ - name: cred.name, - id: cred.id, - postType: cred.postType, - kind: credTypeOptions - .filter(type => { - return parseInt(cred.credential_type_id) === type.value; - })[0].name + ":" - })); + if ($scope.can_edit) { + const selectedCredentialsPromise = MultiCredentialService + .getRelated(jobTemplateData, { permitted: [403] }) + .then(({ data, status }) => { + if (status === 403) { + $scope.canGetAllRelatedResources = false; + multiCredential.selectedCredentials = _.get(jobTemplateData, 'summary_fields.credentials'); + } else { + $scope.canGetAllRelatedResources = !projectGetPermissionDenied && !inventoryGetPermissionDenied; + multiCredential.selectedCredentials = data.results; } - - //$scope.credentialsToPost = machineAndVaultCreds.concat(extraCreds); - $scope.credentialsToPost = extraCreds; - - $scope.$emit('jobTemplateLoaded', master); }); + + multiCredentialPromises.push(selectedCredentialsPromise); + } else { + $scope.canGetAllRelatedResources = false; + multiCredential.selectedCredentials = _.get(jobTemplateData, 'summary_fields.credentials'); } + + $q.all(multiCredentialPromises) + .then(() => { + $scope.multiCredential = multiCredential; + $scope.$emit('jobTemplateLoaded', master); + }); }); if ($scope.removeChoicesReady) { @@ -522,10 +485,7 @@ export default } MultiCredentialService - .findChangedExtraCredentials({ - creds: $scope.credentialsToPost, - url: data.related.credentials - }); + .saveRelated(jobTemplateData, $scope.multiCredential.selectedCredentials); InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups) .catch(({data, status}) => { @@ -668,13 +628,24 @@ export default data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false; data.job_tags = (Array.isArray($scope.job_tags)) ? $scope.job_tags.join() : ""; data.skip_tags = (Array.isArray($scope.skip_tags)) ? $scope.skip_tags.join() : ""; - - // drop legacy 'credential' and 'vault_credential' keys from the update request as they will - // be provided to the related credentials endpoint by the template save success handler. - delete data.credential; - delete data.vault_credential; - - data.extra_vars = ToJSON($scope.parseType, $scope.variables, true); + if ($scope.selectedCredentials && $scope.selectedCredentials + .machine && $scope.selectedCredentials + .machine.id) { + data.credential = $scope.selectedCredentials + .machine.id; + } else { + data.credential = null; + } + if ($scope.selectedCredentials && $scope.selectedCredentials + .vault && $scope.selectedCredentials + .vault.id) { + data.vault_credential = $scope.selectedCredentials + .vault.id; + } else { + data.vault_credential = null; + } + data.extra_vars = ToJSON($scope.parseType, + $scope.variables, true); // We only want to set the survey_enabled flag to // true for this job template if a survey exists @@ -713,6 +684,10 @@ export default data.job_tags = (Array.isArray($scope.job_tags)) ? _.uniq($scope.job_tags).join() : ""; data.skip_tags = (Array.isArray($scope.skip_tags)) ? _.uniq($scope.skip_tags).join() : ""; + // drop legacy 'credential' and 'vault_credential' keys from the update request as they will + // be provided to the related credentials endpoint by the template save success handler. + delete data.credential; + delete data.vault_credential; Rest.setUrl(defaultUrl + $state.params.job_template_id); Rest.put(data) diff --git a/awx/ui/client/src/templates/job_templates/job-template.form.js b/awx/ui/client/src/templates/job_templates/job-template.form.js index 62921638d9..02eaeff943 100644 --- a/awx/ui/client/src/templates/job_templates/job-template.form.js +++ b/awx/ui/client/src/templates/job_templates/job-template.form.js @@ -124,9 +124,9 @@ function(NotificationsList, CompletedJobsList, i18n) { `, awPopOver: i18n._('Select credentials that allow Tower to access the nodes this job will be ran against. You can only select one credential of each type. For machine credentials (SSH), checking "Prompt on launch" without selecting credentials will require you to select a machine credential at run time. If you select credentials and check "Prompt on launch", the selected credential(s) become the defaults that can be updated at run time.'), diff --git a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.directive.js b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.directive.js index b66448c336..511381ef77 100644 --- a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.directive.js +++ b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.directive.js @@ -1,272 +1,251 @@ -export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile', 'CreateSelect2', 'i18n', 'MultiCredentialService', 'credentialTypesLookup', - function(templateUrl, Rest, GetBasePath, GenerateList, $compile, CreateSelect2, i18n, MultiCredentialService, credentialTypesLookup) { +/************************************************* + * Copyright (c) 2018 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ +function MultiCredentialModal( + templateUrl, + generateList, + $compile, + CreateSelect2, + i18n, + CredentialList +) { + const templatePath = 'templates/job_templates/multi-credential/multi-credential-modal'; + const emptyListText = i18n._('No Credentials Matching This Type Have Been Created'); + + const list = _.cloneDeep(CredentialList); + const vaultList = _.cloneDeep(CredentialList); + + list.emptyListText = emptyListText; + + vaultList.emptyListText = emptyListText; + vaultList.fields.name.modalColumnClass = 'col-md-6'; + vaultList.fields.info = { + label: i18n._('Vault ID'), + ngBind: 'credential.inputs.vault_id', + key: false, + nosort: true, + modalColumnClass: 'col-md-6', + infoHeaderClass: '', + dataPlacement: 'top', + }; + + const listHtml = generateList.build({ mode: 'lookup', input_type: 'radio', list }); + const vaultHtml = generateList.build({ mode: 'lookup', input_type: 'checkbox', list: vaultList }); + return { + templateUrl: templateUrl(templatePath), restrict: 'E', + controllerAs: 'vm', + require: ['multiCredentialModal'], scope: { - credentialsToPost: '=', - credentials: '=', - selectedCredentials: '=' + credentialTypes: '=', + selectedCredentials: '=', }, - templateUrl: templateUrl('templates/job_templates/multi-credential/multi-credential-modal'), + link: (scope, element, attrs, controllers) => { + const compiledList = $compile(listHtml)(scope); + const compiledVaultList = $compile(vaultHtml)(scope); - link: function(scope, element) { - credentialTypesLookup() - .then(kinds => { - scope.credentialKinds = kinds; + const modalBodyElement = $('#multi-credential-modal-body'); + const modalElement = $('#multi-credential-modal'); - const machineIsReadOnly = _.get(scope.selectedCredentials, 'machine.readOnly'); - const vaultIsReadOnly = _.get(scope.selectedCredentials, 'vault.readOnly'); + scope.showModal = () => modalElement.modal('show'); + scope.hideModal = () => modalElement.modal('hide'); - if (!machineIsReadOnly) { - scope.credentialKind = `${kinds.Machine}`; - } else if (!vaultIsReadOnly) { - scope.credentialKind = `${kinds.Vault}`; - } else { - scope.credentialKind = `${kinds.Network}`; - } + scope.createList = () => modalBodyElement.append(compiledList); + scope.createVaultList = () => modalBodyElement.append(compiledVaultList); + scope.destroyList = () => modalBodyElement.empty(); - scope.showModal = function() { - scope.modalHidden = false; - $('#multi-credential-modal').modal('show'); - }; - - scope.hideModal = function() { - scope.modalHidden = true; - scope.credentialKind = kinds.Machine; - $('#multi-credential-modal').modal('hide'); - }; - - scope.generateCredentialList = function(inputType = 'radio', list = scope.list) { - console.log(inputType); - let html = GenerateList.build({ - list, - input_type: inputType, - mode: 'lookup' - }); - - $('#multi-credential-modal-body').append($compile(html)(scope)); - scope.listRendered = true; - }; - - scope.destroyCredentialList = () => { - $('#multi-credential-modal-body').empty(); - scope.listRendered = false; - } - - $('#multi-credential-modal').on('hidden.bs.modal', function () { - $('#multi-credential-modal').off('hidden.bs.modal'); - $(element).remove(); - }); - - CreateSelect2({ - element: `#multi-credential-kind-select`, - multiple: false, - placeholder: i18n._('Select a credential') - }); - - MultiCredentialService.getCredentialTypes() - .then(({credential_types, credentialTypeOptions}) => { - scope.credential_types = credential_types; - scope.credentialTypeOptions = credentialTypeOptions; - scope.allCredentialTypeOptions = _.cloneDeep(credentialTypeOptions); - - // We want to hide the machine dropdown option if a machine credential - // has already been selected and the user interacting with the form doesn't - // have the ability to change it. - for(let i=scope.credentialTypeOptions.length - 1; i >=0; i--) { - if((scope.selectedCredentials.machine && - scope.selectedCredentials.machine.credential_type_id === scope.credentialTypeOptions[i].value && - scope.selectedCredentials.machine.readOnly) || - (scope.selectedCredentials.vault && - scope.selectedCredentials.vault.credential_type_id === scope.credentialTypeOptions[i].value && - scope.selectedCredentials.vault.readOnly)) { - scope.credentialTypeOptions.splice(i, 1); - } - } - - scope.$emit('multiCredentialModalLinked'); - }); - }); - }, - - controller: ['$scope', 'CredentialList', 'i18n', 'QuerySet', - 'GetBasePath', function($scope, CredentialList, i18n, qs, - GetBasePath) { - let updateExtraCredentialsList = function() { - let extraCredIds = $scope.selectedCredentials.extra.map(cred => cred.id); - $scope.credentials.forEach(cred => { - cred.checked = (extraCredIds.indexOf(cred.id) > - 1) ? 1 : 0; - }); - - $scope.credTags = MultiCredentialService - .updateCredentialTags($scope.selectedCredentials, - $scope.allCredentialTypeOptions); - }; - - let uncheckAllCredentials = function() { - $scope.credentials.forEach(cred => { - cred.checked = 0; - }); - - $scope.credTags = MultiCredentialService - .updateCredentialTags($scope.selectedCredentials, - $scope.allCredentialTypeOptions); - }; - - const onCredentialKindChanged = (newValue, oldValue) => { - const newValueIsVault = (parseInt(newValue) === _.get($scope, 'credentialKinds.Vault')); - const oldValueIsVault = (parseInt(oldValue) === _.get($scope, 'credentialKinds.Vault')); - const isClosing = ((newValue !== oldValue) && $scope.modalHidden); - - if ((oldValueIsVault || newValueIsVault) && !isClosing) { - $scope.destroyCredentialList(); - } - - $scope.credential_queryset.page = 1; - $scope.credential_default_params.credential_type = parseInt($scope.credentialKind); - $scope.credential_queryset.credential_type = parseInt($scope.credentialKind); - - qs.search(GetBasePath('credentials'), $scope.credential_default_params) - .then(({ data }) => { - $scope.credential_dataset = data; - $scope.credentials = $scope.credential_dataset.results; - - if(!$scope.listRendered) { - if (newValueIsVault) { - $scope.generateCredentialList('checkbox', $scope.vaultList); - } else { - $scope.generateCredentialList(); - } - updateExtraCredentialsList(); - $scope.showModal(); - } - }); - }; - - let init = function() { - $scope.originalSelectedCredentials = _.cloneDeep($scope.selectedCredentials); - $scope.credential_dataset = []; - $scope.credentials = $scope.credentials || []; - $scope.listRendered = false; - - const credList = _.cloneDeep(CredentialList); - - credList.emptyListText = i18n._('No Credentials Matching This Type Have Been Created'); - - const vaultCredList = _.cloneDeep(credList); - - vaultCredList.fields.name.modalColumnClass = 'col-md-6'; - - vaultCredList.fields.info = { - label: i18n._('Vault ID'), - ngBind: 'credential.inputs.vault_id', - key: false, - nosort: true, - modalColumnClass: 'col-md-6', - infoHeaderClass: '', - dataPlacement: 'top' - }; - - $scope.list = credList; - $scope.vaultList = vaultCredList; - - $scope.credential_default_params = { - order_by: 'name', - page_size: 5 - }; - - $scope.credential_queryset = { - order_by: 'name', - page_size: 5 - }; - - $scope.$watch('credentialKind', onCredentialKindChanged); - - $scope.$watchCollection('credentials', updateExtraCredentialsList); - - $scope.$watchCollection('selectedCredentials.extra', () => { - if($scope.credentials && $scope.credentials.length > 0) { - if($scope.selectedCredentials.extra && - $scope.selectedCredentials.extra.length > 0) { - updateExtraCredentialsList(); - } else { - uncheckAllCredentials(); - } - } - }); - }; - - $scope.$on('multiCredentialModalLinked', function() { - init(); + modalElement.on('hidden.bs.modal', () => { + modalElement.off('hidden.bs.modal'); + $(element).remove(); }); - $scope.toggle_credential = id => { - // This is called only when a checkbox input is clicked directly. For clicks anywhere - // else on the row or direct radio button clicks, the toggle_row handler is called - // instead with a slightly different set of arguments. We normalize those arguments - // here and pass them through to the other handler so that the behavior is consistent. - const [credential] = $scope.credentials.filter(credential => credential.id === id); - return $scope.toggle_row(credential); - }; + CreateSelect2({ + placeholder: i18n._('Select a credential'), + element: '#multi-credential-kind-select', + multiple: false, + }); - $scope.toggle_row = function(credential) { - let rowDeselected = false; - for (let i = $scope.selectedCredentials.extra.length - 1; i >= 0; i--) { - if($scope.selectedCredentials.extra[i].id === credential.id) { - $scope.selectedCredentials.extra.splice(i, 1); - rowDeselected = true; - } else if(credential.credential_type === $scope.selectedCredentials.extra[i].credential_type) { - if (credential.credential_type !== $scope.credentialKinds.Vault) { - $scope.selectedCredentials.extra.splice(i, 1); - } else if($scope.selectedCredentials.extra[i].inputs.vault_id === credential.inputs.vault_id) { - // remove existing vault credentials if they have the same vault_id as a recently - // toggled vault credential - $scope.selectedCredentials.extra.splice(i, 1); - } - } - } - if(!rowDeselected) { - $scope.selectedCredentials.extra - .push(_.cloneDeep(credential)); - } - }; - - $scope.selectedCredentialsDirty = function() { - if ($scope.originalSelectedCredentials) { - return !($scope.originalSelectedCredentials.extra.length === 0) && - !_.isEqual($scope.selectedCredentials, - $scope.originalSelectedCredentials); - } else { - return false; - } - }; - - $scope.revertToDefaultCredentials = function() { - $scope.selectedCredentials = _.cloneDeep($scope.originalSelectedCredentials); - }; - - $scope.removeCredential = function(credToRemove) { - [$scope.selectedCredentials, - $scope.credTags] = MultiCredentialService - .removeCredential(credToRemove, - $scope.selectedCredentials, $scope.credTags); - - if ($scope.credentials - .filter(cred => cred.id === credToRemove).length) { - uncheckAllCredentials(); - } - }; - - $scope.cancelForm = function() { - $scope.selectedCredentials = $scope.originalSelectedCredentials; - $scope.credTags = $scope.credentialsToPost; - $scope.hideModal(); - }; - - $scope.saveForm = function() { - $scope.credentialsToPost = $scope.credTags; - $scope.hideModal(); - }; - }] + scope.list = list; + controllers[0].init(scope); + }, + controller: ['GetBasePath', 'QuerySet', 'MultiCredentialService', + multiCredentialModalController], }; -}]; +} + +function multiCredentialModalController(GetBasePath, qs, MultiCredentialService) { + const vm = this; + const { createTag, isReadOnly } = MultiCredentialService; + + const types = {}; + const unwatch = []; + + let scope; + + vm.init = _scope_ => { + scope = _scope_; + scope.modalSelectedCredentials = _.cloneDeep(scope.selectedCredentials); + + scope.credentialTypes.forEach(({ name, id }) => types[name] = id); + + scope.toggle_row = vm.toggle_row; + scope.toggle_credential = vm.toggle_credential; + + scope.credential_default_params = { order_by: 'name', page_size: 5 }; + scope.credential_queryset = { order_by: 'name', page_size: 5 }; + scope.credential_dataset = { results: [], count: 0 }; + scope.credentials = scope.credential_dataset.results; + + scope.credentialType = `${types.Vault}`; + scope.displayedCredentialTypes = scope.credentialTypes; + + const watchType = scope.$watch('credentialType', (newValue, oldValue) => { + if (newValue !== oldValue) { + fetchCredentials(parseInt(newValue)); + } + }); + scope.$watchCollection('modalSelectedCredentials', updateListView); + scope.$watchCollection('modalSelectedCredentials', updateTagView); + scope.$watchCollection('modalSelectedCredentials', updateDisplayedCredentialTypes); + scope.$watchCollection('credentials', updateListView); + + unwatch.push(watchType); + + fetchCredentials(parseInt(scope.credentialType)) + .then(scope.showModal); + }; + + function updateTagView () { + scope.tags = scope.modalSelectedCredentials + .map(c => createTag(c, scope.credentialTypes)); + } + + function updateListView () { + scope.credentials.forEach(credential => { + const index = scope.modalSelectedCredentials + .map(({ id }) => id) + .indexOf(credential.id); + + if (index > -1) { + credential.checked = 1; + } else { + credential.checked = 0; + } + }); + } + + function updateDisplayedCredentialTypes() { + const displayedCredentialTypes = _.cloneDeep(scope.credentialTypes); + + scope.modalSelectedCredentials.forEach(credential => { + const credentialTypeId = credential.credential_type || credential.credential_type_id; + + if(isReadOnly(credential) && credentialTypeId !== types.Vault) { + const index = displayedCredentialTypes + .map(t => t.id).indexOf(credential.credential_type); + + if (index > -1) { + displayedCredentialTypes.splice(index, 1); + } + } + }); + + scope.displayedCredentialTypes = displayedCredentialTypes; + } + + function fetchCredentials (credentialType) { + const endpoint = GetBasePath('credentials'); + + scope.credential_queryset.page = 1; + scope.credential_default_params.credential_type = credentialType; + scope.credential_queryset.credential_type = credentialType; + + return qs.search(endpoint, scope.credential_default_params) + .then(({ data }) => { + const results = data.results.filter(c => !isReadOnly(c)); + const readOnlyCount = data.results.length - results.length; + + data.results = results; + data.count = data.count - readOnlyCount; + + scope.credential_dataset = data; + scope.credentials = data.results; + + scope.destroyList(); + + if (credentialType === types.Vault) { + scope.createVaultList(); + } else { + scope.createList(); + } + }); + } + + vm.revertSelectedCredentials = () => { + scope.modalSelectedCredentials = _.cloneDeep(scope.selectedCredentials); + }; + + vm.removeCredential = id => { + const index = scope.modalSelectedCredentials.map(c => c.id).indexOf(id); + const isSelected = index > -1; + + if (isSelected) { + scope.modalSelectedCredentials.splice(index, 1); + return; + } + }; + + vm.toggle_credential = id => { + // This is called only when a checkbox input is clicked directly. Clicks anywhere else on + // the row or direct radio button clicks invoke the toggle_row handler instead with a + // different set of arguments. We normalize those arguments here and pass them through to + // the other function so that the behavior is consistent. + const credential = scope.credentials.find(c => c.id === id); + scope.toggle_row(credential); + }; + + vm.toggle_row = credential => { + const index = scope.modalSelectedCredentials.map(c => c.id).indexOf(credential.id); + const isSelected = index > -1; + + if (isSelected) { + scope.modalSelectedCredentials.splice(index, 1); + return; + } + + if (credential.credential_type === types.Vault) { + scope.modalSelectedCredentials = scope.modalSelectedCredentials + .filter(({ inputs }) => inputs.vault_id !== credential.inputs.vault_id) + .concat([credential]); + } else { + scope.modalSelectedCredentials = scope.modalSelectedCredentials + .filter(({ credential_type }) => credential_type !== credential.credential_type) + .concat([credential]); + } + }; + + vm.cancelForm = () => { + unwatch.forEach(cb => cb()); + scope.hideModal(); + }; + + vm.saveForm = () => { + scope.selectedCredentials = _.cloneDeep(scope.modalSelectedCredentials); + unwatch.forEach(cb => cb()); + scope.hideModal(); + }; +} + +MultiCredentialModal.$inject = [ + 'templateUrl', + 'generateList', + '$compile', + 'CreateSelect2', + 'i18n', + 'CredentialList', +]; + +export default MultiCredentialModal; diff --git a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.partial.html b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.partial.html index 95d28195af..1e52f574fc 100644 --- a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.partial.html +++ b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential-modal.partial.html @@ -2,21 +2,19 @@