From cbb09bd1edd0d29e35430898c32c80c01953d320 Mon Sep 17 00:00:00 2001 From: Felipe Monteiro Date: Mon, 31 Jul 2017 21:13:39 +0100 Subject: [PATCH] Add endpoint/tests for GET /revisions/{revision_id} --- deckhand/control/api.py | 1 + deckhand/control/common.py | 5 +++++ deckhand/control/revisions.py | 23 ++++++++++++++++++++--- deckhand/control/views/revision.py | 9 +++++++++ deckhand/tests/unit/control/test_api.py | 18 ++++++++++++++---- deckhand/tests/unit/views/test_views.py | 12 ++++++++++++ 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/deckhand/control/api.py b/deckhand/control/api.py index 15d3257f..24b6a608 100644 --- a/deckhand/control/api.py +++ b/deckhand/control/api.py @@ -71,6 +71,7 @@ def start_api(state_manager=None): v1_0_routes = [ ('documents', documents.DocumentsResource()), ('revisions', revisions.RevisionsResource()), + ('revisions/{revision_id}', revisions.RevisionResource()), ('revisions/{revision_id}/documents', revision_documents.RevisionDocumentsResource()), ('secrets', secrets.SecretsResource()) diff --git a/deckhand/control/common.py b/deckhand/control/common.py index a2c26dd5..67c214b7 100644 --- a/deckhand/control/common.py +++ b/deckhand/control/common.py @@ -24,3 +24,8 @@ class ViewBuilder(object): """Model API responses as dictionaries.""" _collection_name = None + + def _gen_url(self, revision): + # TODO: Use a config-based url for the base url below. + base_url = 'https://deckhand/api/v1.0/%s/%s' + return base_url % (self._collection_name, revision.get('id')) diff --git a/deckhand/control/revisions.py b/deckhand/control/revisions.py index 9c73e146..5b0fcae6 100644 --- a/deckhand/control/revisions.py +++ b/deckhand/control/revisions.py @@ -20,7 +20,7 @@ from deckhand.db.sqlalchemy import api as db_api class RevisionsResource(api_base.BaseResource): - """API resource for realizing CRUD endpoints for Revisions.""" + """API resource for realizing GET /revisions.""" def on_get(self, req, resp): """Returns list of existing revisions. @@ -30,8 +30,25 @@ class RevisionsResource(api_base.BaseResource): of each revision. """ revisions = db_api.revision_get_all() - revisions_view = revision_view.ViewBuilder().list(revisions) + revisions_resp = revision_view.ViewBuilder().list(revisions) resp.status = falcon.HTTP_200 resp.append_header('Content-Type', 'application/x-yaml') - resp.body = self.to_yaml_body(revisions_view) + resp.body = self.to_yaml_body(revisions_resp) + + +class RevisionResource(api_base.BaseResource): + """API resource for realizing GET /revisions/{revision_id}.""" + + def on_get(self, req, resp, revision_id): + """Returns detailed description of a particular revision. + + The status of each ValidationPolicy belonging to the revision is also + included. + """ + revision = db_api.revision_get(revision_id) + revision_resp = revision_view.ViewBuilder().show(revision) + + resp.status = falcon.HTTP_200 + resp.append_header('Content-Type', 'application/x-yaml') + resp.body = self.to_yaml_body(revision_resp) diff --git a/deckhand/control/views/revision.py b/deckhand/control/views/revision.py index 32270072..095b2c88 100644 --- a/deckhand/control/views/revision.py +++ b/deckhand/control/views/revision.py @@ -36,3 +36,12 @@ class ViewBuilder(common.ViewBuilder): resp_body['results'].append(result) return resp_body + + def show(self, revision): + return { + 'id': revision.get('id'), + 'createdAt': revision.get('created_at'), + 'url': self._gen_url(revision), + # TODO: Not yet implemented. + 'validationPolicies': [], + } diff --git a/deckhand/tests/unit/control/test_api.py b/deckhand/tests/unit/control/test_api.py index ddbac81c..0346974c 100644 --- a/deckhand/tests/unit/control/test_api.py +++ b/deckhand/tests/unit/control/test_api.py @@ -30,10 +30,18 @@ class TestApi(testtools.TestCase): super(TestApi, self).setUp() for resource in (documents, revisions, revision_documents, secrets): resource_name = resource.__name__.split('.')[-1] - resource_obj = mock.patch.object( - resource, '%sResource' % resource_name.title().replace('_', '') - ).start() - setattr(self, '%s_resource' % resource_name, resource_obj) + + # Mock the singular/plural version. + resource_names = [resource_name, resource_name[:-1]] + for resource_name in resource_names: + try: + resource_obj = mock.patch.object( + resource, '%sResource' % resource_name.title().replace( + '_', '')).start() + setattr(self, '%s_resource' % resource_name, resource_obj) + except AttributeError: + # If a resource doesn't exist, ignore. + pass @mock.patch.object(api, 'db_api', autospec=True) @mock.patch.object(api, 'config', autospec=True) @@ -50,6 +58,8 @@ class TestApi(testtools.TestCase): mock_falcon_api.add_route.assert_has_calls([ mock.call('/api/v1.0/documents', self.documents_resource()), mock.call('/api/v1.0/revisions', self.revisions_resource()), + mock.call('/api/v1.0/revisions/{revision_id}', + self.revision_resource()), mock.call('/api/v1.0/revisions/{revision_id}/documents', self.revision_documents_resource()), mock.call('/api/v1.0/secrets', self.secrets_resource()) diff --git a/deckhand/tests/unit/views/test_views.py b/deckhand/tests/unit/views/test_views.py index e186b0a6..99d0d80c 100644 --- a/deckhand/tests/unit/views/test_views.py +++ b/deckhand/tests/unit/views/test_views.py @@ -64,3 +64,15 @@ class TestRevisionViews(base.TestDbBase): self.assertIn('id', revisions_view['results'][idx]) self.assertEqual(doc_count, revisions_view['results'][idx][ 'count']) + + def test_show_revision(self): + payload = [base.DocumentFixture.get_minimal_fixture() + for _ in range(4)] + documents = self._create_documents(payload) + revision = self._get_revision(documents[0]['revision_id']) + revision_view = self.view_builder.show(revision) + + expected_attrs = ('id', 'url', 'createdAt', 'validationPolicies') + for attr in expected_attrs: + self.assertIn(attr, revision_view) + self.assertIsInstance(revision_view['validationPolicies'], list)