From effc34fdadd38cd13db819e2d26573a3640b1e04 Mon Sep 17 00:00:00 2001 From: qiaomin Date: Mon, 10 Jul 2017 14:54:53 +0800 Subject: [PATCH] Adding identity ng-groups create action This patch adds angular groups create action. To be added in subsequent patches: - Delete, edit, manage members action To Test - set 'groups_panel': True in settings.py - enable keystone v3 in local_settings.py Co-Authored-By: Shu Muto Change-Id: If072ef89dab7aeccef8988d165d687621f1edb7c Implements: blueprint ng-groups-actions --- openstack_dashboard/api/rest/keystone.py | 4 +- .../identity/groups/actions/actions.module.js | 57 ++++++++ .../groups/actions/create.action.service.js | 113 +++++++++++++++ .../actions/create.action.service.spec.js | 132 ++++++++++++++++++ .../identity/groups/groups.module.js | 5 +- .../openstack-service-api/keystone.service.js | 1 + .../keystone.service.spec.js | 1 - 7 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/actions.module.js create mode 100644 openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.js create mode 100644 openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.spec.js diff --git a/openstack_dashboard/api/rest/keystone.py b/openstack_dashboard/api/rest/keystone.py index 147a9227aa..980035af00 100644 --- a/openstack_dashboard/api/rest/keystone.py +++ b/openstack_dashboard/api/rest/keystone.py @@ -20,6 +20,7 @@ from django.views import generic from openstack_dashboard import api from openstack_dashboard.api.rest import urls from openstack_dashboard.api.rest import utils as rest_utils +from openstack_dashboard.utils import identity as identity_utils @urls.register @@ -618,10 +619,9 @@ class Groups(generic.View): This method returns the new group object on success. """ - domain_context = request.session.get('domain_context') new_group = api.keystone.group_create( request, - request.GET.get('domain_id', domain_context), + identity_utils.get_domain_id_for_operation(request), request.DATA['name'], request.DATA.get("description", None)) diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/actions.module.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/actions.module.js new file mode 100644 index 0000000000..8308b827db --- /dev/null +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/actions.module.js @@ -0,0 +1,57 @@ +/** + * Copyright 2017 99Cloud + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.identity.groups.actions + * + * @description + * Provides all of the actions for groups. + */ + angular + .module('horizon.dashboard.identity.groups.actions', [ + 'horizon.framework.conf' + ]) + .run(registerGroupActions); + + registerGroupActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.identity.groups.actions.create.service', + 'horizon.dashboard.identity.groups.resourceType' + ]; + + function registerGroupActions( + registry, + createService, + groupResourceTypeCode + ) { + var groupResourceType = registry.getResourceType(groupResourceTypeCode); + + groupResourceType.globalActions + .append({ + id: 'createGroupAction', + service: createService, + template: { + type: 'create', + text: gettext('Create Group') + } + }); + } + +})(); diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.js new file mode 100644 index 0000000000..fcbee9fd9e --- /dev/null +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.js @@ -0,0 +1,113 @@ +/** + * Copyright 2017 99Cloud + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.identity.groups') + .factory('horizon.dashboard.identity.groups.actions.create.service', createService); + + createService.$inject = [ + '$q', + 'horizon.dashboard.identity.groups.resourceType', + 'horizon.app.core.openstack-service-api.keystone', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.widgets.form.ModalFormService', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.widgets.toast.service' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.identity.groups.actions.create.service + * @Description A service to handle the Create Group modal. + */ + function createService( + $q, + resourceType, + keystoneAPI, + policy, + modalFormService, + actionResultService, + gettext, + toast + ) { + var service = { + allowed: allowed, + perform: perform, + submit: submit + }; + + return service; + + ////////////// + + function allowed() { + return $q.all([ + keystoneAPI.canEditIdentity('group'), + policy.ifAllowed({ rules: [['identity', 'identity:create_group']] }) + ]); + } + + function perform() { + var model = { + name: '', + description: '' + }; + + var schema = { + type: 'object', + properties: { + name: { + title: gettext('Name'), + type: 'string' + }, + description: { + title: gettext('Description'), + type: 'string' + } + }, + required: ['name'] + }; + + var config = { + title: gettext('Create Group'), + schema: schema, + form: ['*'], + model: model + }; + return modalFormService.open(config).then(submit); + } + + function submit(context) { + return keystoneAPI.createGroup(context.model).then(onSuccess); + } + + function onSuccess(response) { + var group = response.data; + toast.add('success', interpolate( + gettext('Group %s was successfully created.'), [group.name])); + + return actionResultService.getActionResult() + .created(resourceType, group.id) + .result; + } + + } +})(); + diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.spec.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.spec.js new file mode 100644 index 0000000000..d12955e1f9 --- /dev/null +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/create.action.service.spec.js @@ -0,0 +1,132 @@ +/** + * Copyright 2017 99Cloud + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + describe('horizon.dashboard.identity.groups.actions.create.service', function() { + + var $q, $scope, keystoneAPI, service, modalFormService, policyAPI, resType, toast; + + /////////////////////// + + beforeEach(module('horizon.framework')); + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.dashboard.identity.groups')); + + beforeEach(inject(function($injector, _$rootScope_, _$q_) { + $scope = _$rootScope_.$new(); + $q = _$q_; + service = $injector.get('horizon.dashboard.identity.groups.actions.create.service'); + toast = $injector.get('horizon.framework.widgets.toast.service'); + modalFormService = $injector.get('horizon.framework.widgets.form.ModalFormService'); + keystoneAPI = $injector.get('horizon.app.core.openstack-service-api.keystone'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + resType = $injector.get('horizon.dashboard.identity.groups.resourceType'); + })); + + it('should allow if can_edit_group is set True in OPENSTACK_KEYSTONE_BACKEND', function() { + //for canEditGroup + var deferredCanEdit = $q.defer(); + deferredCanEdit.resolve(true); + spyOn(keystoneAPI, 'canEditIdentity').and.returnValue(deferredCanEdit.promise); + + //for ifAllowed + var deferredIfAllowed = $q.defer(); + deferredIfAllowed.resolve(true); + spyOn(policyAPI, 'ifAllowed').and.returnValue(deferredIfAllowed.promise); + + var allowed = service.allowed({id: '1234'}); + $scope.$apply(); + + expect(allowed).toBeTruthy(); + expect(policyAPI.ifAllowed).toHaveBeenCalledWith( + { rules: [['identity', 'identity:create_group']] }); + }); + + it('should allow if can_edit_group is set True in OPENSTACK_KEYSTONE_BACKEND', function() { + //for canEditGroup + var deferredCanEdit = $q.defer(); + deferredCanEdit.resolve(false); + spyOn(keystoneAPI, 'canEditIdentity').and.returnValue(deferredCanEdit.promise); + + //for ifAllowed + var deferredIfAllowed = $q.defer(); + deferredIfAllowed.resolve(true); + spyOn(policyAPI, 'ifAllowed').and.returnValue(deferredIfAllowed.promise); + + var allowed = service.allowed({id: '1234'}); + $scope.$apply(); + + // reject + expect(allowed.$$state.status).toEqual(1); + }); + + it('should open the modal with the correct parameters', function() { + var deferred = $q.defer(); + spyOn(modalFormService, 'open').and.returnValue(deferred.promise); + + service.perform(); + + expect(modalFormService.open).toHaveBeenCalled(); + + var config = modalFormService.open.calls.mostRecent().args[0]; + expect(config.model).toBeDefined(); + expect(config.schema).toBeDefined(); + expect(config.form).toBeDefined(); + }); + + it('should submit create group request to keystone', function() { + var deferred = $q.defer(); + spyOn(keystoneAPI, 'createGroup').and.returnValue(deferred.promise); + spyOn(toast, 'add').and.callFake(angular.noop); + var handler = jasmine.createSpyObj('handler', ['success']); + + deferred.resolve( + { + data: { + name: 'saved', + description: 'group-des', + id: '12345' + } + } + ); + service.submit( + { + model: { + name: 'entered', + description: 'group-des' + } + } + ).then(handler.success); + + $scope.$apply(); + + expect(keystoneAPI.createGroup).toHaveBeenCalledWith( + { + name: 'entered', + description: 'group-des' + } + ); + expect(toast.add).toHaveBeenCalledWith('success', 'Group saved was successfully created.'); + + expect(handler.success).toHaveBeenCalled(); + var result = handler.success.calls.first().args[0]; + expect(result.created).toEqual([{type: resType, id: '12345'}]); + }); + + }); +})(); diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/groups.module.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/groups.module.js index 579ffef354..e64006460d 100644 --- a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/groups.module.js +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/groups.module.js @@ -1,5 +1,5 @@ /** - * Copyright 2016 99Cloud + * Copyright 2017 99Cloud * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain @@ -27,7 +27,8 @@ */ angular .module('horizon.dashboard.identity.groups', [ - 'ngRoute' + 'ngRoute', + 'horizon.dashboard.identity.groups.actions' ]) .constant('horizon.dashboard.identity.groups.resourceType', 'OS::Keystone::Group') .run(run) diff --git a/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.js b/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.js index 6c28e27ca9..00e2e34d15 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.js @@ -421,6 +421,7 @@ toastService.add('error', gettext('Unable to fetch the service catalog.')); }); } + } }()); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.spec.js index 04894f804f..f482d0a577 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.spec.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/keystone.service.spec.js @@ -536,5 +536,4 @@ }); }); }); - })();