Queues create wizard

This patch adds the ability to create queues with metadata.
Tests will be added in subsequent patches.

Co-Authored-By: Fei Long Wang <flwang@catalyst.net.nz>

Change-Id: I294f9b1cf84ad11be23e0e17b16c52a963ff235c
This commit is contained in:
Thai Tran 2016-02-09 17:07:35 -08:00 committed by Fei Long Wang
parent 14f41fb1a3
commit e6225498cc
14 changed files with 518 additions and 26 deletions

View File

@ -64,12 +64,13 @@ class Queues(generic.View):
zaqar.queue_delete(request, queue_name)
@rest_utils.ajax(data_required=True)
def create(self, request):
def put(self, request):
"""Create a new queue.
Returns the new queue object on success.
"""
new_queue = zaqar.queue_create(request, **request.DATA)
return rest_utils.CreatedResponse(
'/api/messaging/queues/%s' % new_queue.name,
new_queue.to_dict())
location = '/api/zaqar/queues/%s' % new_queue.name
response = {'name': new_queue.name,
'metadata': new_queue._metadata}
return rest_utils.CreatedResponse(location, response)

View File

@ -60,9 +60,9 @@ def queue_create(request, queue_name, metadata):
# 2. ttl
# 3. max message size
# 4. Metadata
queue = zaqarclient(request).queue(queue_name, force_create=True)
queue.metadata(new_meta=metadata)
return queue
def queue_delete(request, queue_name):

View File

@ -28,6 +28,12 @@ ADD_ANGULAR_MODULES = [
ADD_JS_FILES = [
'app/core/openstack-service-api/zaqar.service.js',
'dashboard/project/queues/queues.module.js',
'dashboard/project/queues/actions/create.action.service.js',
'dashboard/project/queues/actions/create.workflow.service.js',
'dashboard/project/queues/steps/queue-details/queue-details.controller.js',
'dashboard/project/queues/steps/queue-metadata/'
'queue-metadata.controller.js',
'dashboard/project/queues/table/batch-actions.service.js',
'dashboard/project/queues/table/table.controller.js',
]

View File

@ -28,7 +28,8 @@
function ZaqarAPI(apiService, toastService) {
var service = {
getQueues: getQueues
getQueues: getQueues,
createQueue: createQueue
};
return service;
@ -42,6 +43,13 @@
});
}
function createQueue(newQueue) {
return apiService.put('/api/zaqar/queues/', newQueue)
.error(function() {
toastService.add('error', gettext('Unable to create the queue.'));
});
}
}
}());

View File

@ -0,0 +1,105 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.project.queues')
.factory('horizon.dashboard.project.queues.actions.createService', createService);
createService.$inject = [
'horizon.app.core.metadata.service',
'horizon.app.core.openstack-service-api.policy',
'horizon.dashboard.project.queues.events',
'horizon.dashboard.project.queues.actions.createWorkflow',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.framework.widgets.modal.wizard-modal.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngDoc factory
* @name horizon.dashboard.project.queues.actions.createService
* @Description A service to open the queues wizard.
*/
function createService(meta, policy, events, createWorkflow, zaqar, wizard, toast) {
var message = {
success: gettext('Queue %s was successfully created.')
};
var scope;
var model = {
queue_name: null,
metadata: {}
};
var service = {
initScope: initScope,
perform: perform,
allowed: allowed
};
return service;
//////////////
// we define initScope so that the table controller
// will know when a new queue has been created
function initScope($scope) {
scope = $scope;
var queueWatcher = $scope.$on(events.DETAILS_CHANGED, onQueueChange);
var metadataWatcher = $scope.$on(events.METADATA_CHANGED, onMetadataChange);
$scope.$on('$destroy', function destroy() {
queueWatcher();
metadataWatcher();
});
}
function onQueueChange(e, queue) {
model.queue_name = queue.name;
e.stopPropagation();
}
function onMetadataChange(e, metadata) {
model.metadata = metadata;
e.stopPropagation();
}
function perform() {
wizard.modal({
scope: scope,
workflow: createWorkflow,
submit: submit
});
}
function allowed() {
return policy.ifAllowed({ rules: [['queue', 'add_queue']] });;
}
function submit() {
return zaqar.createQueue(model).then(success);
}
function success(response) {
toast.add('success', interpolate(message.success, [response.data.name]));
scope.$emit(events.CREATE_SUCCESS, response.data);
}
} // end of createService
})(); // end of IIFE

View File

