From b244fcaf51696ea130d4b8f3a4202c71d9d4208d Mon Sep 17 00:00:00 2001 From: Rajat Vig Date: Fri, 9 Oct 2015 08:40:32 -0700 Subject: [PATCH] Add Cinder Extensions Service for Angular This also changes the nova extensions service to be a generically reusable extensions service. Change-Id: If7464f86e9c79902d780d49893a246a19ba553bf Partially-Implements: blueprint cinder-extensions.service --- .../cinder-extensions.service.js | 49 +++++++++ .../cinder-extensions.service.spec.js | 83 ++++++++++++++ .../extensions.service.js | 102 ++++++++++++++++++ .../extensions.service.spec.js | 101 +++++++++++++++++ .../nova-extensions.service.js | 59 ++-------- .../nova-extensions.service.spec.js | 2 +- 6 files changed, 342 insertions(+), 54 deletions(-) create mode 100644 openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.js create mode 100644 openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.spec.js create mode 100644 openstack_dashboard/static/app/core/openstack-service-api/extensions.service.js create mode 100644 openstack_dashboard/static/app/core/openstack-service-api/extensions.service.spec.js diff --git a/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.js b/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.js new file mode 100644 index 0000000000..91ac274b6e --- /dev/null +++ b/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.js @@ -0,0 +1,49 @@ +/** + * 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.app.core.openstack-service-api') + .factory('horizon.app.core.openstack-service-api.cinderExtensions', cinderExtensionsAPI); + + cinderExtensionsAPI.$inject = [ + '$cacheFactory', + 'horizon.app.core.openstack-service-api.extensions', + 'horizon.app.core.openstack-service-api.cinder' + ]; + + /** + * @ngdoc service + * @name horizon.app.core.openstack-service-api.cinderExtensions + * @description + * Provides cached access to Cinder Extensions with utilities to help + * with asynchronous data loading. The cache may be reset at any time + * by accessing the cache and calling removeAll. The next call to any + * function will retrieve fresh results. + * + * The enabled extensions do not change often, so using cached data will + * speed up results. Even on a local devstack in informal testing, + * this saved between 30 - 100 ms per request. + */ + function cinderExtensionsAPI($cacheFactory, extensionsAPI, cinderAPI) { + return extensionsAPI({ + cacheFactory: $cacheFactory( + 'horizon.app.core.openstack-service-api.cinderExtensions', + {capacity: 1} + ), + serviceAPI: cinderAPI + }); + } +}()); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.spec.js new file mode 100644 index 0000000000..a13d19807e --- /dev/null +++ b/openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.spec.js @@ -0,0 +1,83 @@ +/* + * 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'; + + describe("cinderExtensions", function() { + var factory, q, cinderAPI; + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(module(function($provide) { + cinderAPI = {getExtensions: function() {return {then: angular.noop};}}; + q = {defer: function() { return {resolve: angular.noop}; }}; + $provide.value('$cacheFactory', function() {return "cache";}); + $provide.value('$q', q); + $provide.value('horizon.app.core.openstack-service-api.cinder', cinderAPI); + })); + + beforeEach(inject(function($injector) { + factory = $injector.get('horizon.app.core.openstack-service-api.cinderExtensions'); + })); + + it("is defined", function() { + expect(factory).toBeDefined(); + }); + + it("defines .cache", function() { + expect(factory.cache).toBeDefined(); + }); + + it("defines .get", function() { + expect(factory.get).toBeDefined(); + var postAction = {then: angular.noop}; + spyOn(cinderAPI, 'getExtensions').and.returnValue(postAction); + spyOn(postAction, 'then'); + factory.get(); + expect(cinderAPI.getExtensions).toHaveBeenCalledWith({cache: factory.cache}); + expect(postAction.then).toHaveBeenCalled(); + var func = postAction.then.calls.argsFor(0)[0]; + var testData = {data: {items: [1, 2, 3]}}; + expect(func(testData)).toEqual([1, 2, 3]); + }); + + it("defines .ifNameEnabled", function() { + expect(factory.ifNameEnabled).toBeDefined(); + var postAction = {then: angular.noop}; + var deferred = {reject: angular.noop, resolve: angular.noop}; + spyOn(q, 'defer').and.returnValue(deferred); + spyOn(factory, 'get').and.returnValue(postAction); + spyOn(postAction, 'then'); + factory.ifNameEnabled("desired"); + expect(factory.get).toHaveBeenCalled(); + var func1 = postAction.then.calls.argsFor(0)[0]; + var func2 = postAction.then.calls.argsFor(0)[1]; + spyOn(deferred, 'reject'); + func1(); + expect(deferred.reject).toHaveBeenCalled(); + + spyOn(deferred, 'resolve'); + var extensions = [{name: "desired"}]; + func1(extensions); + expect(deferred.resolve).toHaveBeenCalled(); + + deferred.reject.calls.reset(); + func2(); + expect(deferred.reject).toHaveBeenCalledWith('Cannot get the extension list.'); + }); + + }); + +})(); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.js b/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.js new file mode 100644 index 0000000000..726b0f5596 --- /dev/null +++ b/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.js @@ -0,0 +1,102 @@ +/** + * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. + * + * 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.app.core.openstack-service-api') + .factory('horizon.app.core.openstack-service-api.extensions', extensions); + + extensions.$inject = [ + '$q' + ]; + + /** + * @ngdoc service + * @name horizon.app.core.openstack-service-api.extensions + * @description + * Provides cached access to Extensions with utilities to help + * with asynchronous data loading. The cache may be reset at any time + * by accessing the cache and calling removeAll. The next call to any + * function will retrieve fresh results. + * + * The enabled extensions do not change often, so using cached data will + * speed up results. Even on a local devstack in informal testing, + * this saved between 30 - 100 ms per request. + * + * This is modeled to be used by other Openstack Services not directly. + * + */ + function extensions($q) { + return function(spec) { + return createService(spec.serviceAPI, spec.cacheFactory); + }; + + function createService(serviceAPI, cacheFactory) { + var service = { + cache: cacheFactory, + get: get, + ifNameEnabled: ifNameEnabled + }; + + return service; + + /////////// + + function get() { + return serviceAPI.getExtensions({cache: service.cache}).then(onGetExtensions); + } + + function onGetExtensions(data) { + return data.data.items; + } + + function ifNameEnabled(desired) { + var deferred = $q.defer(); + + service.get().then(onDataLoaded, onDataFailure); + + function onDataLoaded(extensions) { + if (enabled(extensions, 'name', desired)) { + deferred.resolve(); + } else { + deferred.reject(interpolate( + gettext('Extension is not enabled: %(extension)s.'), + {extension: desired}, + true)); + } + } + + function onDataFailure() { + deferred.reject(gettext('Cannot get the extension list.')); + } + + return deferred.promise; + } + + function enabled(resources, key, desired) { + if (resources) { + return resources.some(function matchResource(resource) { + return resource[key] === desired; + }); + } else { + return false; + } + } + } + } + +}()); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.spec.js new file mode 100644 index 0000000000..4bbbe9b2cf --- /dev/null +++ b/openstack_dashboard/static/app/core/openstack-service-api/extensions.service.spec.js @@ -0,0 +1,101 @@ +/* + * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. + * + * 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'; + + describe("novaExtensions", function() { + var q = { + defer: function() { + return { + resolve: angular.noop + }; + } + }; + + var cacheFactory = function() { + return "cache"; + }; + + var serviceAPI = { + getExtensions: function() { + return {then: angular.noop}; + } + }; + + var factory; + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(module(function($provide) { + $provide.value('$q', q); + })); + + beforeEach(inject(function($injector) { + var factoryCreator = $injector.get('horizon.app.core.openstack-service-api.extensions'); + + factory = factoryCreator({cacheFactory: cacheFactory, serviceAPI: serviceAPI}); + })); + + it("is defined", function() { + expect(factory).toBeDefined(); + }); + + it("defines .cache", function() { + expect(factory.cache).toBeDefined(); + }); + + it("defines .get", function() { + expect(factory.get).toBeDefined(); + var postAction = {then: angular.noop}; + spyOn(serviceAPI, 'getExtensions').and.returnValue(postAction); + spyOn(postAction, 'then'); + factory.get(); + expect(serviceAPI.getExtensions).toHaveBeenCalledWith({cache: factory.cache}); + expect(postAction.then).toHaveBeenCalled(); + var func = postAction.then.calls.argsFor(0)[0]; + var testData = {data: {items: [1, 2, 3]}}; + expect(func(testData)).toEqual([1, 2, 3]); + }); + + it("defines .ifNameEnabled", function() { + expect(factory.ifNameEnabled).toBeDefined(); + var postAction = {then: angular.noop}; + var deferred = {reject: angular.noop, resolve: angular.noop}; + spyOn(q, 'defer').and.returnValue(deferred); + spyOn(factory, 'get').and.returnValue(postAction); + spyOn(postAction, 'then'); + factory.ifNameEnabled("desired"); + expect(factory.get).toHaveBeenCalled(); + var func1 = postAction.then.calls.argsFor(0)[0]; + var func2 = postAction.then.calls.argsFor(0)[1]; + spyOn(deferred, 'reject'); + func1(); + expect(deferred.reject).toHaveBeenCalled(); + + spyOn(deferred, 'resolve'); + var extensions = [{name: "desired"}]; + func1(extensions); + expect(deferred.resolve).toHaveBeenCalled(); + + deferred.reject.calls.reset(); + func2(); + expect(deferred.reject).toHaveBeenCalledWith('Cannot get the extension list.'); + }); + + }); + +})(); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.js b/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.js index 80d7f5e727..822a157f28 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.js @@ -22,7 +22,7 @@ novaExtensionsAPI.$inject = [ '$cacheFactory', - '$q', + 'horizon.app.core.openstack-service-api.extensions', 'horizon.app.core.openstack-service-api.nova' ]; @@ -39,60 +39,13 @@ * speed up results. Even on a local devstack in informal testing, * this saved between 30 - 100 ms per request. */ - function novaExtensionsAPI($cacheFactory, $q, novaAPI) { - var service = { - cache: $cacheFactory( + function novaExtensionsAPI($cacheFactory, extensionsAPI, novaAPI) { + return extensionsAPI({ + cacheFactory: $cacheFactory( 'horizon.app.core.openstack-service-api.novaExtensions', {capacity: 1} ), - get: get, - ifNameEnabled: ifNameEnabled - }; - - return service; - - /////////// - - function get() { - return novaAPI.getExtensions({cache: service.cache}).then(onGetExtensions); - } - - function onGetExtensions(data) { - return data.data.items; - } - - function ifNameEnabled(desired) { - var deferred = $q.defer(); - - service.get().then(onDataLoaded, onDataFailure); - - function onDataLoaded(extensions) { - if (enabled(extensions, 'name', desired)) { - deferred.resolve(); - } else { - deferred.reject(interpolate( - gettext('Extension is not enabled: %(extension)s.'), - {extension: desired}, - true)); - } - } - - function onDataFailure() { - deferred.reject(gettext('Cannot get the Nova extension list.')); - } - - return deferred.promise; - } - - function enabled(resources, key, desired) { - if (resources) { - return resources.some(function matchResource(resource) { - return resource[key] === desired; - }); - } else { - return false; - } - } - + serviceAPI: novaAPI + }); } }()); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.spec.js index fbf1c892c5..7714b5d68f 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.spec.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.spec.js @@ -77,7 +77,7 @@ deferred.reject.calls.reset(); func2(); - expect(deferred.reject).toHaveBeenCalledWith('Cannot get the Nova extension list.'); + expect(deferred.reject).toHaveBeenCalledWith('Cannot get the extension list.'); }); });