mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 02:50:02 -03:30
Add a Galaxy Credential multi-select field to the Organizations form
This commit is contained in:
parent
011822b1f0
commit
458807c0c7
@ -4,11 +4,12 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', '$rootScope', '$location', '$stateParams',
|
||||
'OrganizationForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2', '$state','InstanceGroupsService', 'ConfigData',
|
||||
function($scope, $rootScope, $location, $stateParams, OrganizationForm,
|
||||
GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, Wait, CreateSelect2, $state, InstanceGroupsService, ConfigData) {
|
||||
export default ['$scope', '$rootScope', '$location', '$stateParams', 'OrganizationForm',
|
||||
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2',
|
||||
'$state','InstanceGroupsService', 'ConfigData', 'MultiCredentialService',
|
||||
function($scope, $rootScope, $location, $stateParams, OrganizationForm,
|
||||
GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, Wait, CreateSelect2,
|
||||
$state, InstanceGroupsService, ConfigData, MultiCredentialService) {
|
||||
|
||||
Rest.setUrl(GetBasePath('organizations'));
|
||||
Rest.options()
|
||||
@ -57,18 +58,32 @@ export default ['$scope', '$rootScope', '$location', '$stateParams',
|
||||
const organization_id = data.id,
|
||||
instance_group_url = data.related.instance_groups;
|
||||
|
||||
InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||
MultiCredentialService
|
||||
.saveRelatedSequentially({
|
||||
related: {
|
||||
credentials: data.related.galaxy_credentials
|
||||
}
|
||||
}, $scope.credentials)
|
||||
.then(() => {
|
||||
Wait('stop');
|
||||
$rootScope.$broadcast("EditIndicatorChange", "organizations", organization_id);
|
||||
$state.go('organizations.edit', {organization_id: organization_id}, {reload: true});
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||
.then(() => {
|
||||
Wait('stop');
|
||||
$rootScope.$broadcast("EditIndicatorChange", "organizations", organization_id);
|
||||
$state.go('organizations.edit', {organization_id: organization_id}, {reload: true});
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to save instance groups. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
}).catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to save instance groups. POST returned status: ' + status
|
||||
msg: 'Failed to save Galaxy credentials. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
let explanation = _.has(data, "name") ? data.name[0] : "";
|
||||
|
||||
@ -6,10 +6,12 @@
|
||||
|
||||
export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotificationAdmin',
|
||||
'OrganizationForm', 'Rest', 'ProcessErrors', 'Prompt', 'i18n', 'isOrgAuditor',
|
||||
'GetBasePath', 'Wait', '$state', 'ToggleNotification', 'CreateSelect2', 'InstanceGroupsService', 'InstanceGroupsData', 'ConfigData',
|
||||
'GetBasePath', 'Wait', '$state', 'ToggleNotification', 'CreateSelect2', 'InstanceGroupsService',
|
||||
'InstanceGroupsData', 'ConfigData', 'GalaxyCredentialsData', 'MultiCredentialService',
|
||||
function($scope, $location, $stateParams, isOrgAdmin, isNotificationAdmin,
|
||||
OrganizationForm, Rest, ProcessErrors, Prompt, i18n, isOrgAuditor,
|
||||
GetBasePath, Wait, $state, ToggleNotification, CreateSelect2, InstanceGroupsService, InstanceGroupsData, ConfigData) {
|
||||
GetBasePath, Wait, $state, ToggleNotification, CreateSelect2, InstanceGroupsService,
|
||||
InstanceGroupsData, ConfigData, GalaxyCredentialsData, MultiCredentialService) {
|
||||
|
||||
let form = OrganizationForm(),
|
||||
defaultUrl = GetBasePath('organizations'),
|
||||
@ -29,6 +31,7 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica
|
||||
});
|
||||
|
||||
$scope.instance_groups = InstanceGroupsData;
|
||||
$scope.credentials = GalaxyCredentialsData;
|
||||
const virtualEnvs = ConfigData.custom_virtualenvs || [];
|
||||
$scope.custom_virtualenvs_visible = virtualEnvs.length > 1;
|
||||
$scope.custom_virtualenvs_options = virtualEnvs.filter(
|
||||
@ -100,7 +103,14 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica
|
||||
Rest.setUrl(defaultUrl + id + '/');
|
||||
Rest.put(params)
|
||||
.then(() => {
|
||||
InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||
MultiCredentialService
|
||||
.saveRelatedSequentially({
|
||||
related: {
|
||||
credentials: $scope.organization_obj.related.galaxy_credentials
|
||||
}
|
||||
}, $scope.credentials)
|
||||
.then(() => {
|
||||
InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||
.then(() => {
|
||||
Wait('stop');
|
||||
$state.go($state.current, {}, { reload: true });
|
||||
@ -111,6 +121,12 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica
|
||||
msg: 'Failed to update instance groups. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
}).catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to save Galaxy credentials. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
$scope.organization_name = $scope.name;
|
||||
main = params;
|
||||
})
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
export default ['templateUrl', '$window', function(templateUrl, $window) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
galaxyCredentials: '='
|
||||
},
|
||||
templateUrl: templateUrl('organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal'),
|
||||
|
||||
link: function(scope, element) {
|
||||
|
||||
$('#galaxy-credentials-modal').on('hidden.bs.modal', function () {
|
||||
$('#galaxy-credentials-modal').off('hidden.bs.modal');
|
||||
$(element).remove();
|
||||
});
|
||||
|
||||
scope.showModal = function() {
|
||||
$('#galaxy-credentials-modal').modal('show');
|
||||
};
|
||||
|
||||
scope.destroyModal = function() {
|
||||
$('#galaxy-credentials-modal').modal('hide');
|
||||
};
|
||||
},
|
||||
|
||||
controller: ['$scope', '$compile', 'QuerySet', 'GetBasePath','generateList', 'CredentialList', function($scope, $compile, qs, GetBasePath, GenerateList, CredentialList) {
|
||||
|
||||
function init() {
|
||||
|
||||
$scope.credential_queryset = {
|
||||
order_by: 'name',
|
||||
page_size: 5,
|
||||
credential_type__kind: 'galaxy'
|
||||
};
|
||||
|
||||
$scope.credential_default_params = {
|
||||
order_by: 'name',
|
||||
page_size: 5,
|
||||
credential_type__kind: 'galaxy'
|
||||
};
|
||||
|
||||
qs.search(GetBasePath('credentials'), $scope.credential_queryset)
|
||||
.then(res => {
|
||||
$scope.credential_dataset = res.data;
|
||||
$scope.credentials = $scope.credential_dataset.results;
|
||||
|
||||
let credentialList = _.cloneDeep(CredentialList);
|
||||
|
||||
credentialList.listTitle = false;
|
||||
credentialList.well = false;
|
||||
credentialList.multiSelect = true;
|
||||
credentialList.multiSelectPreview = {
|
||||
selectedRows: 'igTags',
|
||||
availableRows: 'credentials'
|
||||
};
|
||||
credentialList.fields.name.ngClick = "linkoutCredential(credential)";
|
||||
credentialList.fields.name.columnClass = 'col-md-11 col-sm-11 col-xs-11';
|
||||
delete credentialList.fields.consumed_capacity;
|
||||
delete credentialList.fields.jobs_running;
|
||||
|
||||
let html = `${GenerateList.build({
|
||||
list: credentialList,
|
||||
input_type: 'galaxy-credentials-modal-body',
|
||||
hideViewPerPage: true,
|
||||
mode: 'lookup'
|
||||
})}`;
|
||||
|
||||
$scope.list = credentialList;
|
||||
$('#galaxy-credentials-modal-body').append($compile(html)($scope));
|
||||
|
||||
if ($scope.galaxyCredentials) {
|
||||
$scope.galaxyCredentials = $scope.galaxyCredentials.map( (item) => {
|
||||
item.isSelected = true;
|
||||
if (!$scope.igTags) {
|
||||
$scope.igTags = [];
|
||||
}
|
||||
$scope.igTags.push(item);
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.showModal();
|
||||
});
|
||||
|
||||
$scope.$watch('credentials', function(){
|
||||
angular.forEach($scope.credentials, function(credentialRow) {
|
||||
angular.forEach($scope.igTags, function(selectedCredential){
|
||||
if(selectedCredential.id === credentialRow.id) {
|
||||
credentialRow.isSelected = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
$scope.$on("selectedOrDeselected", function(e, value) {
|
||||
let item = value.value;
|
||||
if (value.isSelected) {
|
||||
if(!$scope.igTags) {
|
||||
$scope.igTags = [];
|
||||
}
|
||||
$scope.igTags.push(item);
|
||||
} else {
|
||||
_.remove($scope.igTags, { id: item.id });
|
||||
}
|
||||
});
|
||||
|
||||
$scope.linkoutCredential = function(credential) {
|
||||
$window.open('/#/credentials/' + credential.id,'_blank');
|
||||
};
|
||||
|
||||
$scope.cancelForm = function() {
|
||||
$scope.destroyModal();
|
||||
};
|
||||
|
||||
$scope.saveForm = function() {
|
||||
$scope.galaxyCredentials = $scope.igTags;
|
||||
$scope.destroyModal();
|
||||
};
|
||||
}]
|
||||
};
|
||||
}];
|
||||
@ -0,0 +1,22 @@
|
||||
<div id="galaxy-credentials-modal" class="Lookup modal fade">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header Form-header">
|
||||
<div class="Form-title Form-title--uppercase" translate>Select Galaxy Credentials</div>
|
||||
<div class="Form-header--fields"></div>
|
||||
<div class="Form-exitHolder">
|
||||
<button aria-label="{{'Close'|translate}}" type="button" class="Form-exit" ng-click="cancelForm()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="galaxy-credentials-modal-body"> {{ credential }} </div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-click="cancelForm()" class="btn btn-default" translate>CANCEL</button>
|
||||
<button type="button" ng-click="saveForm()" ng-disabled="!credentials || credentials.length === 0" class="Lookup-save btn btn-primary" translate>SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,14 @@
|
||||
export default ['$scope',
|
||||
function($scope) {
|
||||
|
||||
$scope.galaxyCredentialsTags = [];
|
||||
|
||||
$scope.$watch('galaxyCredentials', function() {
|
||||
$scope.galaxyCredentialsTags = $scope.galaxyCredentials;
|
||||
}, true);
|
||||
|
||||
$scope.deleteTag = function(tag){
|
||||
_.remove($scope.galaxyCredentials, {id: tag.id});
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,15 @@
|
||||
#instance-groups-panel {
|
||||
table {
|
||||
overflow: hidden;
|
||||
}
|
||||
.List-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.isActive {
|
||||
border-left: 10px solid @list-row-select-bord;
|
||||
}
|
||||
.instances-list,
|
||||
.instance-jobs-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import galaxyCredentialsMultiselectController from './galaxy-credentials-multiselect.controller';
|
||||
export default ['templateUrl', '$compile',
|
||||
function(templateUrl, $compile) {
|
||||
return {
|
||||
scope: {
|
||||
galaxyCredentials: '=',
|
||||
fieldIsDisabled: '='
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('organizations/galaxy-credentials-multiselect/galaxy-credentials'),
|
||||
controller: galaxyCredentialsMultiselectController,
|
||||
link: function(scope) {
|
||||
scope.openInstanceGroupsModal = function() {
|
||||
$('#content-container').append($compile('<galaxy-credentials-modal galaxy-credentials="galaxyCredentials"></galaxy-credentials-modal>')(scope));
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,18 @@
|
||||
<div class="input-group Form-mixedInputGroup">
|
||||
<span class="input-group-btn input-group-prepend Form-variableHeightButtonGroup">
|
||||
<button aria-label="{{'Open Galaxy credentials'|translate}}" type="button" class="Form-lookupButton Form-lookupButton--variableHeight btn btn-default" ng-click="openInstanceGroupsModal()"
|
||||
ng-disabled="fieldIsDisabled">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
<span id="InstanceGroups" class="form-control Form-textInput Form-textInput--variableHeight input-medium lookup LabelList-lookupTags"
|
||||
ng-disabled="fieldIsDisabled"
|
||||
ng-class="{'LabelList-lookupTags--disabled' : fieldIsDisabled}">
|
||||
<div ng-if="!fieldIsDisabled" class="LabelList-tagContainer" ng-repeat="tag in galaxyCredentialsTags">
|
||||
<at-tag tag="tag.name" remove-tag="deleteTag(tag)"></at-tag>
|
||||
</div>
|
||||
<div ng-if="fieldIsDisabled" class="LabelList-tag" ng-repeat="tag in galaxyCredentialsTags">
|
||||
<span class="LabelList-name">{{tag.name | sanitize}}</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
@ -12,8 +12,10 @@ import organizationsLinkout from './linkout/main';
|
||||
import OrganizationsLinkoutStates from './linkout/organizations-linkout.route';
|
||||
import OrganizationForm from './organizations.form';
|
||||
import OrganizationList from './organizations.list';
|
||||
import { N_ } from '../i18n';
|
||||
import galaxyCredentialsMultiselect from './galaxy-credentials-multiselect/galaxy-credentials.directive';
|
||||
import galaxyCredentialsModal from './galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.directive';
|
||||
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default
|
||||
angular.module('Organizations', [
|
||||
@ -24,6 +26,8 @@ angular.module('Organizations', [
|
||||
.controller('OrganizationsEdit', OrganizationsEdit)
|
||||
.factory('OrganizationForm', OrganizationForm)
|
||||
.factory('OrganizationList', OrganizationList)
|
||||
.directive('galaxyCredentialsMultiselect', galaxyCredentialsMultiselect)
|
||||
.directive('galaxyCredentialsModal', galaxyCredentialsModal)
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||
let stateExtender = $stateExtenderProvider.$get(),
|
||||
@ -81,6 +85,24 @@ angular.module('Organizations', [
|
||||
});
|
||||
});
|
||||
}],
|
||||
GalaxyCredentialsData: ['$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors',
|
||||
function($stateParams, Rest, GetBasePath, ProcessErrors){
|
||||
let path = `${GetBasePath('organizations')}${$stateParams.organization_id}/galaxy_credentials/`;
|
||||
Rest.setUrl(path);
|
||||
return Rest.get()
|
||||
.then(({data}) => {
|
||||
if (data.results.length > 0) {
|
||||
return data.results;
|
||||
}
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(null, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get credentials. GET returned ' +
|
||||
'status: ' + status
|
||||
});
|
||||
});
|
||||
}],
|
||||
InstanceGroupsData: ['$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors',
|
||||
function($stateParams, Rest, GetBasePath, ProcessErrors){
|
||||
let path = `${GetBasePath('organizations')}${$stateParams.organization_id}/instance_groups/`;
|
||||
|
||||
@ -55,6 +55,15 @@ export default ['NotificationsList', 'i18n',
|
||||
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||
ngShow: 'custom_virtualenvs_visible'
|
||||
},
|
||||
credential: {
|
||||
label: i18n._('Galaxy Credentials'),
|
||||
type: 'custom',
|
||||
awPopOver: "<p>" + i18n._("Select Galaxy credentials. The selection order sets precedence for the sync and lookup of the content") + "</p>",
|
||||
dataTitle: i18n._('Galaxy Credentials'),
|
||||
dataContainer: 'body',
|
||||
dataPlacement: 'right',
|
||||
control: '<galaxy-credentials-multiselect galaxy-credentials="credentials" field-is-disabled="!(organization_obj.summary_fields.user_capabilities.edit || canAdd) || (!current_user.is_superuser && isOrgAdmin)"></galaxy-credentials-multiselect>',
|
||||
},
|
||||
max_hosts: {
|
||||
label: i18n._('Max Hosts'),
|
||||
type: 'number',
|
||||
@ -69,7 +78,7 @@ export default ['NotificationsList', 'i18n',
|
||||
awPopOver: "<p>" + i18n._("The maximum number of hosts allowed to be managed by this organization. Value defaults to 0 which means no limit. Refer to the Ansible documentation for more details.") + "</p>",
|
||||
ngDisabled: '!current_user.is_superuser',
|
||||
ngShow: 'BRAND_NAME === "Tower"'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
buttons: { //for now always generates <button> tags
|
||||
|
||||
@ -46,6 +46,52 @@ function MultiCredentialService (Rest, ProcessErrors, $q, GetBasePath) {
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRelatedSequentially = ({ related }, credentials) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
.get()
|
||||
.then(res => {
|
||||
const { data: { results = [] } } = res;
|
||||
const updatedCredentialIds = (credentials || []).map(({ id }) => id);
|
||||
const currentCredentialIds = results.map(({ id }) => id);
|
||||
const credentialIdsToAssociate = [];
|
||||
const credentialIdsToDisassociate = [];
|
||||
let disassociateRemainingIds = false;
|
||||
|
||||
currentCredentialIds.forEach((currentId, position) => {
|
||||
if (!disassociateRemainingIds && updatedCredentialIds[position] !== currentId) {
|
||||
disassociateRemainingIds = true;
|
||||
}
|
||||
|
||||
if (disassociateRemainingIds) {
|
||||
credentialIdsToDisassociate.push(currentId);
|
||||
}
|
||||
});
|
||||
|
||||
updatedCredentialIds.forEach(updatedId => {
|
||||
if (credentialIdsToDisassociate.includes(updatedId)) {
|
||||
credentialIdsToAssociate.push(updatedId);
|
||||
} else if (!currentCredentialIds.includes(updatedId)) {
|
||||
credentialIdsToAssociate.push(updatedId);
|
||||
}
|
||||
});
|
||||
|
||||
let disassociationPromise = Promise.resolve();
|
||||
credentialIdsToDisassociate.forEach(id => {
|
||||
disassociationPromise = disassociationPromise.then(() => disassociate({ related }, id));
|
||||
});
|
||||
|
||||
return disassociationPromise
|
||||
.then(() => {
|
||||
let associationPromise = Promise.resolve();
|
||||
credentialIdsToAssociate.forEach(id => {
|
||||
associationPromise = associationPromise.then(() => associate({ related }, id));
|
||||
});
|
||||
return associationPromise;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.getRelated = ({ related }, params = { permitted: [] }) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user