Updated /GET revisions response body.
This commit is contained in:
parent
8ff4639c6c
commit
841906a435
|
@ -79,15 +79,23 @@ Sample response:
|
|||
|
||||
```yaml
|
||||
---
|
||||
child_id: null
|
||||
count: 2
|
||||
created_at: '2017-07-31T14:36:00.348967'
|
||||
deleted: false
|
||||
deleted_at: null
|
||||
id: d3428d6a-d8c4-4a5b-8006-aba974cc36a2
|
||||
parent_id: null
|
||||
results: []
|
||||
updated_at: '2017-07-31T14:36:00.348973'
|
||||
count: 7
|
||||
next: https://deckhand/api/v1.0/revisions?limit=2&offset=2
|
||||
prev: null
|
||||
results:
|
||||
- id: 0
|
||||
url: https://deckhand/api/v1.0/revisions/0
|
||||
createdAt: 2017-07-14T21:23Z
|
||||
validationPolicies:
|
||||
site-deploy-validation:
|
||||
status: failed
|
||||
- id: 1
|
||||
url: https://deckhand/api/v1.0/revisions/1
|
||||
createdAt: 2017-07-16T01:15Z
|
||||
validationPolicies:
|
||||
site-deploy-validation:
|
||||
status: succeeded
|
||||
...
|
||||
```
|
||||
|
||||
GET `/revisions/{revision_id}/documents`
|
||||
|
|
|
@ -17,11 +17,14 @@ import yaml
|
|||
|
||||
import falcon
|
||||
from falcon import request
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils as json
|
||||
import six
|
||||
|
||||
from deckhand import errors
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseResource(object):
|
||||
"""Base resource class for implementing API resources."""
|
||||
|
@ -79,12 +82,17 @@ class BaseResource(object):
|
|||
resp.status = status_code
|
||||
|
||||
def to_yaml_body(self, dict_body):
|
||||
"""Converts dictionary into YAML response body.
|
||||
"""Converts JSON body into YAML response body.
|
||||
|
||||
:dict_body: response body to be converted to YAML.
|
||||
:returns: YAML encoding of `dict_body`.
|
||||
"""
|
||||
if isinstance(dict_body, dict):
|
||||
return yaml.safe_dump(dict_body)
|
||||
elif isinstance(dict_body, list):
|
||||
return yaml.safe_dump_all(dict_body)
|
||||
return TypeError('Unrecognized dict_body type when converting response'
|
||||
' body to YAML format.')
|
||||
|
||||
|
||||
class DeckhandRequestContext(object):
|
||||
|
|
|
@ -47,10 +47,11 @@ class DocumentsResource(api_base.BaseResource):
|
|||
LOG.error(error_msg)
|
||||
return self.return_error(resp, falcon.HTTP_400, message=error_msg)
|
||||
|
||||
# Validate the document before doing anything with it.
|
||||
# All concrete documents in the payload must successfully pass their
|
||||
# JSON schema validations. Otherwise raise an error.
|
||||
try:
|
||||
for doc in documents:
|
||||
document_validation.DocumentValidation(doc)
|
||||
document_validation.DocumentValidation(doc).pre_validate()
|
||||
except deckhand_errors.InvalidFormat as e:
|
||||
return self.return_error(resp, falcon.HTTP_400, message=e)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ from deckhand.db.sqlalchemy import api as db_api
|
|||
|
||||
|
||||
class RevisionsResource(api_base.BaseResource):
|
||||
"""API resource for realizing CRUD endpoints for Document Revisions."""
|
||||
"""API resource for realizing CRUD endpoints for Revisions."""
|
||||
|
||||
def on_get(self, req, resp):
|
||||
"""Returns list of existing revisions.
|
||||
|
|
|
@ -212,11 +212,21 @@ def revision_get_all(session=None):
|
|||
session = session or get_session()
|
||||
revisions = session.query(models.Revision).all()
|
||||
revisions_resp = [r.to_dict() for r in revisions]
|
||||
resp_body = {
|
||||
'count': len(revisions_resp),
|
||||
'next': None,
|
||||
'prev': None,
|
||||
'revisions': []
|
||||
}
|
||||
|
||||
for revision in revisions_resp:
|
||||
revision['count'] = len(revision.pop('documents'))
|
||||
result = {}
|
||||
for attr in ('id', 'created_at'):
|
||||
result[utils.to_camel_case(attr)] = revision[attr]
|
||||
result['count'] = len(revision.pop('documents'))
|
||||
resp_body['revisions'].append(result)
|
||||
|
||||
return revisions_resp
|
||||
return resp_body
|
||||
|
||||
|
||||
def revision_get_documents(revision_id, session=None, **filters):
|
||||
|
|
|
@ -121,7 +121,7 @@ class Document(BASE, DeckhandBase):
|
|||
name = Column(String(64), nullable=False)
|
||||
# NOTE: Do not define a maximum length for these JSON data below. However,
|
||||
# this approach is not compatible with all database types.
|
||||
# "metadata" is reserved, so use "doc_metadata" instead.
|
||||
# "metadata" is reserved, so use "_metadata" instead.
|
||||
_metadata = Column(oslo_types.JsonEncodedDict(), nullable=False)
|
||||
data = Column(oslo_types.JsonEncodedDict(), nullable=False)
|
||||
revision_id = Column(Integer, ForeignKey('revisions.id'), nullable=False)
|
||||
|
|
|
@ -34,7 +34,6 @@ class DocumentValidation(object):
|
|||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.pre_validate_data()
|
||||
|
||||
class SchemaVersion(object):
|
||||
"""Class for retrieving correct schema for pre-validation on YAML.
|
||||
|
@ -65,7 +64,7 @@ class DocumentValidation(object):
|
|||
return [v['schema'] for v in self.internal_validations
|
||||
if v['version'] == self.schema_version][0].schema
|
||||
|
||||
def pre_validate_data(self):
|
||||
def pre_validate(self):
|
||||
"""Pre-validate that the YAML file is correctly formatted."""
|
||||
self._validate_with_schema()
|
||||
|
||||
|
|
|
@ -23,6 +23,10 @@ class TestRevisions(base.TestDbBase):
|
|||
self._create_documents(payload)
|
||||
|
||||
revisions = self._list_revisions()
|
||||
self.assertIsInstance(revisions, list)
|
||||
self.assertIsInstance(revisions, dict)
|
||||
self.assertIn('revisions', revisions)
|
||||
self.assertIsInstance(revisions['revisions'], list)
|
||||
|
||||
revisions = revisions['revisions']
|
||||
self.assertEqual(1, len(revisions))
|
||||
self.assertEqual(4, revisions[0]["count"])
|
||||
|
|
|
@ -71,10 +71,8 @@ class TestDocumentValidation(testtools.TestCase):
|
|||
return corrupted_data
|
||||
|
||||
def test_initialization(self):
|
||||
doc_validation = document_validation.DocumentValidation(
|
||||
self.data)
|
||||
self.assertIsInstance(doc_validation,
|
||||
document_validation.DocumentValidation)
|
||||
doc_validation = document_validation.DocumentValidation(self.data)
|
||||
doc_validation.pre_validate() # Should not raise any errors.
|
||||
|
||||
def test_initialization_missing_sections(self):
|
||||
expected_err = ("The provided YAML file is invalid. Exception: '%s' "
|
||||
|
@ -91,11 +89,12 @@ class TestDocumentValidation(testtools.TestCase):
|
|||
for invalid_entry, missing_key in invalid_data:
|
||||
with six.assertRaisesRegex(self, errors.InvalidFormat,
|
||||
expected_err % missing_key):
|
||||
document_validation.DocumentValidation(invalid_entry)
|
||||
doc_validation = document_validation.DocumentValidation(
|
||||
invalid_entry)
|
||||
doc_validation.pre_validate()
|
||||
|
||||
def test_initialization_missing_abstract_section(self):
|
||||
expected_err = ("Could not find 'abstract' property from document.")
|
||||
|
||||
invalid_data = [
|
||||
self._corrupt_data('metadata'),
|
||||
self._corrupt_data('metadata.layeringDefinition'),
|
||||
|
@ -105,7 +104,9 @@ class TestDocumentValidation(testtools.TestCase):
|
|||
for invalid_entry in invalid_data:
|
||||
with six.assertRaisesRegex(self, errors.InvalidFormat,
|
||||
expected_err):
|
||||
document_validation.DocumentValidation(invalid_entry)
|
||||
doc_validation = document_validation.DocumentValidation(
|
||||
invalid_entry)
|
||||
doc_validation.pre_validate()
|
||||
|
||||
@mock.patch.object(document_validation, 'LOG', autospec=True)
|
||||
def test_initialization_with_abstract_document(self, mock_log):
|
||||
|
@ -114,7 +115,9 @@ class TestDocumentValidation(testtools.TestCase):
|
|||
for true_val in (True, 'true', 'True'):
|
||||
abstract_data['metadata']['layeringDefinition']['abstract'] = True
|
||||
|
||||
document_validation.DocumentValidation(abstract_data)
|
||||
doc_validation = document_validation.DocumentValidation(
|
||||
abstract_data)
|
||||
doc_validation.pre_validate()
|
||||
mock_log.info.assert_called_once_with(
|
||||
"Skipping validation for the document because it is abstract")
|
||||
mock_log.info.reset_mock()
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import string
|
||||
|
||||
|
||||
def multi_getattr(multi_key, dict_data):
|
||||
"""Iteratively check for nested attributes in the YAML data.
|
||||
|
@ -45,3 +47,8 @@ def multi_getattr(multi_key, dict_data):
|
|||
data = data.get(attr)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def to_camel_case(s):
|
||||
return (s[0].lower() + string.capwords(s, sep='_').replace('_', '')[1:]
|
||||
if s else s)
|
||||
|
|
Loading…
Reference in New Issue