From 6b805aafcebb346427731156a9ec88faa48c31b3 Mon Sep 17 00:00:00 2001 From: Adam Coldrick Date: Sat, 26 Jan 2019 17:53:41 +0000 Subject: [PATCH] Add an attachments endpoint to stories This endpoint currently supports returning an upload URL for the configured storage backend. Change-Id: I637cf5897f281a4e6b66be6495938aa999a8d3ac Story: 2000679 Task: 29115 --- storyboard/api/v1/attachments.py | 56 ++++++++++++++++++++++++++++++++ storyboard/api/v1/stories.py | 3 ++ 2 files changed, 59 insertions(+) create mode 100644 storyboard/api/v1/attachments.py diff --git a/storyboard/api/v1/attachments.py b/storyboard/api/v1/attachments.py new file mode 100644 index 00000000..50b853ae --- /dev/null +++ b/storyboard/api/v1/attachments.py @@ -0,0 +1,56 @@ +# Copyright (c) 2019 Adam Coldrick +# +# 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. + +from oslo_config import cfg +from pecan import response +from pecan import rest +from pecan.secure import secure +from wsme import types as wtypes +import wsmeext.pecan as wsme_pecan + +from storyboard.api.auth import authorization_checks as checks +from storyboard.api.v1.storage import storage + + +CONF = cfg.CONF + +STORAGE_BACKEND = storage.get_storage_backend() + + +class AttachmentsController(rest.RestController): + """Endpoint for managing attachments.""" + + _custom_actions = {"upload_url": ["GET"]} + + @secure(checks.authenticated) + @wsme_pecan.wsexpose(wtypes.text, int) + def upload_url(self, story_id): + """Return a URL which can be used to upload attachments. + + The storage type and authentication token are provided in the + X-Storage-Type and X-Auth-Token response headers respectively. + + :param story_id: ID of the story the attachment is for. + Currently unused, and just needed for routing. + + """ + response.headers['X-Storage-Type'] = CONF.attachments.storage_backend + + expiry, auth, name = STORAGE_BACKEND.get_auth() + response.headers['X-Auth-Token'] = auth + response.headers['X-Token-Expiry'] = str(expiry) + response.headers['X-Object-Name'] = name + + return STORAGE_BACKEND.get_upload_url() diff --git a/storyboard/api/v1/stories.py b/storyboard/api/v1/stories.py index 64c43571..0e8f61b3 100644 --- a/storyboard/api/v1/stories.py +++ b/storyboard/api/v1/stories.py @@ -28,6 +28,7 @@ import wsmeext.pecan as wsme_pecan from storyboard._i18n import _ from storyboard.api.auth import authorization_checks as checks +from storyboard.api.v1.attachments import AttachmentsController from storyboard.api.v1.search import search_engine from storyboard.api.v1.tags import TagsController from storyboard.api.v1.tasks import TasksNestedController @@ -489,6 +490,8 @@ class StoriesController(rest.RestController): stories_api.story_delete( story_id, current_user=request.current_user_id) + if CONF.attachments.enable_attachments: + attachments = AttachmentsController() comments = CommentsController() events = NestedTimeLineEventsController() tasks = TasksNestedController()