8000 Use plain functions for encoding · millerlucas/python-openapi-codec@7b9c818 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7b9c818

Browse files
committed
Use plain functions for encoding
1 parent 5b5a079 commit 7b9c818

File tree

5 files changed

+144
-192
lines changed

5 files changed

+144
-192
lines changed

openapi_codec/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from coreapi.document import Document, Link, Field
66
from coreapi.exceptions import ParseError
77
from openapi_codec.utils import _get_string, _get_dict, _get_list, _get_bool, get_strings, get_dicts
8-
from openapi_codec.converters import DocumentToOpenAPIConverter
8+
from openapi_codec.converters import generate_swagger_object
99

1010

1111
__version__ = "0.0.3"
@@ -142,5 +142,5 @@ def load(self, bytes, base_url=None):
142142
return doc
143143

144144
def dump(self, document, **kwargs):
145-
converter = DocumentToOpenAPIConverter(document)
146-
return force_bytes(json.dumps(converter.convert()))
145+
data = generate_swagger_object(document)
146+
return force_bytes(json.dumps(data))

openapi_codec/converters.py

Lines changed: 78 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,82 @@
11
from coreapi.compat import urlparse
22

33

4-
class DocumentToOpenAPIConverter(object):
5-
def __init__(self, document):
6-
self.document = document
7-
8-
def convert(self):
9-
return self._generate_swagger_object()
10-
11-
def _generate_swagger_object(self):
12-
"""
13-
Generates root of the Swagger spec.
14-
"""
15-
parsed_url = urlparse.urlparse(self.document.url)
16-
17-
return {
18-
'swagger': '2.0',
19-
'info': self._get_info_object(),
20-
'paths': self._get_paths_object(),
21-
'host': parsed_url.netloc,
4+
def generate_swagger_object(document):
5+
"""
6+
Generates root of the Swagger spec.
7+
"""
8+
parsed_url = urlparse.urlparse(document.url)
9+
10+
return {
11+
'swagger': '2.0',
12+
'info': _get_info_object(document),
13+
'paths': _get_paths_object(document),
14+
'host': parsed_url.netloc,
15+
}
16+
17+
18+
def _get_info_object(document):
19+
return {
20+
'title': document.title,
21+
'version': '' # Required by the spec
22+
}
23+
24+
25+
def _get_paths_object(document):
26+
paths = {}
27+
for tag, object_ in document.data.items():
28+
if not hasattr(object_, 'links'):
29+
continue
30+
31+
for link in object_.links.values():
32+
if link.url not in paths:
33+
paths[link.url] = {}
34+
35+
operation = _get_operation(tag, link)
36+
paths[link.url].update({link.action: operation})
37+
38+
return paths
39+
40+
41+
def _get_operation(tag, link):
42+
return {
43+
'tags': [tag],
44+
'description': link.description,
45+
'responses': _get_responses(link),
46+
'parameters': _get_parameters(link.fields)
47+
}
48+
49+
50+
def _get_parameters(fields):
51+
"""
52+
Generates Swagger Parameter Item object.
53+
"""
54+
return [
55+
{
56+
'name': field.name,
57+
'required': field.required,
58+
'in': _convert_location_to_in(field),
59+
'description': field.description,
60+
'type': 'string'
2261
}
23-
24-
def _get_info_object(self):
25-
return {
26-
'title': self.document.title,
27-
'version': '' # Required by the spec
28-
}
29-
30-
def _get_paths_object(self):
31-
paths = {}
32-
for tag, object_ in self.document.data.items():
33-
if not hasattr(object_, 'links'):
34-
continue
35-
36-
for link in object_.links.values():
37-
if link.url not in paths:
38-
paths[link.url] = {}
39-
40-
operation = self._get_operation(tag, link)
41-
paths[link.url].update({link.action: operation})
42-
43-
return paths
44-
45-
@classmethod
46-
def _get_operation(cls, tag, link):
47-
return {
48-
'tags': [tag],
49-
'description': link.description,
50-
'responses': cls._get_responses(link.action),
51-
'parameters': cls._get_parameters(link.fields)
52-
}
53-
54-
@classmethod
55-
def _get_parameters(cls, fields):
56-
"""
57-
Generates Swagger Parameter Item object.
58-
"""
59-
return [
60-
{
61-
'name': field.name,
62-
'required': field.required,
63-
'in': cls._convert_location_to_in(field.location),
64-
'description': field.description,
65-
'type': 'string'
66-
}
67-
for field in fields
68-
]
69-
70-
@classmethod
71-
def _convert_location_to_in(cls, location):
72-
"""
73-
Translates the CoreAPI field `location` into the Swagger `in`.
74-
The values are all the same with the exception of form -> formData.
75-
"""
76-
if location == 'form':
77-
return 'formData'
78-
79-
return location
80-
81-
@classmethod
82-
def _get_responses(cls, action):
83-
"""
84-
Returns minimally acceptable responses object based
85-
on action / method type.
86-
"""
87-
template = {'description': ''}
88-
if action == 'post':
89-
return {'201': template}
90-
if action == 'delete':
91-
return {'204': template}
92-
93-
return {'200': template}
62+
for field in fields
63+
]
64+
65+
66+
def _convert_location_to_in(field):
67+
if field.location == 'form':
68+
return 'formData'
69+
return field.location
70+
71+
72+
def _get_responses(link):
73+
"""
74+
Returns minimally acceptable responses object based
75+
on action / method type.
76+
"""
77+
template = {'description': ''}
78+
if link.action == 'post':
79+
return {'201': template}
80+
if link.action == 'delete':
81+
return {'204': template}
82+
return {'200': template}

tests/compat.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/test_converters.py

Lines changed: 63 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
1-
import uuid
2-
31
import coreapi
4-
from openapi_codec.converters import DocumentToOpenAPIConverter
5-
6-
from .compat import mock, TestCase
2+
from openapi_codec.converters import generate_swagger_object, _get_parameters
3+
from unittest import TestCase
74

85

9-
class TestGetInfoObject(TestCase):
6+
class TestBasicInfo(TestCase):
107
def setUp(self):
118
self.document = coreapi.Document(title='Example API')
12-
converter = DocumentToOpenAPIConverter(self.document)
13-
self.sut = converter._get_info_object()
9+
self.swagger = generate_swagger_object(self.document)
1410

15-
def test_title(self):
16-
self.assertDictContainsSubset({'title': self.document.title}, self.sut)
11+
def test_info(self):
12+
self.assertIn('info', self.swagger)
13+
expected = {
14+
'title': self.document.title,
15+
'version': ''
16+
}
17+
self.assertEquals(self.swagger['info'], expected)
1718

18-
def test_version(self):
19-
"""
20-
Ensures that the version is provided since it is a required field.
21-
"""
22-
self.assertDictContainsSubset({'version': ''}, self.sut)
19+
def test_swagger_version(self):
20+
self.assertIn('swagger', self.swagger)
21+
expected = '2.0'
22+
self.assertEquals(self.swagger['swagger'], expected)
2323

24+
def test_host(self):
25+
self.assertIn('host', self.swagger)
26+
expected = ''
27+
self.assertEquals(self.swagger['host'], expected)
2428

25-
class TestGetPathsObject(TestCase):
29+
30+
class TestPaths(TestCase):
2631
def setUp(self):
27-
self.path = '/users'
32+
self.path = '/users/'
2833
self.document = coreapi.Document(
2934
content={
3035
'users': {
@@ -39,79 +44,54 @@ def setUp(self):
3944
}
4045
}
4146
)
42-
self.sut = DocumentToOpenAPIConverter(self.document) \
43-
._get_paths_object()
44-
45-
def test_url_is_converted_to_key(self):
46-
self.assertIn(self.path, self.sut)
47-
48-
def test_actions_are_converted_to_keys_under_url(self):
49-
expected = [
50-
link.action for link in self.document.data['users'].values()
51-
]
52-
self.assertCountEqual(expected, self.sut[self.path].keys())
47+
self.swagger = generate_swagger_object(self.document)
48+
49+
def test_paths(self):
50+
self.assertIn('paths', self.swagger)
51+
self.assertIn(self.path, self.swagger['paths'])
52+
self.assertIn('get', self.swagger['paths'][self.path])
53+
self.assertIn('post', self.swagger['paths'][self.path])
54+
expected = {
55+
'responses': {
56+
'200': {
57+
'description': ''
58+
}
59+
},
60+
'parameters': [],
61+
'description': '',
62+
'tags': ['users']
63+
}
64+
self.assertEquals(self.swagger['paths'][self.path]['get'], expected)
65+
expected = {
66+
'responses': {
67+
'201': {
68+
'description': ''
69+
}
70+
},
71+
'parameters': [],
72+
'description': '',
73+
'tags': ['users']
74+
}
75+
self.assertEquals(self.swagger['paths'][self.path]['post'], expected)
5376

5477

55-
class TestGetParameters(TestCase):
78+
class TestParameters(TestCase):
5679
def setUp(self):
5780
self.field = coreapi.Field(
5881
name='email',
59-
required='true',
82+
required=True,
6083
location='query',
6184
description='A valid email address.'
6285
)
63-
patcher = mock.patch.object(
64-
DocumentToOpenAPIConverter,
65-
'_convert_location_to_in'
66-
)
67-
self.location_mock = patcher.start()
68-
self.addCleanup(patcher.stop)
69-
70-
self.sut = DocumentToOpenAPIConverter \
71-
._get_parameters([self.field])[0]
86+
self.swagger = _get_parameters([self.field])
7287

7388
def test_expected_fields(self):
74-
self.assertDictContainsSubset(
75-
{
76-
'name': self.field.name,
77-
'required': self.field.required,
78-
'in': self.location_mock.return_value,
79-
'description': self.field.description,
80-
'type': 'string' # Everything is a string for now.
81-
},
82-
self.sut
83-
)
84-
85-
86-
class TestConvertLocationToIn(TestCase):
87-
def setUp(self):
88-
self.sut = DocumentToOpenAPIConverter._convert_location_to_in
89-
90-
def test_form_is_converted_to_formdata(self):
91-
self.assertEqual('formData', self.sut('form'))
92-
93-
def test_random_string_is_returned_as_is(self):
94-
"""
95-
Asserts that any input (other than form) is returned as-is,
96-
since the Swagger Parameter object `in` property maps 1:1 with
97-
the Field.location property,
98-
"""
99-
expected = str(uuid.uuid4())
100-
self.assertEqual(expected, self.sut(expected))
101-
102-
103-
class TestGetResponses(TestCase):
104-
def setUp(self):
105-
self.sut = DocumentToOpenAPIConverter._get_responses
106-
107-
def test_post(self):
108-
self.assertDictEqual({'201': {'description': ''}}, self.sut('post'))
109-
110-
def test_delete(self):
111-
self.assertDictEqual({'201': {'description': ''}}, self.sut('post'))
112-
113-
def test_default(self):
114-
self.assertDictEqual(
115-
{'200': {'description': ''}},
116-
self.sut(uuid.uuid4())
117-
)
89+
self.assertEquals(len(self.swagger), 1)
90+
expected = {
91+
'name': self.field.name,
92+
'required': self.field.required,
93+
'in': 'query',
94+
'description': self.field.description,
95+
'type': 'string' # Everything is a string for now.
96+
}
97+
self.assertEquals(self.swagger[0], expected)

tests/test_example.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)
0