@ -0,0 +1,54 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.project.queues')
.factory('horizon.dashboard.project.queues.actions.createWorkflow', createWorkflow);
createWorkflow.$inject = [
'horizon.app.core.workflow.factory',
'horizon.dashboard.project.queues.basePath',
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc factory
* @name horizon.dashboard.project.queues.actions.createWorkflow
* @description A workflow for the create queue action.
*/
function createWorkflow(workflowService, basePath, gettext) {
var workflow = workflowService({
title: gettext('Create Queue'),
btnText: { finish: gettext('Create') },
steps: [{
title: gettext('Queue Details'),
templateUrl: basePath + 'steps/queue-details/queue-details.html',
formName: 'queueDetailsForm'
}, {
title: gettext('Queue Metadata'),
templateUrl: basePath + 'steps/queue-metadata/queue-metadata.html',
formName: 'queueMetadataForm'
}]
});
return workflow;
}
})();

View File

@ -1,5 +1,6 @@
/**
* Copyright 2015 Catalyst IT Ltd
* Copyright 2016 IBM Corp.
*
* 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
@ -19,20 +20,40 @@
/**
* @ngdoc overview
* @name horizon.dashboard.project
*
* @description
* Dashboard module to host various queues panels.
* @name horizon.dashboard.project.queues
* @description Dashboard module to host various queues panels.
*/
angular
.module('horizon.dashboard.project.queues', [])
.constant('horizon.dashboard.project.queues.events', events())
.config(config);
config.$inject = ['$provide', '$windowProvider'];
config.$inject = [
'$provide',
'$windowProvider'
];
/**
* @ngdoc value
* @name horizon.dashboard.project.queues.events
* @description a list of events for queues
*/
function events() {
return {
CREATE_SUCCESS: 'horizon.dashboard.project.queues.CREATE_SUCCESS',
DETAILS_CHANGED: 'horizon.dashboard.project.queues.DETAILS_CHANGED',
METADATA_CHANGED: 'horizon.dashboard.project.queues.METADATA_CHANGED'
};
}
/**
* @ndoc config
* @name horizon.dashboard.project.queues.basePath
* @description Base path for the queues panel
*/
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/';
$provide.constant('horizon.dashboard.project.basePath', path);
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/queues/';
$provide.constant('horizon.dashboard.project.queues.basePath', path);
}
})();

View File

@ -0,0 +1,62 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.project.queues')
.controller('horizon.dashboard.project.queues.steps.QueueDetailsController', controller);
controller.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.events'
];
/**
* @ngdoc controller
* @name horizon.dashboard.project.queues.steps.QueueDetailsController
* @description This controller is use for creating a queue.
*/
function controller($scope, zaqar, events) {
var ctrl = this;
ctrl.queue = {};
////////////////////////
// watch this object, when it changes, emit to parent listeners
var watcher = $scope.$watchCollection(getQueue, onQueneChange);
$scope.$on('$destroy', function() {
watcher();
})
////////////////////////
function getQueue() {
return ctrl.queue;
}
function onQueneChange(newValue, oldValue){
if (newValue !== oldValue) {
$scope.$emit(events.DETAILS_CHANGED, newValue);
}
}
} // end of controller
})();

View File

@ -0,0 +1,33 @@
<div ng-controller="horizon.dashboard.project.queues.steps.QueueDetailsController as detailsCtrl">
<h1 translate>Queue Details</h1>
<div class="content">
<div translate style="margin-bottom:1em;">
Note that you can create a queue without defining metadata.
The metadata step is optional but recommended.
</div>
<div class="selected-source clearfix">
<div class="row">
<div class="col-xs-12 col-sm-8">
<div class="form-group required">
<label class="control-label" translate>Queue Name</label>
<input required
id="queueDetailsForm-name" name="name"
type="text" class="form-control"
ng-model="detailsCtrl.queue.name"
ng-maxlength="255"
placeholder="{$ 'Enter a queue name'|translate $}">
<p class="help-block alert alert-danger"
ng-show="queueDetailsForm.name.$invalid && queueDetailsForm.name.$dirty">
<translate>A user name between 1-256 characters is required.</translate>
</p>
</div>
</div>
</div><!-- row -->
</div><!-- clearfix -->
</div><!-- content -->
</div><!-- controller -->

View File

@ -0,0 +1,103 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.project.queues')
.controller('horizon.dashboard.project.queues.steps.QueueMetadataController', controller);
controller.$inject = [
'$q',
'$scope',
'horizon.app.core.metadata.service',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.events',
'horizon.framework.widgets.metadata.tree.service'
];
/**
* @ngdoc controller
* @name horizon.dashboard.project.queues.steps.QueueDetailsController
* @description This controller is use for creating a queue.
*/
function controller($q, $scope, metadata, zaqar, events, metaTree) {
var ctrl = this;
var queue = $scope.queue? $scope.queue: {};
ctrl.tree = new metaTree.Tree([], []);
/* eslint-enable angular/ng_controller_as */
$scope.$watchCollection(getTree, onMetadataChanged);
/* eslint-enable angular/ng_controller_as */
init();
////////////////////////////////
function init() {
$q.all({
available: standardDefinitions(),
existing: getExistingMetdataPromise(queue)
})
.then(onMetadataGet);
}
function onMetadataGet(response) {
console.log(response);
ctrl.tree = new metaTree.Tree(
response.available.data.items,
response.existing.data
);
}
function getTree() {
return ctrl.tree.getExisting();
}
function standardDefinitions() {
// TODO: currently, there is no standard metadefinitions
// should add some reserved/fixed definition here
// preferably it should come from zaqar and not hardcoded here
var deferred = $q.defer();
deferred.resolve({data: []});
return deferred.promise;
}
function getExistingMetdataPromise(queue) {
//if (angular.isDefined(queue.id)) {
// return metadata.getMetadata('queue', queue.id);
//}
//else {
var deferred = $q.defer();
deferred.resolve({data: []});
return deferred.promise;
//}
}
function onMetadataChanged(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.$emit(events.METADATA_CHANGED, newValue);
}
}
} // end of controller
})();

