From 40936ca5d4d369f927eec68a649c81e9adeabf8a Mon Sep 17 00:00:00 2001 From: Shu Muto Date: Tue, 16 May 2017 15:13:14 +0900 Subject: [PATCH] Add signed url action for queue This patch adds "Signed URL" action as row action. Change-Id: I9d8f87b4d1ff9a2b2e245470417e071a2bf00e1c Implements: blueprint support-presigned-queue --- zaqar_ui/api/rest/zaqar.py | 7 +- zaqar_ui/api/zaqar.py | 6 + .../openstack-service-api/zaqar.service.js | 7 + .../project/queues/actions/actions.module.js | 9 + .../queues/actions/signed-url.service.js | 196 ++++++++++++++++++ .../dashboard/project/queues/queues.scss | 5 + 6 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 zaqar_ui/static/dashboard/project/queues/actions/signed-url.service.js diff --git a/zaqar_ui/api/rest/zaqar.py b/zaqar_ui/api/rest/zaqar.py index e7f071f..db038b6 100644 --- a/zaqar_ui/api/rest/zaqar.py +++ b/zaqar_ui/api/rest/zaqar.py @@ -85,8 +85,11 @@ class QueueActions(generic.View): resource_types = request.DATA.get("resource_types") zaqar.queue_purge(request, queue_name, resource_types) elif action == "share": - # FIXME(flwang): This is placeholder for pre-signed feature. - pass + paths = request.DATA.get("paths") + ttl_seconds = request.DATA.get("ttl_seconds") + methods = request.DATA.get("methods") + return zaqar.queue_signed_url(request, queue_name, paths, + ttl_seconds, methods) @urls.register diff --git a/zaqar_ui/api/zaqar.py b/zaqar_ui/api/zaqar.py index ecb9222..a170916 100644 --- a/zaqar_ui/api/zaqar.py +++ b/zaqar_ui/api/zaqar.py @@ -102,6 +102,12 @@ def message_list(request, queue_name): return zaqarclient(request).queue(queue_name).messages() +def queue_signed_url(request, queue_name, paths, ttl_seconds, methods): + queue = zaqarclient(request).queue(queue_name, auto_create=False) + return queue.signed_url(paths=paths, ttl_seconds=ttl_seconds, + methods=methods) + + def subscription_list(request, queue_name): return [{'subscriber': s.subscriber, 'id': s.id, 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 524a5ca..19c45c2 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 @@ -43,6 +43,7 @@ purgeQueue: purgeQueue, postMessages: postMessages, getMessages: getMessages, + signedUrl: signedUrl, getSubscriptions: getSubscriptions, addSubscription: addSubscription, deleteSubscription: deleteSubscription, @@ -106,6 +107,12 @@ return apiService.post(url, msgs).error(error(msg)); } + function signedUrl(queueName, form) { + var msg = gettext('Unable to create signed URL.'); + var url = queuePath + queueName + '/share'; + return apiService.post(url, form).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 090d4b7..dd91e90 100644 --- a/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js +++ b/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js @@ -35,6 +35,7 @@ 'horizon.dashboard.project.queues.actions.purgeQueueService', 'horizon.dashboard.project.queues.actions.postMessageService', 'horizon.dashboard.project.queues.actions.listMessageService', + 'horizon.dashboard.project.queues.actions.signedUrlService', 'horizon.dashboard.project.queues.actions.createSubscriptionService', 'horizon.dashboard.project.queues.resourceType' ]; @@ -47,6 +48,7 @@ purgeQueueService, postMessageService, listMessageService, + signedUrlService, createSubscriptionService, resourceType ) { @@ -67,6 +69,13 @@ text: gettext('View Messages') } }) + .append({ + id: 'queuesSignedUrl', + service: signedUrlService, + template: { + text: gettext('Signed URL') + } + }) .append({ id: 'queuesItemUpdate', service: updateQueueService, diff --git a/zaqar_ui/static/dashboard/project/queues/actions/signed-url.service.js b/zaqar_ui/static/dashboard/project/queues/actions/signed-url.service.js new file mode 100644 index 0000000..5e5faa5 --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/signed-url.service.js @@ -0,0 +1,196 @@ +/** + * 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'; + + /** + * @ngdoc factory + * @name horizon.dashboard.project.queues.signed-url.service + * @description + * Service for the signed url for the queue + */ + angular + .module('horizon.dashboard.project.queues') + .factory( + 'horizon.dashboard.project.queues.actions.signedUrlService', + signedUrlService); + + signedUrlService.$inject = [ + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.project.queues.basePath', + 'horizon.dashboard.project.queues.events', + 'horizon.dashboard.project.queues.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.form.ModalFormService', + 'horizon.framework.widgets.modal-wait-spinner.service', + 'horizon.framework.widgets.toast.service' + ]; + + function signedUrlService( + policy, zaqar, basePath, events, resourceType, actionResult, gettext, + $qExtensions, modal, waitSpinner, toast + ) { + // schema + var schema = { + type: "object", + properties: { + name: { + }, + paths: { + }, + ttl_seconds: { + type: "number", + minimum: 1 + }, + methods: { + } + } + }; + + // form + var form = [ + { + type: "section", + htmlClass: "col-sm-12", + items: [ + { // for result message + type: "help", + helpvalue: "", + condition: true + }, + { + key: "paths", + type: "checkboxes", + title: gettext("Paths"), + titleMap: [ + {value: "messages", name: gettext("Messages")}, + {value: "subscriptions", name: gettext("Subscriptions")}, + {value: "claims", name: gettext("Claims")} + ], + htmlClass: "horizontal-checkboxes" + }, + { + key: "ttl_seconds", + title: gettext("TTL Seconds") + }, + { + key: "methods", + title: gettext("Methods"), + type: "checkboxes", + titleMap: [ + {value: "GET", name: gettext("GET")}, + {value: "HEAD", name: gettext("HEAD")}, + {value: "OPTIONS", name: gettext("OPTIONS")}, + {value: "POST", name: gettext("POST")}, + {value: "PUT", name: gettext("PUT")}, + {value: "DELETE", name: gettext("DELETE")} + ], + htmlClass: "horizontal-checkboxes" + } + ] + } + ]; + + // model + var model = { + id: '', + name: '', + paths: '', + ttl_seconds: '', + methods: '' + }; + + // modal config + var config = { + title: gettext("Signed URL for %s"), + schema: schema, + form: angular.copy(form), + model: model + }; + + var message = { + success: gettext("Signed URL was successfully created for the queue %s with expires %s " + + "and signature %s.") + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return policy.ifAllowed({ rules: [['queue', 'signed_url']] }); + } + + function perform(selected) { + config.model.id = selected.name; + config.model.name = selected.name; + config.model.paths = ''; + config.form = angular.copy(form); + config.title = interpolate(config.title, [selected.name]); + modal.open(config).then(submit); + } + + function submit(context) { + var name = context.model.name; + delete context.model.id; + delete context.model.name; + delete context.model.output; + if (!context.model.ttl_seconds) { + delete context.model.ttl_seconds; + } + waitSpinner.showModalSpinner(gettext('Creating Signed URL')); + return zaqar.signedUrl(name, context.model).then(function(response) { + config.model = { + paths: context.model.paths, + ttl_seconds: context.model.ttl_seconds, + methods: context.model.methods + }; + config.form = angular.copy(form); + + // for result message + config.form[0].items[0].helpvalue = "
" + + interpolate(message.success, + [name, response.data.expires, response.data.signature] + ) + "
"; + config.form[0].items[0].condition = false; + + // display new dialog + waitSpinner.hideModalSpinner(); + modal.open(config).then(submit); + + var result = actionResult.getActionResult().updated(resourceType, name); + return result.results; + }, function(response) { + // close spinner and display toast + waitSpinner.hideModalSpinner(); + toast.add('error', response.data.split("(")[0].trim() + "."); + var result = actionResult.getActionResult().failed(resourceType, name); + return result.results; + }); + } + } +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/queues.scss b/zaqar_ui/static/dashboard/project/queues/queues.scss index 71788b6..492c500 100644 --- a/zaqar_ui/static/dashboard/project/queues/queues.scss +++ b/zaqar_ui/static/dashboard/project/queues/queues.scss @@ -5,4 +5,9 @@ textarea#messages { height: 28em; +} + +.horizontal-checkboxes div { + display: table-cell; + padding-right: 10px; } \ No newline at end of file