From ad6b60658590284e0714b14faf4ea46cad10ac01 Mon Sep 17 00:00:00 2001 From: Fei Long Wang Date: Mon, 27 Feb 2017 15:57:19 +1300 Subject: [PATCH] Support post messages and get messages This patch adds "Post Messages" and "View Messages" actions. "delete" and "claim" actions for list messages dialog will be added by subsequent patches. Implement blueprint: post-get-messages Change-Id: I42f44e2d8ab9266ba8c21c853956ca69c62ecfce --- zaqar_ui/api/rest/zaqar.py | 32 +++- zaqar_ui/api/zaqar.py | 8 + zaqar_ui/karma.conf.js | 1 + .../openstack-service-api/zaqar.service.js | 14 ++ .../project/queues/actions/actions.module.js | 18 +++ .../queues/actions/list-message.controller.js | 62 ++++++++ .../project/queues/actions/list-message.html | 33 ++++ .../queues/actions/list-message.service.js | 136 +++++++++++++++++ .../queues/actions/list-message.spec.js | 53 +++++++ .../queues/actions/post-message.help.html | 28 ++++ .../queues/actions/post-message.service.js | 143 ++++++++++++++++++ .../dashboard/project/queues/queues.module.js | 1 + .../dashboard/project/queues/queues.scss | 4 + .../project/queues/table/queue.controller.js | 6 + .../dashboard/project/queues/table/queue.html | 1 + 15 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/list-message.controller.js create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/list-message.html create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/list-message.service.js create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/list-message.spec.js create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/post-message.help.html create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/post-message.service.js diff --git a/zaqar_ui/api/rest/zaqar.py b/zaqar_ui/api/rest/zaqar.py index 1385e62..e7f071f 100644 --- a/zaqar_ui/api/rest/zaqar.py +++ b/zaqar_ui/api/rest/zaqar.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import six import yaml @@ -138,7 +139,7 @@ class Queues(generic.View): @urls.register class Subscriptions(generic.View): - """API for queues""" + """API for Subscriptions""" url_regex = r'zaqar/queues/(?P[^/]+)/subscriptions/$' @rest_utils.ajax() @@ -163,6 +164,35 @@ class Subscriptions(generic.View): return zaqar.subscription_create(request, queue_name, request.DATA) +@urls.register +class Messages(generic.View): + """API for messages""" + url_regex = r'zaqar/queues/(?P[^/]+)/messages/$' + + @rest_utils.ajax() + def get(self, request, queue_name): + """Get a list of messages""" + result = zaqar.message_list(request, queue_name) + messages = [] + for m in result: + claim_id = None + if m.claim_id: + claim_id = m.claim_id() + messages.append({'age': m.age, + 'body': m.body, + 'claim_id': claim_id, + 'id': m.id, + 'href': m.href, + 'ttl': m.ttl}) + return messages + + @rest_utils.ajax(data_required=True) + def post(self, request, queue_name): + """Create new messages""" + messages = json.loads(request.DATA.get("messages")) + return zaqar.message_post(request, queue_name, messages) + + @urls.register class Subscription(generic.View): """API for retrieving a single subscription""" diff --git a/zaqar_ui/api/zaqar.py b/zaqar_ui/api/zaqar.py index ca3da35..ecb9222 100644 --- a/zaqar_ui/api/zaqar.py +++ b/zaqar_ui/api/zaqar.py @@ -94,6 +94,14 @@ def queue_purge(request, queue_name, resource_types): queue.purge(resource_types=resource_types) +def message_post(request, queue_name, messages_data): + return zaqarclient(request).queue(queue_name).post(messages_data) + + +def message_list(request, queue_name): + return zaqarclient(request).queue(queue_name).messages() + + def subscription_list(request, queue_name): return [{'subscriber': s.subscriber, 'id': s.id, diff --git a/zaqar_ui/karma.conf.js b/zaqar_ui/karma.conf.js index 19c7cf0..0192378 100644 --- a/zaqar_ui/karma.conf.js +++ b/zaqar_ui/karma.conf.js @@ -65,6 +65,7 @@ module.exports = function (config) { toxPath + 'xstatic/pkg/tv4/data/tv4.js', toxPath + 'xstatic/pkg/objectpath/data/ObjectPath.js', toxPath + 'xstatic/pkg/angular_schema_form/data/schema-form.js', + toxPath + 'xstatic/pkg/angular_fileupload/data/ng-file-upload.js', // TODO: These should be mocked. toxPath + '/horizon/static/horizon/js/horizon.js', diff --git a/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js b/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js index a922c70..524a5ca 100644 --- a/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js +++ b/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js @@ -29,6 +29,7 @@ function ZaqarAPI(apiService, toast) { var queuePath = '/api/zaqar/queues/'; + var msgPath = '/api/zaqar/queues/%s/messages/'; var subPath = '/api/zaqar/queues/%s/subscriptions/'; var poolPath = '/api/zaqar/pools/'; var flavorPath = '/api/zaqar/flavors/'; @@ -40,6 +41,8 @@ deleteQueue: deleteQueue, updateQueue: updateQueue, purgeQueue: purgeQueue, + postMessages: postMessages, + getMessages: getMessages, getSubscriptions: getSubscriptions, addSubscription: addSubscription, deleteSubscription: deleteSubscription, @@ -92,6 +95,17 @@ return apiService.post(url, form).error(error(msg)); } + function getMessages(queueName) { + var url = interpolate(msgPath, [queueName]); + return apiService.get(url); + } + + function postMessages(queueName, msgs) { + var msg = gettext('Unable to post messages.'); + var url = interpolate(msgPath, [queueName]); + return apiService.post(url, msgs).error(error(msg)); + } + function getSubscriptions(queue) { var url = interpolate(subPath, [queue.name]); return apiService.get(url); diff --git a/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js b/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js index ad6d071..090d4b7 100644 --- a/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js +++ b/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js @@ -33,6 +33,8 @@ 'horizon.dashboard.project.queues.actions.deleteQueueService', 'horizon.dashboard.project.queues.actions.updateQueueService', 'horizon.dashboard.project.queues.actions.purgeQueueService', + 'horizon.dashboard.project.queues.actions.postMessageService', + 'horizon.dashboard.project.queues.actions.listMessageService', 'horizon.dashboard.project.queues.actions.createSubscriptionService', 'horizon.dashboard.project.queues.resourceType' ]; @@ -43,12 +45,28 @@ deleteQueueService, updateQueueService, purgeQueueService, + postMessageService, + listMessageService, createSubscriptionService, resourceType ) { var queueResourceType = registry.getResourceType(resourceType); queueResourceType.itemActions + .append({ + id: 'messagesPost', + service: postMessageService, + template: { + text: gettext('Post Messages') + } + }) + .append({ + id: 'messagesList', + service: listMessageService, + template: { + text: gettext('View Messages') + } + }) .append({ id: 'queuesItemUpdate', service: updateQueueService, diff --git a/zaqar_ui/static/dashboard/project/queues/actions/list-message.controller.js b/zaqar_ui/static/dashboard/project/queues/actions/list-message.controller.js new file mode 100644 index 0000000..aa2d37c --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/list-message.controller.js @@ -0,0 +1,62 @@ +/** + * Copyright 2017 Catalyst IT Ltd. + * + * 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'; + + /** + * @ngdoc overview + * @name messageController + * @ngController + * + * @description + * Controller for the messages table + */ + angular + .module('horizon.dashboard.project.queues.actions') + .controller('horizon.dashboard.project.queues.actions.messageController', + messageController); + + messageController.$inject = [ + '$scope', + 'horizon.app.core.openstack-service-api.zaqar' + ]; + + function messageController($scope, zaqar) { + + var ctrl = this; + ctrl.queue = $scope.model.id; + ctrl.messages = []; + ctrl.claimMessage = claimMessage; + ctrl.deleteMessage = deleteMessage; + + zaqar.getMessages(ctrl.queue).success(function (response) { + ctrl.messages = response; + }); + + ////////// + + /* TODO: actions will be implemented later. + function claimMessage(message) { + console.info(message); + } + + function deleteMessage(message) { + console.info(message); + } + */ + } +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/actions/list-message.html b/zaqar_ui/static/dashboard/project/queues/actions/list-message.html new file mode 100644 index 0000000..2d0145a --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/list-message.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + +
IDBodyTime to LiveAge
No message to show.
{$ msg.id $}{$ msg.body $}{$ msg.ttl $}{$ msg.age $}
\ No newline at end of file diff --git a/zaqar_ui/static/dashboard/project/queues/actions/list-message.service.js b/zaqar_ui/static/dashboard/project/queues/actions/list-message.service.js new file mode 100644 index 0000000..81096eb --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/list-message.service.js @@ -0,0 +1,136 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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.listMessageService', listMessageService); + + listMessageService.$inject = [ + '$q', + 'horizon.dashboard.project.queues.basePath', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.project.queues.events', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.form.ModalFormService', + 'horizon.framework.widgets.toast.service' + ]; + + /** + * @ngdoc factory + * @name horizon.dashboard.project.queues.actions.listMessageService + * @param {Object} $q + * @param {String} basePath + * @param {Object} policy + * @param {Object} zaqar + * @param {Object} events + * @param {Object} gettext + * @param {Object} $qExtensions + * @param {Object} modal + * @param {Object} toast + * @returns {Object} list messages service + * @description Brings up the polling messages modal dialog. + * On submit, poll messages from given queues. + * On cancel, do nothing. + */ + function listMessageService( + $q, basePath, policy, zaqar, events, gettext, $qExtensions, modal, toast + ) { + // schema + var schema = { + type: "object", + properties: { + postMessages: { + title: gettext("List Messages"), + type: "string" + } + } + }; + + // form + var form = [ + { + type: 'section', + htmlClass: 'row', + items: [ + { + type: 'section', + htmlClass: 'col-sm-12', + items: [ + { + type: 'template', + templateUrl: basePath + 'actions/list-message.html' + } + ] + } + ] + } + ]; + + // model + var model; + + var message = { + success: gettext('Messages has been posted to queue %s successfully.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + function perform(selected) { + model = { + id: selected.id, + name: selected.name + }; + // modal config + var config = { + "title": gettext('List Messages'), + "submitText": gettext('List Messages'), + "schema": schema, + "form": form, + "model": model + }; + return modal.open(config).then(submit); + } + + function submit(context) { + var id = context.model.id; + var name = context.model.name; + delete context.model.id; + delete context.model.name; + return zaqar.postMessages(id, context.model).then(function() { + toast.add('success', interpolate(message.success, [name])); + }); + } + } +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/actions/list-message.spec.js b/zaqar_ui/static/dashboard/project/queues/actions/list-message.spec.js new file mode 100644 index 0000000..2537483 --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/list-message.spec.js @@ -0,0 +1,53 @@ +/** + * 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.dashboard.project.queues.actions.messageController', function() { + var zaqar, controller, $scope, $q, deferred; + + beforeEach(module('horizon.framework')); + beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.dashboard.project.queues')); + + beforeEach(inject(function ($injector, _$rootScope_) { + $scope = _$rootScope_.$new(); + $scope.model = { + id: '' + }; + zaqar = $injector.get('horizon.app.core.openstack-service-api.zaqar'); + controller = $injector.get('$controller'); + controller( + 'horizon.dashboard.project.queues.actions.messageController', + { + $scope: $scope, + zaqar: zaqar + }); + deferred = $q.defer(); + deferred.resolve({data: {id: '1'}}); + spyOn(zaqar, 'getMessages').and.returnValue(deferred.promise); + asdf(); + })); + + it('should load messages for queue', function() { + expect(zaqar.getMessages).toHaveBeenCalled(); + }); + + it('should queue_id is provided by scope variable', function() { + $scope.model.id = '1'; + $scope.$apply(); + expect($scope.model.id).toBe('1'); + }); + }); +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/actions/post-message.help.html b/zaqar_ui/static/dashboard/project/queues/actions/post-message.help.html new file mode 100644 index 0000000..5928007 --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/post-message.help.html @@ -0,0 +1,28 @@ +
+

+ You can submit up to 10 messages in a single request, but you must always + encapsulate the messages in a collection container (an array in JSON, even + for a single message - without the JSON array, you receive the “Invalid + request body” message). The resulting value of the Location header or + response body might be used to retrieve the created messages for further + processing. +

+

+ The client specifies only the body and TTL for the message. The server + inserts metadata, such as ID and age. +

+

+ See a sample as below: +

+
+[
+  {
+    "body": {
+      "event": "BackupProgress",
+      "current_bytes": "2341134",
+      "total_bytes": "99614720"
+    }
+  }
+]
+
+
\ No newline at end of file diff --git a/zaqar_ui/static/dashboard/project/queues/actions/post-message.service.js b/zaqar_ui/static/dashboard/project/queues/actions/post-message.service.js new file mode 100644 index 0000000..ec8472f --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/post-message.service.js @@ -0,0 +1,143 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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.postMessageService', postMessageService); + + postMessageService.$inject = [ + '$q', + 'horizon.dashboard.project.queues.basePath', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.project.queues.events', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.form.ModalFormService', + 'horizon.framework.widgets.toast.service' + ]; + + /** + * @ngdoc factory + * @name horizon.dashboard.project.queues.actions.postMessageService + * @param {Object} $q + * @param {String} basePath + * @param {Object} policy + * @param {Object} zaqar + * @param {Object} events + * @param {Object} gettext + * @param {Object} $qExtensions + * @param {Object} modal + * @param {Object} toast + * @returns {Object} post messages service + * @description Brings up the post messages modal dialog. + * On submit, post messages to given queues. + * On cancel, do nothing. + */ + function postMessageService( + $q, basePath, policy, zaqar, events, gettext, $qExtensions, modal, toast + ) { + // schema + var schema = { + type: "object", + properties: { + postMessages: { + title: gettext("Post Messages"), + type: "string" + } + } + }; + + // form + var form = [ + { + type: 'section', + htmlClass: 'row', + items: [ + { + type: 'section', + htmlClass: 'col-sm-6', + items: [ + { + key: 'messages', + type: 'textarea' + } + ] + }, + { + type: 'template', + templateUrl: basePath + 'actions/post-message.help.html' + } + ] + } + ]; + + // model + var model = {}; + + var message = { + success: gettext('Messages has been posted to queue %s successfully.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + var scope; + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + function perform(selected, $scope) { + scope = $scope; + model = { + id: selected.id, + name: selected.name + }; + // modal config + var config = { + "title": gettext('List Messages'), + "submitText": gettext('Post'), + "schema": schema, + "form": form, + "model": model + }; + return modal.open(config).then(submit); + } + + function submit(context) { + var id = context.model.id; + var name = context.model.name; + delete context.model.id; + delete context.model.name; + return zaqar.postMessages(id, context.model).then(function() { + toast.add('success', interpolate(message.success, [name])); + scope.$emit(events.POST_MESSAGE_SUCCESS, name); + }); + } + } +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/queues.module.js b/zaqar_ui/static/dashboard/project/queues/queues.module.js index f906796..08f60a2 100644 --- a/zaqar_ui/static/dashboard/project/queues/queues.module.js +++ b/zaqar_ui/static/dashboard/project/queues/queues.module.js @@ -50,6 +50,7 @@ DELETE_SUCCESS: 'horizon.dashboard.project.queues.DELETE_SUCCESS', UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS', PURGE_SUCCESS: 'horizon.dashboard.project.queues.PURGE_SUCCESS', + POST_MESSAGE_SUCCESS: 'horizon.dashboard.project.queues.POST_MESSAGE_SUCCESS', SUBSCRIPTION_CREATE_SUCCESS: 'horizon.dashboard.project.queues.SUBSCRIPTION_CREATE_SUCCESS' }; } diff --git a/zaqar_ui/static/dashboard/project/queues/queues.scss b/zaqar_ui/static/dashboard/project/queues/queues.scss index a64dba3..71788b6 100644 --- a/zaqar_ui/static/dashboard/project/queues/queues.scss +++ b/zaqar_ui/static/dashboard/project/queues/queues.scss @@ -1,4 +1,8 @@ .subtitle { margin: 2em 0; +} + +textarea#messages { + height: 28em; } \ No newline at end of file diff --git a/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js b/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js index dab5c49..d2677ca 100644 --- a/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js +++ b/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js @@ -55,12 +55,14 @@ var deleteWatcher = $scope.$on(events.DELETE_SUCCESS, onDeleteSuccess); var updateWatcher = $scope.$on(events.UPDATE_SUCCESS, onUpdateSuccess); var purgeWatcher = $scope.$on(events.PURGE_SUCCESS, onPurgeSuccess); + var postMessageWatcher = $scope.$on(events.POST_MESSAGE_SUCCESS, onPostMessageSuccess); var subWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, broadcastEvents); $scope.$on('$destroy', function destroy() { createWatcher(); deleteWatcher(); updateWatcher(); purgeWatcher(); + postMessageWatcher(); subWatcher(); }); } @@ -146,6 +148,10 @@ refreshSubscriptions(queueName); } + function onPostMessageSuccess(e, queueName) { + e.stopPropagation(); + refreshQueue(queueName); + } } })(); diff --git a/zaqar_ui/static/dashboard/project/queues/table/queue.html b/zaqar_ui/static/dashboard/project/queues/table/queue.html index e894a8e..de08747 100644 --- a/zaqar_ui/static/dashboard/project/queues/table/queue.html +++ b/zaqar_ui/static/dashboard/project/queues/table/queue.html @@ -109,6 +109,7 @@ +