From 97724aeb4fcb23ce04d3719b65f1f6c1d6a3e858 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Sun, 11 Jun 2017 14:50:51 -0400 Subject: [PATCH 01/20] fixing #37 issue support of definitions for nested objects --- openapi_codec/encode.py | 65 +++++++++++++++++++++++++--- tests/test_encode.py | 94 ++++++++++++++++++++++++++++++++++++++++- tests/test_mappings.py | 1 - 3 files changed, 153 insertions(+), 7 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 4a6fbec..7ed41fc 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -1,5 +1,6 @@ import coreschema from collections import OrderedDict +from coreapi.document import Field from coreapi.compat import urlparse from openapi_codec.utils import get_method, get_encoding, get_location, get_links_from_document @@ -23,15 +24,61 @@ def generate_swagger_object(document): swagger['schemes'] = [parsed_url.scheme] swagger['paths'] = _get_paths_object(document) + swagger['definitions'] = _get_definitions(document) return swagger +def _get_definitions(document): + + definitions = dict() + links = _get_links(document) + + def get_field_def_data(field_item, defs): + + definition_data = { + 'type': 'object', + 'properties': {} + } + + if isinstance(field_item, coreschema.Object): + props = field_item.properties + else: + props = field_item.schema.properties + + for f_name, f_schema in props.iteritems(): + + if _get_field_type(f_schema) is 'object': + defs[f_name] = get_field_def_data(f_schema, defs) + definition_data['properties'][f_name] = { + '$ref': '#/definitions/{}'.format(f_name) + } + else: + definition_data['properties'][f_name] = { + 'type': _get_field_type(f_schema), + 'description': '' + } + + return definition_data + + for _, link, _ in links: + for field in link.fields: + field_type = _get_field_type(field) + if field_type == 'object': + definitions[field.name] = get_field_def_data(field, definitions) + + if field_type == 'array': + item_name = '{}_item'.format(field.name) + definitions[item_name] = get_field_def_data(field.schema.items, definitions) + + return definitions + + def _add_tag_prefix(item): operation_id, link, tags = item if tags: operation_id = tags[0] + '_' + operation_id - return (operation_id, link, tags) + return operation_id, link, tags def _get_links(document): @@ -114,8 +161,10 @@ def _get_field_type(field): # Deprecated return field.type - if field.schema is None: - return 'string' + if isinstance(field, Field): + cls = field.schema.__class__ + else: + cls = field.__class__ return { coreschema.String: 'string', @@ -124,7 +173,7 @@ def _get_field_type(field): coreschema.Boolean: 'boolean', coreschema.Array: 'array', coreschema.Object: 'object', - }.get(field.schema.__class__, 'string') + }.get(cls, 'string') def _get_parameters(link, encoding): @@ -160,8 +209,14 @@ def _get_parameters(link, encoding): 'description': field_description, 'type': field_type, } + if field_type == 'array': - schema_property['items'] = {'type': 'string'} + item_name = '{}_item'.format(field.name) + schema_property['items'] = {'$ref': '#/definitions/{}'.format(item_name)} + + if field_type == 'object': + schema_property = {'$ref': '#/definitions/{}'.format(field.name)} + properties[field.name] = schema_property if field.required: required.append(field.name) diff --git a/tests/test_encode.py b/tests/test_encode.py index 0ffd883..76adc3b 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -1,6 +1,8 @@ import coreapi import coreschema -from openapi_codec.encode import generate_swagger_object, _get_parameters + +from collections import OrderedDict +from openapi_codec.encode import generate_swagger_object, _get_parameters, _get_definitions from unittest import TestCase @@ -32,6 +34,11 @@ def test_schemes(self): expected = ['https'] self.assertEquals(self.swagger['schemes'], expected) + def test_definitions(self): + self.assertIn('definitions', self.swagger) + expected = dict() + self.assertEquals(self.swagger['definitions'], expected) + class TestPaths(TestCase): def setUp(self): @@ -101,3 +108,88 @@ def test_expected_fields(self): 'type': 'string' # Everything is a string for now. } self.assertEquals(self.swagger[0], expected) + + +class TestDefinitions(TestCase): + + def setUp(self): + + obj_props = OrderedDict() + obj_props['foo'] = coreschema.String() + obj_props['bar'] = coreschema.Integer() + + self.object_field = coreapi.Field( + name='dummy_object', + required=True, + location='form', + schema=coreschema.Object( + properties=obj_props + ) + ) + + self.array_field = coreapi.Field( + name='dummy_array', + location='form', + schema=coreschema.Array( + items=self.object_field + ) + ) + + self.link = coreapi.Link( + action='post', + url='/users/', + fields=[self.object_field, self.array_field] + ) + + self.document = coreapi.Document( + content={ + 'users': { + 'create': self.link, + } + } + ) + + self.definitions = _get_definitions(self.document) + self.parameters = _get_parameters(self.link, '') + self.swagger = generate_swagger_object(self.document) + + def test_basic_definitions(self): + + print self.definitions + + self.assertIn('definitions', self.swagger) + self.assertIn('dummy_object', self.definitions) + self.assertIn('dummy_array_item', self.definitions) + + expected_dummy_object_def = { + 'type': 'object', + 'properties': { + 'foo': {'type': 'string', 'description': ''}, + 'bar': {'type': 'integer', 'description': ''} + } + } + + self.assertEqual(self.definitions.get('dummy_object'), expected_dummy_object_def) + self.assertEqual(self.definitions.get('dummy_array_item'), expected_dummy_object_def) + + expected_dummy_parameters = [{ + 'schema': { + 'required': ['dummy_object'], + 'type': 'object', + 'properties': { + 'dummy_array': { + 'items': { + '$ref': '#/definitions/dummy_array_item' + }, + 'type': 'array', + 'description': '' + }, + 'dummy_object': { + '$ref': '#/definitions/dummy_object' + } + } + }, + 'name': 'data', + 'in': 'body' + }] + self.assertEqual(self.parameters, expected_dummy_parameters) diff --git a/tests/test_mappings.py b/tests/test_mappings.py index 87d23c2..8904543 100644 --- a/tests/test_mappings.py +++ b/tests/test_mappings.py @@ -202,7 +202,6 @@ def test_mapping(): coreapi.Field( name='example', location='body', - schema=coreschema.String() ) ] From a67ba3aed851fec952fc9694413df33c33c853ba Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Sun, 11 Jun 2017 14:55:34 -0400 Subject: [PATCH 02/20] fix unexpected print --- tests/test_encode.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index 76adc3b..5073f37 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -155,8 +155,6 @@ def setUp(self): def test_basic_definitions(self): - print self.definitions - self.assertIn('definitions', self.swagger) self.assertIn('dummy_object', self.definitions) self.assertIn('dummy_array_item', self.definitions) From 2391f7886ed97f1f0b98c1473b8b14c9440081d2 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Sun, 11 Jun 2017 15:14:30 -0400 Subject: [PATCH 03/20] py2X<->py3X --- openapi_codec/encode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 7ed41fc..fbb9906 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -46,7 +46,7 @@ def get_field_def_data(field_item, defs): else: props = field_item.schema.properties - for f_name, f_schema in props.iteritems(): + for f_name, f_schema in iter(props.items()): if _get_field_type(f_schema) is 'object': defs[f_name] = get_field_def_data(f_schema, defs) From aea9c8c24aa1e882947ffb61ca42532d10952ed3 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Mon, 26 Jun 2017 10:28:12 -0400 Subject: [PATCH 04/20] OrderedDict instead of regular dict --- openapi_codec/encode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index fbb9906..cba60f5 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -31,7 +31,7 @@ def generate_swagger_object(document): def _get_definitions(document): - definitions = dict() + definitions = OrderedDict() links = _get_links(document) def get_field_def_data(field_item, defs): From 5a7bb2a8a7c2376dd224cb979ce6697ffd9f5360 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Mon, 15 Jan 2018 09:44:13 -0500 Subject: [PATCH 05/20] adding py36 to tox envlist --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 3e9707d..9c2f2e1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35,py34,py27 +envlist = py35,py34,py36,py27 [testenv] deps = -rrequirements.txt commands = ./runtests From 532bfbdc04b8f412a9e2931e17bf7b12a6990235 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Tue, 16 Jan 2018 19:11:29 -0500 Subject: [PATCH 06/20] preliminary changes --- openapi_codec/encode.py | 117 ++++++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index cba60f5..2b0038e 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -1,4 +1,7 @@ +import random +import string import coreschema + from collections import OrderedDict from coreapi.document import Field from coreapi.compat import urlparse @@ -23,53 +26,84 @@ def generate_swagger_object(document): if parsed_url.scheme: swagger['schemes'] = [parsed_url.scheme] - swagger['paths'] = _get_paths_object(document) swagger['definitions'] = _get_definitions(document) + swagger['paths'] = _get_paths_object(document, swagger['definitions']) return swagger -def _get_definitions(document): +def _get_or_update_definitions(update_def_data, update_def_name, definitions): - definitions = OrderedDict() - links = _get_links(document) + # Check if there's existing definition with same or props + clashing_def_names = filter(lambda d: d.startswith(update_def_name) or definitions.get(d) == update_def_data, definitions.keys()) - def get_field_def_data(field_item, defs): + for clashing_def_name in clashing_def_names: + clash_def_data = definitions.get(clashing_def_name) + if clash_def_data == update_def_data: + return clash_def_data + else: + if clashing_def_names: + rand_part = ''.join([random.choice(string.letters + string.digits) for _ in range(5)]) + update_def_name = '{}_{}'.format(update_def_name, rand_part) + definitions[update_def_name] = update_def_data + return update_def_data - definition_data = { - 'type': 'object', - 'properties': {} - } + return None - if isinstance(field_item, coreschema.Object): - props = field_item.properties + +def _get_field_definition_data(field_item, defs): + + definition_data = { + 'type': 'object', + 'properties': {} + } + + if isinstance(field_item, coreschema.Object): + props = field_item.properties + elif isinstance(field_item.schema, coreschema.schemas.Array): + props = field_item.schema.items.properties + else: + props = field_item.schema.properties + + for f_name, f_schema in iter(props.items()): + + if _get_field_type(f_schema) is 'object': + def_data = _get_or_update_definitions( + _get_field_definition_data(f_schema, defs), + '{}_def_item'.format(f_schema.name), + defs + ) + if def_data: + return def_data else: - props = field_item.schema.properties + definition_data['properties'][f_name] = { + 'type': _get_field_type(f_schema), + 'description': '' + } - for f_name, f_schema in iter(props.items()): + return definition_data - if _get_field_type(f_schema) is 'object': - defs[f_name] = get_field_def_data(f_schema, defs) - definition_data['properties'][f_name] = { - '$ref': '#/definitions/{}'.format(f_name) - } - else: - definition_data['properties'][f_name] = { - 'type': _get_field_type(f_schema), - 'description': '' - } - return definition_data +def _get_definitions(document): + + definitions = OrderedDict() + links = _get_links(document) for _, link, _ in links: for field in link.fields: field_type = _get_field_type(field) + + # Get field definition data if field_type == 'object': - definitions[field.name] = get_field_def_data(field, definitions) + def_data = _get_field_definition_data(field, definitions) + elif field_type == 'array': + def_data = _get_field_definition_data(field.schema.items, definitions) - if field_type == 'array': - item_name = '{}_item'.format(field.name) - definitions[item_name] = get_field_def_data(field.schema.items, definitions) + _get_or_update_definitions( + def_data, + '{}_def_item'.format(field.name), + definitions + ) return definitions @@ -107,7 +141,7 @@ def _get_links(document): return links -def _get_paths_object(document): +def _get_paths_object(document, definitions): paths = OrderedDict() links = _get_links(document) @@ -117,13 +151,13 @@ def _get_paths_object(document): paths[link.url] = OrderedDict() method = get_method(link) - operation = _get_operation(operation_id, link, tags) + operation = _get_operation(operation_id, link, tags, definitions) paths[link.url].update({method: operation}) return paths -def _get_operation(operation_id, link, tags): +def _get_operation(operation_id, link, tags, definitions): encoding = get_encoding(link) description = link.description.strip() summary = description.splitlines()[0] if description else None @@ -131,7 +165,7 @@ def _get_operation(operation_id, link, tags): operation = { 'operationId': operation_id, 'responses': _get_responses(link), - 'parameters': _get_parameters(link, encoding) + 'parameters': _get_parameters(link, encoding, definitions) } if description: @@ -176,7 +210,7 @@ def _get_field_type(field): }.get(cls, 'string') -def _get_parameters(link, encoding): +def _get_parameters(link, encoding, definitions): """ Generates Swagger Parameter Item object. """ @@ -210,12 +244,17 @@ def _get_parameters(link, encoding): 'type': field_type, } - if field_type == 'array': - item_name = '{}_item'.format(field.name) - schema_property['items'] = {'$ref': '#/definitions/{}'.format(item_name)} - - if field_type == 'object': - schema_property = {'$ref': '#/definitions/{}'.format(field.name)} + if field_type in ('object', 'array'): + definition_data = _get_field_definition_data(field, definitions).get('properties') + definition = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions)[0] + + schema_property = {'$ref': '#/definitions/{}'.format(definition)} + if field_type == 'array': + schema_property.pop('$ref') + schema_property['type'] = 'array' + schema_property['items'] = { + '$ref': '#/definitions/{}'.format(definition) + } properties[field.name] = schema_property if field.required: From 5be91820298fb97f0fc39e1c4125ac79e25d6bea Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 21:31:46 -0500 Subject: [PATCH 07/20] bugfixing, enhancements, tests update --- openapi_codec/encode.py | 39 ++++++++------ tests/test_encode.py | 114 +++++++++++++++++++--------------------- 2 files changed, 79 insertions(+), 74 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 2b0038e..a8ec413 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -34,8 +34,11 @@ def generate_swagger_object(document): def _get_or_update_definitions(update_def_data, update_def_name, definitions): - # Check if there's existing definition with same or props - clashing_def_names = filter(lambda d: d.startswith(update_def_name) or definitions.get(d) == update_def_data, definitions.keys()) + # Check if there's existing definition with same name or props + clashing_def_names = filter( + lambda d: d.startswith(update_def_name) or definitions.get(d) == update_def_data, + definitions.keys() + ) for clashing_def_name in clashing_def_names: clash_def_data = definitions.get(clashing_def_name) @@ -48,8 +51,6 @@ def _get_or_update_definitions(update_def_data, update_def_name, definitions): definitions[update_def_name] = update_def_data return update_def_data - return None - def _get_field_definition_data(field_item, defs): @@ -63,7 +64,10 @@ def _get_field_definition_data(field_item, defs): elif isinstance(field_item.schema, coreschema.schemas.Array): props = field_item.schema.items.properties else: - props = field_item.schema.properties + try: + props = field_item.schema.properties + except AttributeError: + props = OrderedDict() for f_name, f_schema in iter(props.items()): @@ -98,6 +102,8 @@ def _get_definitions(document): def_data = _get_field_definition_data(field, definitions) elif field_type == 'array': def_data = _get_field_definition_data(field.schema.items, definitions) + else: + def_data = _get_field_definition_data(field, definitions) _get_or_update_definitions( def_data, @@ -245,16 +251,19 @@ def _get_parameters(link, encoding, definitions): } if field_type in ('object', 'array'): - definition_data = _get_field_definition_data(field, definitions).get('properties') - definition = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions)[0] - - schema_property = {'$ref': '#/definitions/{}'.format(definition)} - if field_type == 'array': - schema_property.pop('$ref') - schema_property['type'] = 'array' - schema_property['items'] = { - '$ref': '#/definitions/{}'.format(definition) - } + definition_data = _get_field_definition_data(field, definitions) + + definition_data = definition_data.get('properties') + defs = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions) + + if defs: + schema_property = {'$ref': '#/definitions/{}'.format(defs[0])} + if field_type == 'array': + schema_property.pop('$ref') + schema_property['type'] = 'array' + schema_property['items'] = { + '$ref': '#/definitions/{}'.format(defs[0]) + } properties[field.name] = schema_property if field.required: diff --git a/tests/test_encode.py b/tests/test_encode.py index 5073f37..39aec24 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -96,7 +96,7 @@ def setUp(self): location='query', schema=coreschema.String(description='A valid email address.') ) - self.swagger = _get_parameters(coreapi.Link(fields=[self.field]), encoding='') + self.swagger = _get_parameters(coreapi.Link(fields=[self.field]), encoding='', definitions=OrderedDict()) def test_expected_fields(self): self.assertEquals(len(self.swagger), 1) @@ -114,80 +114,76 @@ class TestDefinitions(TestCase): def setUp(self): - obj_props = OrderedDict() - obj_props['foo'] = coreschema.String() - obj_props['bar'] = coreschema.Integer() + # Clashing name + self.clashing_name = 'author' - self.object_field = coreapi.Field( - name='dummy_object', + # Schema objects + name_schema_obj = coreschema.schemas.Object( + properties=OrderedDict({'name': coreschema.schemas.String(description='name')}) + ) + bday_schema_obj = coreschema.schemas.Object( + properties=OrderedDict({'birthday': coreschema.schemas.String(description='birthday')}) + ) + + # Fields + author_field = coreapi.Field( + name='author', required=True, location='form', - schema=coreschema.Object( - properties=obj_props - ) + schema=name_schema_obj ) - - self.array_field = coreapi.Field( - name='dummy_array', + clashing_author_field = coreapi.Field( + name='author', + required=True, location='form', - schema=coreschema.Array( - items=self.object_field + schema=bday_schema_obj + ) + co_authoors_field = coreapi.Field( + name='co_authoors', + required=True, + location='form', + schema=coreschema.schemas.Array( + items=bday_schema_obj ) ) - self.link = coreapi.Link( - action='post', - url='/users/', - fields=[self.object_field, self.array_field] + # Link objects + v1_songs_link = coreapi.Link( + url='/api/v1/songs/', + action=u'post', + encoding=u'application/json', + fields=[author_field], + ) + v2_songs_link = coreapi.Link( + url='/api/v2/songs/', + action=u'post', + encoding=u'application/json', + fields=[clashing_author_field, co_authoors_field], ) + self.links = OrderedDict({ + 'v1': OrderedDict({'list': v1_songs_link}), + 'v2': OrderedDict({'list': v2_songs_link}) + }) + + # Coreapi document object self.document = coreapi.Document( - content={ - 'users': { - 'create': self.link, - } - } + 'test api', + content=self.links ) + # Init definitions and swagger object self.definitions = _get_definitions(self.document) - self.parameters = _get_parameters(self.link, '') self.swagger = generate_swagger_object(self.document) - def test_basic_definitions(self): + def test_clashing_names(self): + # Basic checks self.assertIn('definitions', self.swagger) - self.assertIn('dummy_object', self.definitions) - self.assertIn('dummy_array_item', self.definitions) - - expected_dummy_object_def = { - 'type': 'object', - 'properties': { - 'foo': {'type': 'string', 'description': ''}, - 'bar': {'type': 'integer', 'description': ''} - } - } + self.assertEqual(len(self.swagger['definitions'].keys()), 2, 'Unexpected definitions count') - self.assertEqual(self.definitions.get('dummy_object'), expected_dummy_object_def) - self.assertEqual(self.definitions.get('dummy_array_item'), expected_dummy_object_def) - - expected_dummy_parameters = [{ - 'schema': { - 'required': ['dummy_object'], - 'type': 'object', - 'properties': { - 'dummy_array': { - 'items': { - '$ref': '#/definitions/dummy_array_item' - }, - 'type': 'array', - 'description': '' - }, - 'dummy_object': { - '$ref': '#/definitions/dummy_object' - } - } - }, - 'name': 'data', - 'in': 'body' - }] - self.assertEqual(self.parameters, expected_dummy_parameters) + # Check nothing unexpected is in definitions + defs = filter( + lambda d: d.startswith('{}_def_item'.format(self.clashing_name)), self.swagger['definitions'].keys() + ) + self.assertEqual(len(defs), 2, 'Unexpected definitions count') \ No newline at end of file From fec8d0e2df833c608b71cfb54198c3df6770ba5b Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 21:35:09 -0500 Subject: [PATCH 08/20] flake8 warning fix --- tests/test_encode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index 39aec24..f3432bc 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -186,4 +186,4 @@ def test_clashing_names(self): defs = filter( lambda d: d.startswith('{}_def_item'.format(self.clashing_name)), self.swagger['definitions'].keys() ) - self.assertEqual(len(defs), 2, 'Unexpected definitions count') \ No newline at end of file + self.assertEqual(len(defs), 2, 'Unexpected definitions count') From 41527828f5c53e4d016700d8ef233adde525b65d Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 21:45:05 -0500 Subject: [PATCH 09/20] py2.7 <-> py3.X --- openapi_codec/encode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index a8ec413..04a6453 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -46,7 +46,7 @@ def _get_or_update_definitions(update_def_data, update_def_name, definitions): return clash_def_data else: if clashing_def_names: - rand_part = ''.join([random.choice(string.letters + string.digits) for _ in range(5)]) + rand_part = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(5)]) update_def_name = '{}_{}'.format(update_def_name, rand_part) definitions[update_def_name] = update_def_data return update_def_data From 09a06485fc063510eadfc8c537ac07ff8491f0ba Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 21:59:36 -0500 Subject: [PATCH 10/20] tests run aftermath fixes --- openapi_codec/encode.py | 10 ++++++++-- tests/test_encode.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 04a6453..507869b 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -257,12 +257,18 @@ def _get_parameters(link, encoding, definitions): defs = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions) if defs: - schema_property = {'$ref': '#/definitions/{}'.format(defs[0])} + # Note: Python2.X <-> Python3.X + try: + def_name = defs[0] + except TypeError: + def_name = next(defs) + + schema_property = {'$ref': '#/definitions/{}'.format(def_name)} if field_type == 'array': schema_property.pop('$ref') schema_property['type'] = 'array' schema_property['items'] = { - '$ref': '#/definitions/{}'.format(defs[0]) + '$ref': '#/definitions/{}'.format(def_name) } properties[field.name] = schema_property diff --git a/tests/test_encode.py b/tests/test_encode.py index f3432bc..edc6569 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -186,4 +186,4 @@ def test_clashing_names(self): defs = filter( lambda d: d.startswith('{}_def_item'.format(self.clashing_name)), self.swagger['definitions'].keys() ) - self.assertEqual(len(defs), 2, 'Unexpected definitions count') + self.assertEqual(len(list(defs)), 2, 'Unexpected definitions count') From cd49ed42b1c791058620b8d72b47b8fb1aba7727 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 22:05:10 -0500 Subject: [PATCH 11/20] small change --- openapi_codec/encode.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 507869b..2a4fe87 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -26,6 +26,9 @@ def generate_swagger_object(document): if parsed_url.scheme: swagger['schemes'] = [parsed_url.scheme] + if not parsed_url.netloc and not parsed_url.scheme: + swagger['host'] = document.url + swagger['definitions'] = _get_definitions(document) swagger['paths'] = _get_paths_object(document, swagger['definitions']) From 56dea307f89009b97eaccad2304bb73ca374df13 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Wed, 17 Jan 2018 22:16:35 -0500 Subject: [PATCH 12/20] statement enhancement --- openapi_codec/encode.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index 2a4fe87..f019106 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -101,9 +101,7 @@ def _get_definitions(document): field_type = _get_field_type(field) # Get field definition data - if field_type == 'object': - def_data = _get_field_definition_data(field, definitions) - elif field_type == 'array': + if field_type == 'array': def_data = _get_field_definition_data(field.schema.items, definitions) else: def_data = _get_field_definition_data(field, definitions) From 2efbdcc961ec9d379d5942d8a5ad07351297188c Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 07:02:28 -0500 Subject: [PATCH 13/20] adding docstrings --- openapi_codec/encode.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index f019106..d88b53a 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -36,6 +36,10 @@ def generate_swagger_object(document): def _get_or_update_definitions(update_def_data, update_def_name, definitions): + """ + Updates definitions with provided data If definition is not present in map, returns found definition + data in case definition overlaps with existing one. + """ # Check if there's existing definition with same name or props clashing_def_names = filter( @@ -56,7 +60,9 @@ def _get_or_update_definitions(update_def_data, update_def_name, definitions): def _get_field_definition_data(field_item, defs): - + """ + Returns dictionary with field definition data. + """ definition_data = { 'type': 'object', 'properties': {} @@ -92,6 +98,9 @@ def _get_field_definition_data(field_item, defs): def _get_definitions(document): + """ + Returns dictionary with schema definitions. + """ definitions = OrderedDict() links = _get_links(document) @@ -116,6 +125,10 @@ def _get_definitions(document): def _add_tag_prefix(item): + """ + Returns tuple (operation_id, link, tags) with modified operation_id in case of tags. + """ + operation_id, link, tags = item if tags: operation_id = tags[0] + '_' + operation_id @@ -124,7 +137,7 @@ def _add_tag_prefix(item): def _get_links(document): """ - Return a list of (operation_id, link, [tags]) + Return a list of (operation_id, link, [tags]). """ # Extract all the links from the first or second level of the document. links = [] @@ -149,6 +162,9 @@ def _get_links(document): def _get_paths_object(document, definitions): + """ + Returns dictionary with document paths. + """ paths = OrderedDict() links = _get_links(document) @@ -165,6 +181,10 @@ def _get_paths_object(document, definitions): def _get_operation(operation_id, link, tags, definitions): + """ + Returns dictionary with operation parameters. + """ + encoding = get_encoding(link) description = link.description.strip() summary = description.splitlines()[0] if description else None @@ -187,6 +207,10 @@ def _get_operation(operation_id, link, tags, definitions): def _get_field_description(field): + """ + Returns field description. + """ + if getattr(field, 'description', None) is not None: # Deprecated return field.description @@ -198,6 +222,9 @@ def _get_field_description(field): def _get_field_type(field): + """ + Returns field string type by the given field schema. + """ if getattr(field, 'type', None) is not None: # Deprecated return field.type From 44ebcbed69d0f4b794e69a07bb0cd848e1b99aff Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 07:23:42 -0500 Subject: [PATCH 14/20] more sophisticated checks added --- tests/test_encode.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/test_encode.py b/tests/test_encode.py index edc6569..e78d4cd 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -187,3 +187,52 @@ def test_clashing_names(self): lambda d: d.startswith('{}_def_item'.format(self.clashing_name)), self.swagger['definitions'].keys() ) self.assertEqual(len(list(defs)), 2, 'Unexpected definitions count') + + v1_list_params = _get_parameters(self.links['v1']['list'], '', self.definitions) + v2_list_params = _get_parameters(self.links['v2']['list'], '', self.definitions) + + expected_def_name = filter( + lambda d: d.startswith('{}_def_item_'.format(self.clashing_name)), + self.definitions.keys() + )[0] + + expected_v1_list_params = [ + { + 'schema': { + 'required': ['author'], + 'type': 'object', + 'properties': { + 'author': { + '$ref': '#/definitions/author_def_item' + } + } + }, + 'name': 'data', + 'in': 'body' + } + ] + + expected_v2_list_params = [ + { + 'schema': { + 'required': ['author', 'co_authoors'], + 'type': 'object', + 'properties': { + 'co_authoors': { + 'items': { + '$ref': '#/definitions/{}'.format(expected_def_name) + }, + 'type': 'array' + }, + 'author': { + '$ref': '#/definitions/{}'.format(expected_def_name) + } + } + }, + 'name': 'data', + 'in': 'body' + } + ] + + self.assertEqual(v1_list_params, expected_v1_list_params, 'Unexpected definition params') + self.assertEqual(v2_list_params, expected_v2_list_params, 'Unexpected definition params') From 836aa93eb2a58b21dbf5c3e630ee6398927e517c Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 07:26:36 -0500 Subject: [PATCH 15/20] fix typo --- tests/test_encode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index e78d4cd..c2bedda 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -138,7 +138,7 @@ def setUp(self): location='form', schema=bday_schema_obj ) - co_authoors_field = coreapi.Field( + co_authors_field = coreapi.Field( name='co_authoors', required=True, location='form', @@ -158,7 +158,7 @@ def setUp(self): url='/api/v2/songs/', action=u'post', encoding=u'application/json', - fields=[clashing_author_field, co_authoors_field], + fields=[clashing_author_field, co_authors_field], ) self.links = OrderedDict({ From efd75161e856c98f16ee2e97dc17588864105a3a Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 07:30:53 -0500 Subject: [PATCH 16/20] py2.X <-> py3.X --- tests/test_encode.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index c2bedda..a5bf06e 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -194,7 +194,13 @@ def test_clashing_names(self): expected_def_name = filter( lambda d: d.startswith('{}_def_item_'.format(self.clashing_name)), self.definitions.keys() - )[0] + ) + + # NOTE: mind the Python3 + try: + expected_def_name = expected_def_name[0] + except TypeError: + expected_def_name = next(expected_def_name) expected_v1_list_params = [ { From cb565a3de43c5c9b55d7af9eba76117f33691f48 Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 10:31:39 -0500 Subject: [PATCH 17/20] final changes --- openapi_codec/encode.py | 10 ++++------ tests/test_encode.py | 12 ++---------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index d88b53a..bdb1d8f 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -42,17 +42,15 @@ def _get_or_update_definitions(update_def_data, update_def_name, definitions): """ # Check if there's existing definition with same name or props - clashing_def_names = filter( - lambda d: d.startswith(update_def_name) or definitions.get(d) == update_def_data, - definitions.keys() - ) + clashing_def_names = \ + [d for d in definitions.keys() if d.startswith(update_def_name) or definitions.get(d) == update_def_data] for clashing_def_name in clashing_def_names: clash_def_data = definitions.get(clashing_def_name) if clash_def_data == update_def_data: return clash_def_data else: - if clashing_def_names: + if list(clashing_def_names): rand_part = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(5)]) update_def_name = '{}_{}'.format(update_def_name, rand_part) definitions[update_def_name] = update_def_data @@ -282,7 +280,7 @@ def _get_parameters(link, encoding, definitions): definition_data = _get_field_definition_data(field, definitions) definition_data = definition_data.get('properties') - defs = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions) + defs = [d for d in definitions if definitions.get(d).get('properties') == definition_data] if defs: # Note: Python2.X <-> Python3.X diff --git a/tests/test_encode.py b/tests/test_encode.py index a5bf06e..4c68b44 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -191,16 +191,8 @@ def test_clashing_names(self): v1_list_params = _get_parameters(self.links['v1']['list'], '', self.definitions) v2_list_params = _get_parameters(self.links['v2']['list'], '', self.definitions) - expected_def_name = filter( - lambda d: d.startswith('{}_def_item_'.format(self.clashing_name)), - self.definitions.keys() - ) - - # NOTE: mind the Python3 - try: - expected_def_name = expected_def_name[0] - except TypeError: - expected_def_name = next(expected_def_name) + expected_def_name = \ + [d for d in self.definitions.keys() if d.startswith('{}_def_item_'.format(self.clashing_name))][0] expected_v1_list_params = [ { From ca20480bb762ebe5c6f3cd58181ee5091b57dc2f Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 11:31:33 -0500 Subject: [PATCH 18/20] trying to fix flake8 issue --- tests/test_encode.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index 4c68b44..5675c23 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -139,7 +139,7 @@ def setUp(self): schema=bday_schema_obj ) co_authors_field = coreapi.Field( - name='co_authoors', + name='co_authors', required=True, location='form', schema=coreschema.schemas.Array( @@ -200,9 +200,7 @@ def test_clashing_names(self): 'required': ['author'], 'type': 'object', 'properties': { - 'author': { - '$ref': '#/definitions/author_def_item' - } + 'author': {'$ref': '#/definitions/author_def_item'} } }, 'name': 'data', @@ -216,15 +214,11 @@ def test_clashing_names(self): 'required': ['author', 'co_authoors'], 'type': 'object', 'properties': { - 'co_authoors': { - 'items': { - '$ref': '#/definitions/{}'.format(expected_def_name) - }, + 'co_authors': { + 'items': {'$ref': '#/definitions/{}'.format(expected_def_name)}, 'type': 'array' }, - 'author': { - '$ref': '#/definitions/{}'.format(expected_def_name) - } + 'author': {'$ref': '#/definitions/{}'.format(expected_def_name)} } }, 'name': 'data', From 84a1d7b1ebd38a41a8d4940c7455706221c4c79c Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 11:37:46 -0500 Subject: [PATCH 19/20] fixing tests once again --- openapi_codec/encode.py | 2 +- tests/test_encode.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi_codec/encode.py b/openapi_codec/encode.py index bdb1d8f..8a0042c 100644 --- a/openapi_codec/encode.py +++ b/openapi_codec/encode.py @@ -280,7 +280,7 @@ def _get_parameters(link, encoding, definitions): definition_data = _get_field_definition_data(field, definitions) definition_data = definition_data.get('properties') - defs = [d for d in definitions if definitions.get(d).get('properties') == definition_data] + defs = filter(lambda d: definitions.get(d).get('properties') == definition_data, definitions) if defs: # Note: Python2.X <-> Python3.X diff --git a/tests/test_encode.py b/tests/test_encode.py index 5675c23..2d8fe0b 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -211,7 +211,7 @@ def test_clashing_names(self): expected_v2_list_params = [ { 'schema': { - 'required': ['author', 'co_authoors'], + 'required': ['author', 'co_authors'], 'type': 'object', 'properties': { 'co_authors': { From 34d9cd5d9c292f4b18846cf02194a68a062c907d Mon Sep 17 00:00:00 2001 From: Kirill Matyunin Date: Thu, 18 Jan 2018 11:45:21 -0500 Subject: [PATCH 20/20] flake8 fix --- tests/test_encode.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_encode.py b/tests/test_encode.py index 2d8fe0b..1d5c43f 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -197,11 +197,13 @@ def test_clashing_names(self): expected_v1_list_params = [ { 'schema': { - 'required': ['author'], - 'type': 'object', - 'properties': { - 'author': {'$ref': '#/definitions/author_def_item'} - } + 'required': ['author'], + 'type': 'object', + 'properties': { + 'author': { + '$ref': '#/definitions/author_def_item' + } + } }, 'name': 'data', 'in': 'body' pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy