From 47e3638f951e57ebdd224f2a6494f6dab48553d4 Mon Sep 17 00:00:00 2001 From: Zhihao Yuan Date: Tue, 6 Aug 2013 10:54:46 -0400 Subject: [PATCH] feat(api): GET and HEAD methods on queue To allow GET/HEAD /queues/a-queue gives users an intuitive approach to test whether the queue exits, while the URI above is returned as the Location header by a queue's PUT method. Implements: blueprint queue-get-and-head Fixes: bug #1207075 Change-Id: Iefd3236a739b1a5d959efb489fc2b966ee893599 --- marconi/storage/base.py | 13 ++++++++++++- marconi/storage/mongodb/queues.py | 4 ++++ marconi/storage/sqlite/queues.py | 9 +++++++++ marconi/tests/storage/base.py | 6 ++++++ .../tests/transport/wsgi/test_queue_lifecycle.py | 10 +++++++--- marconi/tests/util/faulty_storage.py | 3 +++ marconi/transport/wsgi/queues.py | 15 +++++++++++++++ 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/marconi/storage/base.py b/marconi/storage/base.py index f27443cd8..99145e2b9 100644 --- a/marconi/storage/base.py +++ b/marconi/storage/base.py @@ -113,7 +113,7 @@ class QueueBase(ControllerBase): @abc.abstractmethod def create(self, name, project=None): - """Base method for queue creation + """Base method for queue creation. :param name: The queue name :param project: Project id @@ -122,6 +122,17 @@ class QueueBase(ControllerBase): """ raise NotImplementedError + @abc.abstractmethod + def exists(self, name, project=None): + """Base method for testing queue existence. + + :param name: The queue name + :param project: Project id + :returns: True if a queue exists and False + if it does not. + """ + raise NotImplementedError + @abc.abstractmethod def set_metadata(self, name, metadata, project=None): """Base method for updating a queue metadata. diff --git a/marconi/storage/mongodb/queues.py b/marconi/storage/mongodb/queues.py index b4bf9f226..48bbbfade 100644 --- a/marconi/storage/mongodb/queues.py +++ b/marconi/storage/mongodb/queues.py @@ -122,6 +122,10 @@ class QueueController(storage.QueueBase): else: return True + @utils.raises_conn_error + def exists(self, name, project=None): + return self._col.find_one({'p': project, 'n': name}) is not None + @utils.raises_conn_error def set_metadata(self, name, metadata, project=None): rst = self._col.update({'p': project, 'n': name}, diff --git a/marconi/storage/sqlite/queues.py b/marconi/storage/sqlite/queues.py index 01b5d7e47..e9fad69e9 100644 --- a/marconi/storage/sqlite/queues.py +++ b/marconi/storage/sqlite/queues.py @@ -95,6 +95,15 @@ class QueueController(base.QueueBase): return self.driver.affected + def exists(self, name, project): + if project is None: + project = '' + + return self.driver.run(''' + select id from Queues + where project = ? and name = ? + ''', project, name).fetchone() is not None + def set_metadata(self, name, metadata, project): if project is None: project = '' diff --git a/marconi/tests/storage/base.py b/marconi/tests/storage/base.py index fa1ce090a..ae6b3db34 100644 --- a/marconi/tests/storage/base.py +++ b/marconi/tests/storage/base.py @@ -75,6 +75,9 @@ class QueueControllerTest(ControllerBaseTest): created = self.controller.create('test', project=self.project) self.assertTrue(created) + # Test Queue Existence + self.assertTrue(self.controller.exists('test', project=self.project)) + # Test Queue retrieval metadata = self.controller.get_metadata('test', project=self.project) self.assertEqual(metadata, {}) @@ -103,6 +106,9 @@ class QueueControllerTest(ControllerBaseTest): # Test Queue Deletion self.controller.delete('test', project=self.project) + # Test Queue Existence + self.assertFalse(self.controller.exists('test', project=self.project)) + # Test DoesNotExist Exception with testing.expect(storage.exceptions.DoesNotExist): self.controller.get_metadata('test', project=self.project) diff --git a/marconi/tests/transport/wsgi/test_queue_lifecycle.py b/marconi/tests/transport/wsgi/test_queue_lifecycle.py index cda17ee66..df0f1b431 100644 --- a/marconi/tests/transport/wsgi/test_queue_lifecycle.py +++ b/marconi/tests/transport/wsgi/test_queue_lifecycle.py @@ -48,9 +48,9 @@ class QueueLifecycleBaseTest(base.TestBase): location = ('Location', '/v1/queues/gumshoe') self.assertIn(location, self.srmock.headers) - # Get on queues shouldn't work any more - self.simulate_get(path, project_id) - self.assertEquals(self.srmock.status, falcon.HTTP_405) + # Ensure queue existence + self.simulate_head(path, project_id) + self.assertEquals(self.srmock.status, falcon.HTTP_204) # Add metadata doc = '{"messages": {"ttl": 600}}' @@ -67,6 +67,10 @@ class QueueLifecycleBaseTest(base.TestBase): self.simulate_delete(path, project_id) self.assertEquals(self.srmock.status, falcon.HTTP_204) + # Get non-existent queue + self.simulate_get(path, project_id) + self.assertEquals(self.srmock.status, falcon.HTTP_404) + # Get non-existent stats self.simulate_get(path + '/stats', project_id) self.assertEquals(self.srmock.status, falcon.HTTP_404) diff --git a/marconi/tests/util/faulty_storage.py b/marconi/tests/util/faulty_storage.py index 8e6bccfdc..8575e6d8c 100644 --- a/marconi/tests/util/faulty_storage.py +++ b/marconi/tests/util/faulty_storage.py @@ -44,6 +44,9 @@ class QueueController(storage.QueueBase): def create(self, name, project=None): raise NotImplementedError() + def exists(self, name, project=None): + raise NotImplementedError() + def set_metadata(self, name, metadata, project=None): raise NotImplementedError() diff --git a/marconi/transport/wsgi/queues.py b/marconi/transport/wsgi/queues.py index bfee70de3..cd7d349c6 100644 --- a/marconi/transport/wsgi/queues.py +++ b/marconi/transport/wsgi/queues.py @@ -49,6 +49,21 @@ class ItemResource(object): resp.status = falcon.HTTP_201 if created else falcon.HTTP_204 resp.location = req.path + def on_head(self, req, resp, project_id, queue_name): + LOG.debug(_("Queue item exists - queue: %(queue)s, " + "project: %(project)s") % + {"queue": queue_name, "project": project_id}) + + if self.queue_controller.exists(queue_name, + project=project_id): + resp.status = falcon.HTTP_204 + else: + resp.status = falcon.HTTP_404 + + resp.content_location = req.path + + on_get = on_head + def on_delete(self, req, resp, project_id, queue_name): LOG.debug(_("Queue item DELETE - queue: %(queue)s, " "project: %(project)s") %