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
This commit is contained in:
Shu Muto 2017-07-04 15:55:34 +09:00
parent ffd9140ae8
commit 4982dc3cda
12 changed files with 264 additions and 8 deletions

View File

@ -48,6 +48,7 @@ Default:
{
'images_panel': True,
'key_pairs_panel': False,
'flavors_panel': False,
'users_panel': False,
'roles_panel': False,

View File

@ -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<keypair_name>[^/]+)/$', 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<keypair_name>[^/]+)/$', legacy_views.DetailView.as_view(),
name='detail'),
]

View File

@ -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,

View File

@ -1 +1,2 @@
@import "images/images";
@import "keypairs/keypairs";

View File

@ -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);
}
}
}

View File

@ -0,0 +1,5 @@
<hz-resource-property-list
resource-type-name="OS::Nova::Keypair"
item="item"
property-groups="[['fingerprint', 'public_key']]">
</hz-resource-property-list>

View File

@ -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'
});
}
})();

View File

@ -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;
}
});
});
})();

View File

@ -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;
}
}
}
}
})();

View File

@ -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');
}));
});
});
})();

View File

@ -0,0 +1,5 @@
<hz-resource-panel resource-type-name="OS::Nova::Keypair">
<hz-resource-table resource-type-name="OS::Nova::Keypair"
track-by="trackBy">
</hz-resource-table>
</hz-resource-panel>

View File

@ -0,0 +1,8 @@
---
features:
- |
[`blueprint ng-keypairs <https://blueprints.launchpad.net/horizon/+spec/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.