From 4982dc3cdaa94dfb1960207656ec20fe60d84388 Mon Sep 17 00:00:00 2001 From: Shu Muto Date: Tue, 4 Jul 2017 15:55:34 +0900 Subject: [PATCH] Angularize Key Pairs index view This patch adds Angularized index view. Actions and details view will be implemented in subsequent patches. To use Angularized Key Pairs index view, copy following file: openstack_dashboard/local/local_settings.d/ _11_toggle_angular_features.py.example to: _11_toggle_angular_features.py and add following line into _11_toggle_angular_features.py: ANGULAR_FEATURES.update({'key_pairs_panel': True}) Change-Id: Ie1bf555430f77fc6bc95deedb8328caa5a2287aa Partial-Implements: blueprint ng-keypairs --- doc/source/configuration/settings.rst | 1 + .../dashboards/project/key_pairs/urls.py | 25 +++++-- openstack_dashboard/settings.py | 1 + .../static/app/core/_core.scss | 1 + .../static/app/core/keypairs/_keypairs.scss | 9 +++ .../app/core/keypairs/details/drawer.html | 5 ++ .../app/core/keypairs/keypairs.module.js | 74 ++++++++++++++++++- .../app/core/keypairs/keypairs.module.spec.js | 23 ++++++ .../app/core/keypairs/keypairs.service.js | 66 +++++++++++++++++ .../core/keypairs/keypairs.service.spec.js | 54 ++++++++++++++ .../static/app/core/keypairs/panel.html | 5 ++ .../bp-ng-keypairs-876c38a1a8aed60f.yaml | 8 ++ 12 files changed, 264 insertions(+), 8 deletions(-) create mode 100644 openstack_dashboard/static/app/core/keypairs/_keypairs.scss create mode 100644 openstack_dashboard/static/app/core/keypairs/details/drawer.html create mode 100644 openstack_dashboard/static/app/core/keypairs/keypairs.service.js create mode 100644 openstack_dashboard/static/app/core/keypairs/keypairs.service.spec.js create mode 100644 openstack_dashboard/static/app/core/keypairs/panel.html create mode 100644 releasenotes/notes/bp-ng-keypairs-876c38a1a8aed60f.yaml diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index 14d9a039af..1fcd1e16d3 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -48,6 +48,7 @@ Default: { 'images_panel': True, + 'key_pairs_panel': False, 'flavors_panel': False, 'users_panel': False, 'roles_panel': False, diff --git a/openstack_dashboard/dashboards/project/key_pairs/urls.py b/openstack_dashboard/dashboards/project/key_pairs/urls.py index 9c5b2015f1..5c4f3af621 100644 --- a/openstack_dashboard/dashboards/project/key_pairs/urls.py +++ b/openstack_dashboard/dashboards/project/key_pairs/urls.py @@ -16,12 +16,23 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings from django.conf.urls import url -from openstack_dashboard.dashboards.project.key_pairs import views +from django.utils.translation import ugettext_lazy as _ -urlpatterns = [ - url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^import/$', views.ImportView.as_view(), name='import'), - url(r'^(?P[^/]+)/$', views.DetailView.as_view(), - name='detail'), -] +from horizon.browsers import views +from openstack_dashboard.dashboards.project.key_pairs import views as \ + legacy_views + +if settings.ANGULAR_FEATURES.get('key_pairs_panel'): + title = _("Key Pairs") + urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), + ] +else: + urlpatterns = [ + url(r'^$', legacy_views.IndexView.as_view(), name='index'), + url(r'^import/$', legacy_views.ImportView.as_view(), name='import'), + url(r'^(?P[^/]+)/$', legacy_views.DetailView.as_view(), + name='detail'), + ] diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index 51db4cab04..4284c5e646 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -324,6 +324,7 @@ COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context' # Dictionary of currently available angular features ANGULAR_FEATURES = { 'images_panel': True, + 'key_pairs_panel': False, 'flavors_panel': False, 'users_panel': False, 'roles_panel': False, diff --git a/openstack_dashboard/static/app/core/_core.scss b/openstack_dashboard/static/app/core/_core.scss index c46c3bf362..1501200102 100644 --- a/openstack_dashboard/static/app/core/_core.scss +++ b/openstack_dashboard/static/app/core/_core.scss @@ -1 +1,2 @@ @import "images/images"; +@import "keypairs/keypairs"; diff --git a/openstack_dashboard/static/app/core/keypairs/_keypairs.scss b/openstack_dashboard/static/app/core/keypairs/_keypairs.scss new file mode 100644 index 0000000000..301a8c0cc6 --- /dev/null +++ b/openstack_dashboard/static/app/core/keypairs/_keypairs.scss @@ -0,0 +1,9 @@ +hz-resource-property-list[resource-type-name="OS::Nova::Keypair"] { + hz-resource-property[prop-name="public_key"] dd { + overflow-wrap: break-word; + width: calc(100vw - 61px); + @media (min-width: 992px) { + width: calc(100vw - 281px); + } + } +} diff --git a/openstack_dashboard/static/app/core/keypairs/details/drawer.html b/openstack_dashboard/static/app/core/keypairs/details/drawer.html new file mode 100644 index 0000000000..171100261c --- /dev/null +++ b/openstack_dashboard/static/app/core/keypairs/details/drawer.html @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/openstack_dashboard/static/app/core/keypairs/keypairs.module.js b/openstack_dashboard/static/app/core/keypairs/keypairs.module.js index 6e2285c001..970a95255a 100644 --- a/openstack_dashboard/static/app/core/keypairs/keypairs.module.js +++ b/openstack_dashboard/static/app/core/keypairs/keypairs.module.js @@ -27,7 +27,79 @@ */ angular .module('horizon.app.core.keypairs', [ + 'ngRoute', + 'horizon.app.core', + 'horizon.framework.conf' ]) - ; + .constant('horizon.app.core.keypairs.resourceType', 'OS::Nova::Keypair') + .run(run) + .config(config); + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.nova', + 'horizon.app.core.keypairs.basePath', + 'horizon.app.core.keypairs.resourceType', + 'horizon.app.core.keypairs.service' + ]; + + function run(registry, nova, basePath, resourceType, keypairsService) { + registry.getResourceType(resourceType) + .setNames(gettext('Key Pair'), gettext('Key Pairs')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + .setProperties(keypairProperties()) + .setListFunction(keypairsService.getKeypairsPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true + }) + .append({ + id: 'fingerprint', + priority: 2 + }); + + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }); + } + + function keypairProperties() { + return { + 'id': {label: gettext('ID'), filters: ['noValue'] }, + 'name': {label: gettext('Name'), filters: ['noName'] }, + 'fingerprint': {label: gettext('Fingerprint'), filters: ['noValue'] }, + 'created_at': {label: gettext('Created'), filters: ['simpleDate'] }, + 'user_id': {label: gettext('User ID'), filters: ['noValue'] }, + 'public_key': {label: gettext('Public Key'), filters: ['noValue'] } + }; + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'app/core/keypairs/'; + $provide.constant('horizon.app.core.keypairs.basePath', path); + $routeProvider.when('/project/key_pairs', { + templateUrl: path + 'panel.html' + }); + } })(); diff --git a/openstack_dashboard/static/app/core/keypairs/keypairs.module.spec.js b/openstack_dashboard/static/app/core/keypairs/keypairs.module.spec.js index 15fb1144bd..c945184e9e 100644 --- a/openstack_dashboard/static/app/core/keypairs/keypairs.module.spec.js +++ b/openstack_dashboard/static/app/core/keypairs/keypairs.module.spec.js @@ -22,4 +22,27 @@ }); }); + describe('loading the module', function () { + var registry; + + beforeEach(module('horizon.app.core.keypairs')); + beforeEach(inject(function($injector) { + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('registers names', function() { + expect(registry.getResourceType('OS::Nova::Keypair').getName()).toBe("Key Pairs"); + }); + + it('should set facets for search', function () { + var names = registry.getResourceType('OS::Nova::Keypair').filterFacets + .map(getName); + expect(names).toContain('name'); + + function getName(x) { + // underscore.js and .pluck() would be great here. + return x.name; + } + }); + }); })(); diff --git a/openstack_dashboard/static/app/core/keypairs/keypairs.service.js b/openstack_dashboard/static/app/core/keypairs/keypairs.service.js new file mode 100644 index 0000000000..e9c23f0370 --- /dev/null +++ b/openstack_dashboard/static/app/core/keypairs/keypairs.service.js @@ -0,0 +1,66 @@ +/* + * 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.keypairs') + .factory('horizon.app.core.keypairs.service', keypairsService); + + keypairsService.$inject = [ + '$filter', + 'horizon.app.core.openstack-service-api.nova' + ]; + + /* + * @ngdoc factory + * @name horizon.app.core.keypairs.service + * + * @description + * This service provides functions that are used through the key pair + * features. These are primarily used in the module registrations + * but do not need to be restricted to such use. Each exposed function + * is documented below. + */ + function keypairsService($filter, nova) { + return { + getKeypairsPromise: getKeypairsPromise + }; + + /* + * @ngdoc function + * @name getKeypairsPromise + * @description + * Given filter/query parameters, returns a promise for the matching + * key pairs. This is used in displaying lists of key pairs. In this + * case, we need to modify the API's response by adding a composite value + * called 'trackBy' to assist the display mechanism when updating rows. + */ + function getKeypairsPromise(params) { + return nova.getKeypairs(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItems)}}; + + function modifyItems(item) { + item = item.keypair; + item.trackBy = item.name; + return item; + } + } + } + + } + +})(); diff --git a/openstack_dashboard/static/app/core/keypairs/keypairs.service.spec.js b/openstack_dashboard/static/app/core/keypairs/keypairs.service.spec.js new file mode 100644 index 0000000000..92a5496f31 --- /dev/null +++ b/openstack_dashboard/static/app/core/keypairs/keypairs.service.spec.js @@ -0,0 +1,54 @@ +/* + * 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('horizon.app.core.keypairs', function () { + it('should exist', function () { + expect(angular.module('horizon.app.core.keypairs')).toBeDefined(); + }); + }); + + describe('keypairsService', function() { + var service; + beforeEach(module('horizon.app.core')); + beforeEach(inject(function($injector) { + service = $injector.get('horizon.app.core.keypairs.service'); + })); + + describe('getKeypairsPromise', function() { + it("provides a promise that gets translated", inject(function($q, $injector, $timeout) { + var nova = $injector.get('horizon.app.core.openstack-service-api.nova'); + var session = $injector.get('horizon.app.core.openstack-service-api.userSession'); + var deferred = $q.defer(); + var deferredSession = $q.defer(); + spyOn(nova, 'getKeypairs').and.returnValue(deferred.promise); + spyOn(session, 'get').and.returnValue(deferredSession.promise); + var result = service.getKeypairsPromise({}); + deferredSession.resolve({}); + deferred.resolve({ + data: { + items: [{keypair: {name: 'keypair1'}}] + } + }); + $timeout.flush(); + expect(nova.getKeypairs).toHaveBeenCalled(); + expect(result.$$state.value.data.items[0].name).toBe('keypair1'); + expect(result.$$state.value.data.items[0].trackBy).toBe('keypair1'); + })); + }); + + }); + +})(); diff --git a/openstack_dashboard/static/app/core/keypairs/panel.html b/openstack_dashboard/static/app/core/keypairs/panel.html new file mode 100644 index 0000000000..e27a6c7545 --- /dev/null +++ b/openstack_dashboard/static/app/core/keypairs/panel.html @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/releasenotes/notes/bp-ng-keypairs-876c38a1a8aed60f.yaml b/releasenotes/notes/bp-ng-keypairs-876c38a1a8aed60f.yaml new file mode 100644 index 0000000000..840bd7e7fd --- /dev/null +++ b/releasenotes/notes/bp-ng-keypairs-876c38a1a8aed60f.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + [`blueprint ng-keypairs `_] + Add Angular Key Pairs panel. The Key Pairs panel allows users to view + a list of created or imported key pairs. This panel displays a table + view of the name and fingerprint of each key pair. Also, public key + is shown in expanded row.