View File

@ -0,0 +1,8 @@
<div ng-controller="horizon.dashboard.project.queues.steps.QueueMetadataController as metadataCtrl">
<h1 translate>Queue Metadata</h1>
<div class="content">
<metadata-tree model="metadataCtrl.tree" form="metadataForm"></metadata-tree>
</div><!-- content -->
</div><!-- controller -->

View File

@ -0,0 +1,63 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.project.queues')
.factory('horizon.dashboard.project.queues.batch-actions.service', batchActions);
batchActions.$inject = [
'horizon.dashboard.project.queues.actions.createService',
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc factory
* @name horizon.dashboard.project.queues.batch-actions.service
* @description A list of table batch actions.
*/
function batchActions(createService, gettext) {
var service = {
initScope: initScope,
actions: actions
};
return service;
///////////////
function initScope(scope) {
angular.forEach([createService], setActionScope);
function setActionScope(action) {
action.initScope(scope.$new());
}
}
function actions() {
return [{
service: createService,
template: {
type: 'create',
text: gettext('Create Queue')
}
}];
}
}
})();

View File

@ -29,17 +29,32 @@
queuesTableController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.zaqar'
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.project.queues.batch-actions.service',
'horizon.dashboard.project.queues.events'
];
function queuesTableController($scope, zaqar) {
function queuesTableController($scope, zaqar, batchActions, events) {
var ctrl = this;
ctrl.iqueues = [];
ctrl.queues = [];
ctrl.queuesSrc = [];
ctrl.batchActions = batchActions;
ctrl.batchActions.initScope($scope);
init();
initScope();
//////////
function initScope() {
var createWatcher = $scope.$on(events.CREATE_SUCCESS, onCreateSuccess);
$scope.$on('$destroy', function destroy() {
createWatcher();
})
}
//////////
@ -48,12 +63,12 @@
}
function getQueuesSuccess(response) {
ctrl.queuesSrc = response;
}
// TODO: If the response does not contain the id field
// then you must manually add them on the client-side
// the horizon table's checkboxes requires that the id field be present
console.log(response);
ctrl.queues = response;
function onCreateSuccess(e, newQueue) {
e.stopPropagation();
ctrl.queuesSrc.push(newQueue);
}
}

View File

@ -2,21 +2,34 @@
<table ng-controller="horizon.dashboard.project.queues.tableController as table"
hz-table ng-cloak
st-table="table.iqueues"
st-safe-src="table.queues"
st-table="table.queues"
st-safe-src="table.queuesSrc"
default-sort="name"
default-sort-reverse="false"
class="table-striped table-rsp table-detail modern">
<thead>
<tr>
<!--
Table-batch-actions:
This is where batch actions like searching, creating, and deleting.
-->
<th colspan="100" class="search-header">
<hz-search-bar group-classes="input-group-sm" icon-classes="fa-search">
<actions allowed="table.batchActions.actions" type="batch">
</actions>
</hz-search-bar>
</th>
</tr>
<!--
Table-column-headers:
The headers for the table columns
-->
<tr>
<th class=select-col>
<input type="checkbox" hz-select-all="table.iqueues">
<input type="checkbox" hz-select-all="table.queuesSrc">
</th>
<th class="expander"></th>
<th class="rsp-p1" st-sort="name" st-sort-default translate>Name</th>
@ -35,7 +48,7 @@
Include action-col if you want to perform actions.
rsp-p1 rsp-p2 are responsive priority as user resizes window.
-->
<tr ng-repeat-start="q in table.iqueues track by q.name"
<tr ng-repeat-start="q in table.queuesSrc track by q.name"
nt-class="{'st-selected': checked[q.name]}">
<td class="select-col">
@ -105,7 +118,7 @@
<tfoot>
<td colspan="100">
<span class="display">{$ table.iqueues.length|itemCount $}</span>
<span class="display">{$ table.queuesSrc.length|itemCount $}</span>
<div st-pagination="" st-items-by-page="10" st-displayed-pages="10"></div>
</td>
</tfoot>