Merge "Final changes to Angular Images before making default"
This commit is contained in:
commit
28b6d6b30a
@ -19,6 +19,7 @@
|
||||
angular
|
||||
.module('horizon.framework.util.filters')
|
||||
.filter('yesno', yesNoFilter)
|
||||
.filter('simpleDate', simpleDateFilter)
|
||||
.filter('gb', gbFilter)
|
||||
.filter('mb', mbFilter)
|
||||
.filter('title', titleFilter)
|
||||
@ -45,6 +46,19 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name simpleDate
|
||||
* @description
|
||||
* Evaluates given for display as a short date, returning '-' if empty.
|
||||
*/
|
||||
simpleDateFilter.$inject = ['$filter'];
|
||||
function simpleDateFilter($filter) {
|
||||
return function (input) {
|
||||
return $filter('noValue')($filter('date')(input, 'short'));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name gb
|
||||
|
@ -38,6 +38,21 @@
|
||||
});
|
||||
});
|
||||
|
||||
describe('simpleDate', function () {
|
||||
var simpleDateFilter;
|
||||
beforeEach(inject(function (_simpleDateFilter_) {
|
||||
simpleDateFilter = _simpleDateFilter_;
|
||||
}));
|
||||
|
||||
it('returns blank if nothing', function () {
|
||||
expect(simpleDateFilter()).toBe('-');
|
||||
});
|
||||
|
||||
it('returns the expected time', function() {
|
||||
expect(simpleDateFilter('2016-06-24T04:19:07')).toBe('6/24/16 4:19 AM');
|
||||
});
|
||||
});
|
||||
|
||||
describe('gb', function () {
|
||||
var gbFilter;
|
||||
beforeEach(inject(function (_gbFilter_) {
|
||||
|
@ -0,0 +1,5 @@
|
||||
<hz-resource-panel resource-type-name="OS::Glance::Image">
|
||||
<hz-resource-table resource-type-name="OS::Glance::Image"
|
||||
track-by="trackBy"
|
||||
list-function-extra-params="{is_public: 'None'}"></hz-resource-table>
|
||||
</hz-resource-panel>
|
@ -30,27 +30,23 @@
|
||||
registerImageDetails.$inject = [
|
||||
'horizon.app.core.images.basePath',
|
||||
'horizon.app.core.images.resourceType',
|
||||
'horizon.app.core.openstack-service-api.glance',
|
||||
'horizon.app.core.images.service',
|
||||
'horizon.framework.conf.resource-type-registry.service'
|
||||
];
|
||||
|
||||
function registerImageDetails(
|
||||
basePath,
|
||||
imageResourceType,
|
||||
glanceApi,
|
||||
imageService,
|
||||
registry
|
||||
) {
|
||||
registry.getResourceType(imageResourceType)
|
||||
.setLoadFunction(loadFunction)
|
||||
.setLoadFunction(imageService.getImagePromise)
|
||||
.detailsViews.append({
|
||||
id: 'imageDetailsOverview',
|
||||
name: gettext('Overview'),
|
||||
template: basePath + 'details/overview.html'
|
||||
});
|
||||
|
||||
function loadFunction(identifier) {
|
||||
return glanceApi.getImage(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -5,7 +5,7 @@
|
||||
item="item"
|
||||
property-groups="[
|
||||
['name', 'id'],
|
||||
['is_public', 'protected'],
|
||||
['visibility', 'protected'],
|
||||
['disk_format', 'size'],
|
||||
['min_disk', 'min_ram']]">
|
||||
</hz-resource-property-list>
|
||||
|
@ -23,12 +23,14 @@
|
||||
ImageOverviewController.$inject = [
|
||||
'horizon.app.core.images.resourceType',
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.app.core.openstack-service-api.userSession',
|
||||
'$scope'
|
||||
];
|
||||
|
||||
function ImageOverviewController(
|
||||
imageResourceTypeCode,
|
||||
registry,
|
||||
userSession,
|
||||
$scope
|
||||
) {
|
||||
var ctrl = this;
|
||||
@ -44,6 +46,12 @@
|
||||
ctrl.image.properties = Object.keys(ctrl.image.properties).map(function mapProps(prop) {
|
||||
return {name: prop, value: ctrl.image.properties[prop]};
|
||||
});
|
||||
|
||||
userSession.get().then(setProject);
|
||||
|
||||
function setProject(session) {
|
||||
ctrl.projectId = session.project_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,14 @@
|
||||
|
||||
beforeEach(module('horizon.app.core.images'));
|
||||
beforeEach(module('horizon.framework.conf'));
|
||||
beforeEach(inject(function($controller, $q) {
|
||||
beforeEach(inject(function($controller, $q, $injector) {
|
||||
var session = $injector.get('horizon.app.core.openstack-service-api.userSession');
|
||||
var deferred = $q.defer();
|
||||
var sessionDeferred = $q.defer();
|
||||
deferred.resolve({data: {properties: {'a': 'apple'}}});
|
||||
deferred.resolve({project_id: '12'});
|
||||
spyOn(glance, 'getNamespaces').and.returnValue(deferred.promise);
|
||||
spyOn(session, 'get').and.returnValue(sessionDeferred.promise);
|
||||
ctrl = $controller('ImageOverviewController',
|
||||
{
|
||||
'$scope': {context: {loadPromise: deferred.promise}}
|
||||
|
@ -8,45 +8,35 @@
|
||||
cls="dl-horizontal"
|
||||
item="ctrl.image"
|
||||
property-groups="[[
|
||||
'type', 'status', 'size', 'min_disk', 'min_ram', 'disk_format',
|
||||
'container_format']]">
|
||||
'id', 'type', 'status', 'size', 'min_disk', 'min_ram', 'disk_format',
|
||||
'container_format', 'created_at', 'updated_at']]">
|
||||
</hz-resource-property-list>
|
||||
</div>
|
||||
<div class="col-md-6 detail">
|
||||
<h3>{$ 'Security' | translate $}</h3>
|
||||
<hr>
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Owner</dt>
|
||||
<dd>{$ ctrl.image.owner $}</dd>
|
||||
<dt translate>Filename</dt>
|
||||
<dd>{$ ctrl.image.properties.filename $}</dd>
|
||||
<dd>{$ ctrl.image.properties.filename | noValue $}</dd>
|
||||
<dt translate>Visibility</dt>
|
||||
<dd>{$ ctrl.image | imageVisibility $}</dd>
|
||||
<dd>{$ ctrl.image | imageVisibility:ctrl.projectId $}</dd>
|
||||
<dt translate>Protected</dt>
|
||||
<dd>{$ ctrl.image.protected | yesno $}</dd>
|
||||
<dt translate>Checksum</dt>
|
||||
<dd>{$ ctrl.image.checksum $}</dd>
|
||||
<dd>{$ ctrl.image.checksum | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 detail">
|
||||
<h3 translate>Record Properties</h3>
|
||||
<hr>
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Created</dt>
|
||||
<dd>{$ ctrl.image.created_at | date:'short' $}</dd>
|
||||
<dt translate>Updated</dt>
|
||||
<dd>{$ ctrl.image.updated_at | date:'short' $}</dd>
|
||||
<dt translate>ID</dt>
|
||||
<dd>{$ ctrl.image.id $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-md-6 detail">
|
||||
<h3 translate>Custom Properties</h3>
|
||||
<hr>
|
||||
<dl class="dl-horizontal">
|
||||
<div ng-repeat="prop in ctrl.image.properties">
|
||||
<dt data-toggle="tooltip" title="{$ prop.name $}">{$ ctrl.resourceType.label(prop.name) $}</dt>
|
||||
<dd>{$ ctrl.resourceType.format(prop.name, prop.value) $}</dd>
|
||||
<dd>{$ prop.value $}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
@ -81,7 +81,8 @@
|
||||
filters: ['uppercase']
|
||||
})
|
||||
.setProperty('created_at', {
|
||||
label: gettext('Created At')
|
||||
label: gettext('Created At'),
|
||||
filters: ['simpleDate']
|
||||
})
|
||||
.setProperty('disk_format', {
|
||||
label: gettext('Disk Format'),
|
||||
@ -90,10 +91,6 @@
|
||||
.setProperty('id', {
|
||||
label: gettext('ID')
|
||||
})
|
||||
.setProperty('is_public', {
|
||||
label: gettext('Is Public'),
|
||||
filters: ['yesno']
|
||||
})
|
||||
.setProperty('type', {
|
||||
label: gettext('Type'),
|
||||
filters: [imagesService.imageType]
|
||||
@ -129,7 +126,8 @@
|
||||
label: gettext('Tags')
|
||||
})
|
||||
.setProperty('updated_at', {
|
||||
label: gettext('Updated At')
|
||||
label: gettext('Updated At'),
|
||||
filters: ['simpleDate']
|
||||
})
|
||||
.setProperty('virtual_size', {
|
||||
label: gettext('Virtual Size')
|
||||
@ -172,6 +170,10 @@
|
||||
priority: 1,
|
||||
itemInTransitionFunction: imagesService.isInTransition
|
||||
})
|
||||
.append({
|
||||
id: 'visibility',
|
||||
priority: 1
|
||||
})
|
||||
.append({
|
||||
id: 'protected',
|
||||
priority: 1
|
||||
@ -208,6 +210,18 @@
|
||||
{label: gettext('Deleted'), key: 'deleted'}
|
||||
]
|
||||
})
|
||||
.append({
|
||||
label: gettext('Visibility'),
|
||||
name: 'visibility',
|
||||
isServer: false,
|
||||
singleton: true,
|
||||
options: [
|
||||
{label: gettext('Public'), key: gettext('Public')},
|
||||
{label: gettext('Private'), key: gettext('Private')},
|
||||
{label: gettext('Shared With Project'), key: gettext('Shared With Project')},
|
||||
{label: gettext('Unknown'), key: 'unknown'}
|
||||
]
|
||||
})
|
||||
.append({
|
||||
label: gettext('Protected'),
|
||||
name: 'protected',
|
||||
@ -330,7 +344,7 @@
|
||||
});
|
||||
|
||||
$routeProvider.when('/admin/images/', {
|
||||
templateUrl: path + 'panel.html'
|
||||
templateUrl: path + 'admin-panel.html'
|
||||
});
|
||||
|
||||
function goToAngularDetails(params) {
|
||||
|
@ -20,7 +20,9 @@
|
||||
.factory('horizon.app.core.images.service', imageService);
|
||||
|
||||
imageService.$inject = [
|
||||
'$filter',
|
||||
'horizon.app.core.openstack-service-api.glance',
|
||||
'horizon.app.core.openstack-service-api.userSession',
|
||||
'horizon.app.core.images.transitional-statuses'
|
||||
];
|
||||
|
||||
@ -34,9 +36,10 @@
|
||||
* but do not need to be restricted to such use. Each exposed function
|
||||
* is documented below.
|
||||
*/
|
||||
function imageService(glance, transitionalStatuses) {
|
||||
function imageService($filter, glance, userSession, transitionalStatuses) {
|
||||
return {
|
||||
getDetailsPath: getDetailsPath,
|
||||
getImagePromise: getImagePromise,
|
||||
getImagesPromise: getImagesPromise,
|
||||
imageType: imageType,
|
||||
isInTransition: isInTransition
|
||||
@ -99,17 +102,34 @@
|
||||
* 'trackBy' to assist the display mechanism when updating rows.
|
||||
*/
|
||||
function getImagesPromise(params) {
|
||||
return glance.getImages(params).then(modifyResponse);
|
||||
var projectId;
|
||||
return userSession.get().then(getImages);
|
||||
|
||||
function getImages(userSession) {
|
||||
projectId = userSession.project_id;
|
||||
return glance.getImages(params).then(modifyResponse);
|
||||
}
|
||||
|
||||
function modifyResponse(response) {
|
||||
return {data: {items: response.data.items.map(addTrackBy)}};
|
||||
return {data: {items: response.data.items.map(modifyImage)}};
|
||||
|
||||
function addTrackBy(image) {
|
||||
function modifyImage(image) {
|
||||
image.trackBy = image.id + image.updated_at;
|
||||
image.visibility = $filter('imageVisibility')(image, projectId);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @ngdoc function
|
||||
* @name getImagePromise
|
||||
* @description
|
||||
* Given an id, returns a promise for the image data.
|
||||
*/
|
||||
function getImagePromise(identifier) {
|
||||
return glance.getImage(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -86,14 +86,30 @@
|
||||
describe('getImagesPromise', function() {
|
||||
it("provides a promise that gets translated", inject(function($q, $injector, $timeout) {
|
||||
var glance = $injector.get('horizon.app.core.openstack-service-api.glance');
|
||||
var session = $injector.get('horizon.app.core.openstack-service-api.userSession');
|
||||
var deferred = $q.defer();
|
||||
var deferredSession = $q.defer();
|
||||
spyOn(glance, 'getImages').and.returnValue(deferred.promise);
|
||||
spyOn(session, 'get').and.returnValue(deferredSession.promise);
|
||||
var result = service.getImagesPromise({});
|
||||
deferred.resolve({data: {items: [{id: 1, updated_at: 'jul1'}]}});
|
||||
deferredSession.resolve({project_id: '12'});
|
||||
$timeout.flush();
|
||||
expect(result.$$state.value.data.items[0].trackBy).toBe('1jul1');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getImagePromise', function() {
|
||||
it("provides a promise", inject(function($q, $injector) {
|
||||
var glance = $injector.get('horizon.app.core.openstack-service-api.glance');
|
||||
var deferred = $q.defer();
|
||||
spyOn(glance, 'getImage').and.returnValue(deferred.promise);
|
||||
var result = service.getImagePromise({});
|
||||
deferred.resolve({id: 1, updated_at: 'jul1'});
|
||||
expect(glance.getImage).toHaveBeenCalled();
|
||||
expect(result.$$state.value.updated_at).toBe('jul1');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
|
@ -155,7 +155,14 @@
|
||||
}
|
||||
|
||||
function onGetAvailabilityZones(response) {
|
||||
ctrl.availabilityZones = response.items;
|
||||
ctrl.availabilityZones = response.items.map(justNames);
|
||||
if (ctrl.availabilityZones.length > 0) {
|
||||
ctrl.volume.availability_zone = ctrl.availabilityZones[0];
|
||||
}
|
||||
|
||||
function justNames(item) {
|
||||
return item.zoneName;
|
||||
}
|
||||
}
|
||||
|
||||
function onGetAbsoluteLimits(response) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
describe('horizon.app.core.images.controller.CreateVolumeController', function () {
|
||||
|
||||
var controller, quotaChartDefaults, $scope, $filter, getAbsoluteLimitsSpy;
|
||||
var controller, quotaChartDefaults, $scope, $filter, getAbsoluteLimitsSpy, nova;
|
||||
|
||||
var cinder = {
|
||||
getVolumeTypes: function() {
|
||||
@ -39,21 +39,21 @@
|
||||
}
|
||||
};
|
||||
|
||||
var nova = {
|
||||
getAvailabilityZones: function() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
return callback({ items: ['zone1'] });
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(module('horizon.app.core.images'));
|
||||
beforeEach(module('horizon.framework.widgets.charts'));
|
||||
beforeEach(module('horizon.framework.util.filters'));
|
||||
|
||||
beforeEach(inject(function ($injector, _$rootScope_, _$filter_) {
|
||||
|
||||
nova = {
|
||||
getAvailabilityZones: function() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
return callback({ items: [{zoneName: 'zone1'}] });
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
$scope = _$rootScope_.$new();
|
||||
$scope.image = {
|
||||
name: 'ImageName',
|
||||
@ -364,6 +364,37 @@
|
||||
|
||||
ctrl.volume.size = 100;
|
||||
|
||||
var emittedEventArgs = $scope.$emit.calls.argsFor(0);
|
||||
var expectedVolume = {
|
||||
size: 100,
|
||||
name: ctrl.image.name,
|
||||
description: '',
|
||||
volume_type: 'lvmdriver-1',
|
||||
availability_zone: 'zone1', // pre-selects first
|
||||
metadata: {},
|
||||
image_id: ctrl.image.id,
|
||||
snapshot_id: null,
|
||||
source_volid: null
|
||||
};
|
||||
|
||||
expect(emittedEventArgs[0]).toEqual('horizon.app.core.images.VOLUME_CHANGED');
|
||||
expect(emittedEventArgs[1]).toEqual(expectedVolume);
|
||||
});
|
||||
|
||||
it('not default the availability_zone if none present', function() {
|
||||
|
||||
nova.getAvailabilityZones = function() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
return callback({ items: [] });
|
||||
}
|
||||
};
|
||||
};
|
||||
var ctrl = createController();
|
||||
$scope.$apply();
|
||||
|
||||
ctrl.volume.size = 100;
|
||||
|
||||
var emittedEventArgs = $scope.$emit.calls.argsFor(0);
|
||||
var expectedVolume = {
|
||||
size: 100,
|
||||
|
@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h3 translate>Description</h3>
|
||||
<p translate>This page allows you to create a volume based off of the image.</p>
|
||||
</div>
|
@ -49,12 +49,11 @@
|
||||
<span translate>Availability Zone</span>
|
||||
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||
</label>
|
||||
<select class="form-control" id="availability-zone" ng-model="createVolumeCtrl.volume.availability_zone" ng-required="true">
|
||||
<option value="" selected="selected" translate>Select a zone</option>
|
||||
<option ng-repeat="availabilityZone in createVolumeCtrl.availabilityZones"
|
||||
ng-value="availabilityZone.zoneName">
|
||||
{$ availabilityZone.zoneName $}
|
||||
</option>
|
||||
<select ng-options="item for item in createVolumeCtrl.availabilityZones"
|
||||
class="form-control"
|
||||
id="availability-zone"
|
||||
ng-model="createVolumeCtrl.volume.availability_zone"
|
||||
ng-required="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,6 +43,7 @@
|
||||
{
|
||||
title: gettext('Volume Details'),
|
||||
templateUrl: basePath + 'steps/create-volume/create-volume.html',
|
||||
helpUrl: basePath + 'steps/create-volume/create-volume.help.html',
|
||||
formName: 'volumeForm'
|
||||
}
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user