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 index 2f40a0b880..9487ad59f8 100644 --- 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 @@ -34,6 +34,7 @@ 'horizon.framework.conf.resource-type-registry.service', 'horizon.dashboard.identity.groups.actions.create.service', 'horizon.dashboard.identity.groups.actions.delete.service', + 'horizon.dashboard.identity.groups.actions.edit.service', 'horizon.dashboard.identity.groups.resourceType' ]; @@ -41,11 +42,19 @@ registry, createService, deleteService, + editService, groupResourceTypeCode ) { var groupResourceType = registry.getResourceType(groupResourceTypeCode); groupResourceType.itemActions + .append({ + id: 'editAction', + service: editService, + template: { + text: gettext('Edit Group') + } + }) .append({ id: 'deleteAction', service: deleteService, diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.js new file mode 100644 index 0000000000..0e865c4d88 --- /dev/null +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.js @@ -0,0 +1,106 @@ +/** + * 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.edit.service', editService); + + editService.$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.widgets.toast.service' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.identity.groups.actions.edit.service + * @Description A service to handle the Edit Group modal. + */ + function editService( + $q, + resourceType, + keystoneAPI, + policy, + modalFormService, + actionResultService, + toast + ) { + var service = { + allowed: allowed, + perform: perform, + onLoad: onLoad, + submit: submit, + onSuccess: onSuccess + }; + + return service; + + ////////////// + + function allowed() { + return $q.all([ + keystoneAPI.canEditIdentity('group'), + policy.ifAllowed({ rules: [['identity', 'identity:update_group']] }) + ]); + } + + function perform(group) { + return keystoneAPI.getGroup(group.id).then(service.onLoad); + } + + function onLoad(response) { + var schema = { + type: 'object', + properties: { + name: { + title: gettext('Name'), + type: 'string' + }, + description: { + title: gettext('Description'), + type: 'string' + } + }, + required: ['name'] + }; + + var config = { + title: gettext('Edit Group'), + schema: schema, + form: ['*'], + model: response.data + }; + return modalFormService.open(config).then(service.submit); + } + + function submit(context) { + return keystoneAPI.editGroup(context.model).then(service.onSuccess); + } + + function onSuccess(response) { + toast.add('success', gettext('Group updated successfully.')); + + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.id) + .result; + } + } +})(); diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.spec.js b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.spec.js new file mode 100644 index 0000000000..3c369bf25b --- /dev/null +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/actions/edit.action.service.spec.js @@ -0,0 +1,101 @@ +/** + * 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.edit.service', function() { + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.dashboard.identity.groups')); + beforeEach(module('horizon.framework')); + + var modalService, service, keystoneAPI, policyAPI, toast, resourceType, $q, $scope; + + beforeEach(inject(function($injector, _$q_, _$rootScope_) { + service = $injector.get('horizon.dashboard.identity.groups.actions.edit.service'); + keystoneAPI = $injector.get('horizon.app.core.openstack-service-api.keystone'); + modalService = $injector.get('horizon.framework.widgets.form.ModalFormService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + toast = $injector.get('horizon.framework.widgets.toast.service'); + resourceType = $injector.get('horizon.dashboard.identity.groups.resourceType'); + $q = _$q_; + $scope = _$rootScope_.$new(); + })); + + describe('perform', function() { + it('should fetch the correct group', function test() { + var deferred = $q.defer(); + deferred.resolve({id: 1, name: 'spam roll'}); + spyOn(keystoneAPI, 'getGroup').and.returnValue(deferred.promise); + spyOn(service, 'onLoad'); + + service.perform({id: 2}); + $scope.$apply(); + + expect(keystoneAPI.getGroup).toHaveBeenCalled(); + expect(service.onLoad).toHaveBeenCalledWith({id: 1, name: 'spam roll'}); + }); + + it('should open the edit modal', function test() { + var group = {id: 1, name: 'spam roll'}; + var edited = {id: 1, name: 'egg roll'}; + var deferred = $q.defer(); + spyOn(modalService, 'open').and.returnValue(deferred.promise); + spyOn(service, 'submit'); + + deferred.resolve(edited); + service.onLoad({data: group}); + $scope.$apply(); + + expect(modalService.open).toHaveBeenCalled(); + var config = modalService.open.calls.argsFor(0)[0]; + expect(config.model).toEqual(group); + expect(config.schema).toBeDefined(); + expect(service.submit).toHaveBeenCalledWith(edited); + }); + + it('should handle edit modal submission', function test() { + var deferred = $q.defer(); + spyOn(keystoneAPI, 'editGroup').and.returnValue(deferred.promise); + spyOn(service, 'onSuccess'); + + deferred.resolve('result'); + service.submit({model: 'model'}); + $scope.$apply(); + + expect(keystoneAPI.editGroup).toHaveBeenCalledWith('model'); + expect(service.onSuccess).toHaveBeenCalledWith('result'); + }); + + it('should handle successful edit', function test() { + spyOn(toast, 'add'); + var result = service.onSuccess({config: {data: {id: 2}}}); + + expect(result.updated).toEqual([{type: resourceType, id: 2}]); + expect(toast.add).toHaveBeenCalled(); + }); + + }); + + describe('allow method', function() { + it('should use default policy if batch action', function test() { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of edit + +})(); 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 e64006460d..cb58afd1e2 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 @@ -74,7 +74,16 @@ }); function listFunction() { - return keystone.getGroups(); + return keystone.getGroups().then(modifyResponse); + } + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.domain_id + item.name + item.description; + return item; + } } /** diff --git a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/panel.html b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/panel.html index 4153b98aa8..001550813c 100644 --- a/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/panel.html +++ b/openstack_dashboard/dashboards/identity/static/dashboard/identity/groups/panel.html @@ -1,4 +1,5 @@ - +