Skip to content

Commit 802fd21

Browse files
committed
external document with partial swagger.json
swagger.json with PathItem and Schema is allowed.
1 parent 1ab2392 commit 802fd21

File tree

9 files changed

+117
-45
lines changed

9 files changed

+117
-45
lines changed

pyswagger/core.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -142,22 +142,25 @@ def _load_json(self, url, getter=None, parser=None):
142142
# get root document to check its swagger version.
143143
obj, _ = six.advance_iterator(getter)
144144
tmp = {'_tmp_': {}}
145-
if not parser:
146-
if utils.get_swagger_version(obj) == '1.2':
147-
# swagger 1.2
148-
with ResourceListContext(tmp, '_tmp_') as ctx:
149-
ctx.parse(getter, obj)
150-
else:
151-
# swagger 2.0
152-
with SwaggerContext(tmp, '_tmp_') as ctx:
153-
ctx.parse(obj)
154-
else:
155-
raise NotImplementedError()
145+
version = utils.get_swagger_version(obj)
146+
if version == '1.2':
147+
# swagger 1.2
148+
with ResourceListContext(tmp, '_tmp_') as ctx:
149+
ctx.parse(getter, obj)
150+
elif version == '2.0':
151+
# swagger 2.0
152+
with SwaggerContext(tmp, '_tmp_') as ctx:
153+
ctx.parse(obj)
154+
elif version == None and parser:
156155
with parser(tmp, '_tmp_') as ctx:
157156
ctx.parse(obj)
158157

158+
version = tmp['_tmp_'].__swagger_version__ if hasattr(tmp['_tmp_'], '__swagger_version__') else version
159+
else:
160+
raise NotImplementedError('Unsupported Swagger Version: {0} from {1}'.format(version, url))
161+
159162
self.__app_cache[url] = weakref.proxy(self) # avoid circular reference
160-
self.__version = utils.get_swagger_version(tmp['_tmp_'])
163+
self.__version = version
161164
self.__raw = tmp['_tmp_']
162165

163166
def _validate(self):
@@ -211,14 +214,15 @@ def _prepare_obj(self, strict=True):
211214
# TODO: partial object would go to this place.
212215
raise NotImplementedError('Unsupported Version: {0}'.format(self.__version))
213216

214-
if self.__root.schemes and len(self.__root.schemes) > 0:
215-
self.__schemes = self.__root.schemes
217+
if hasattr(self.__root, 'schemes') and self.__root.schemes:
218+
if len(self.__root.schemes) > 0:
219+
self.__schemes = self.__root.schemes
216220

217221
s.scan(root=self.__root, route=[Resolve()])
218222
s.scan(root=self.__root, route=[PatchObject()])
219223

220224
@classmethod
221-
def load(kls, url, getter=None, app_cache=None, url_load_hook=None):
225+
def load(kls, url, getter=None, parser=None, app_cache=None, url_load_hook=None):
222226
""" load json as a raw SwaggerApp
223227
224228
:param str url: url of path of Swagger API definition
@@ -233,7 +237,7 @@ def load(kls, url, getter=None, app_cache=None, url_load_hook=None):
233237
url = utils.normalize_url(url)
234238
app = kls(url, app_cache, url_load_hook)
235239

236-
app._load_json(url, getter)
240+
app._load_json(url, getter, parser)
237241

238242
# update schem if any
239243
p = six.moves.urllib.parse.urlparse(url)
@@ -271,7 +275,10 @@ def prepare(self, strict=True):
271275
# 'op' -- shortcut for Operation with tag and operaionId
272276
self.__op = utils.ScopeDict(tr.op)
273277
# 'm' -- shortcut for model in Swagger 1.2
274-
self.__m = utils.ScopeDict(self.__root.definitions)
278+
if hasattr(self.__root, 'definitions'):
279+
self.__m = utils.ScopeDict(self.__root.definitions)
280+
else:
281+
self.__m = utils.ScopeDict({})
275282

276283
# cycle detection
277284
if len(cy.cycles['schema']) > 0 and strict:
@@ -299,7 +306,7 @@ def create(kls, url, strict=True):
299306
"""
300307
_create_ = create
301308

302-
def resolve(self, jref):
309+
def resolve(self, jref, parser=None):
303310
""" reference resolver
304311
305312
:param str jref: a JSON Reference, refer to http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 for details.
@@ -315,7 +322,7 @@ def resolve(self, jref):
315322
if url:
316323
if url not in self.__app_cache:
317324
# This loaded SwaggerApp would be kept in app_cache.
318-
app = SwaggerApp.load(url, app_cache=self.__app_cache, url_load_hook=self.__url_load_hook)
325+
app = SwaggerApp.load(url, parser=parser, app_cache=self.__app_cache, url_load_hook=self.__url_load_hook)
319326
app.prepare()
320327

321328
# nothing but only keeping a strong reference of

pyswagger/scanner/v2_0/patch_obj.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import absolute_import
22
from ...scan import Dispatcher
3-
from ...spec.v2_0.objects import PathItem, Operation, Schema
3+
from ...spec.v2_0.objects import PathItem, Operation, Schema, Swagger
44
from ...spec.v2_0.parser import PathItemContext
55
from ...utils import jp_split, scope_split
66

@@ -18,17 +18,19 @@ class Disp(Dispatcher): pass
1818
def _operation(self, path, obj, app):
1919
"""
2020
"""
21-
# produces/consumes
22-
obj.update_field('produces', app.root.produces if len(obj.produces) == 0 else obj.produces)
23-
obj.update_field('consumes', app.root.consumes if len(obj.consumes) == 0 else obj.consumes)
21+
if isinstance(app.root, Swagger):
22+
# produces/consumes
23+
obj.update_field('produces', app.root.produces if len(obj.produces) == 0 else obj.produces)
24+
obj.update_field('consumes', app.root.consumes if len(obj.consumes) == 0 else obj.consumes)
2425

2526
# combine parameters from PathItem
26-
for p in obj._parent_.parameters:
27-
for pp in obj.parameters:
28-
if p.name == pp.name:
29-
break
30-
else:
31-
obj.parameters.append(p)
27+
if obj._parent_:
28+
for p in obj._parent_.parameters:
29+
for pp in obj.parameters:
30+
if p.name == pp.name:
31+
break
32+
else:
33+
obj.parameters.append(p)
3234

3335
# schemes
3436
obj.update_field('schemes', app.schemes if len(obj.schemes) == 0 else obj.schemes)
@@ -37,7 +39,11 @@ def _operation(self, path, obj, app):
3739
def _path_item(self, path, obj, app):
3840
"""
3941
"""
40-
url = app.root.host + (app.root.basePath or '') + jp_split(path)[-1]
42+
if isinstance(app.root, Swagger):
43+
url = app.root.host + (app.root.basePath or '') + jp_split(path)[-1]
44+
else:
45+
url = None
46+
4147
for c in PathItemContext.__swagger_child__:
4248
o = getattr(obj, c[0])
4349
if isinstance(o, Operation):

pyswagger/scanner/v2_0/resolve.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from __future__ import absolute_import
22
from ...scan import Dispatcher
33
from ...spec.v2_0.parser import (
4+
SchemaContext,
5+
ParameterContext,
6+
ResponseContext,
47
PathItemContext
58
)
69
from ...spec.v2_0.objects import (
@@ -15,12 +18,12 @@
1518
def is_resolved(obj):
1619
return getattr(obj, '$ref') == None or obj.ref_obj != None
1720

18-
def _resolve(obj, app, prefix):
21+
def _resolve(obj, app, prefix, parser):
1922
if is_resolved(obj):
2023
return
2124

2225
r = getattr(obj, '$ref')
23-
ro = app.resolve(normalize_jr(r, prefix))
26+
ro = app.resolve(normalize_jr(r, prefix), parser)
2427

2528
if not ro:
2629
raise ReferenceError('Unable to resolve: {0}'.format(r))
@@ -38,7 +41,7 @@ def _merge(obj, app, prefix, ctx):
3841
cur = obj
3942
to_resolve = []
4043
while not is_resolved(cur):
41-
_resolve(cur, app, prefix)
44+
_resolve(cur, app, prefix, ctx)
4245

4346
to_resolve.append(cur)
4447
cur = cur.ref_obj if cur.ref_obj else cur
@@ -56,15 +59,15 @@ class Disp(Dispatcher): pass
5659

5760
@Disp.register([Schema])
5861
def _schema(self, _, obj, app):
59-
_resolve(obj, app, '#/definitions')
62+
_resolve(obj, app, '#/definitions', SchemaContext)
6063

6164
@Disp.register([Parameter])
6265
def _parameter(self, _, obj, app):
63-
_resolve(obj, app, '#/parameters')
66+
_resolve(obj, app, '#/parameters', ParameterContext)
6467

6568
@Disp.register([Response])
6669
def _response(self, _, obj, app):
67-
_resolve(obj, app, '#/responses')
70+
_resolve(obj, app, '#/responses', ResponseContext)
6871

6972
@Disp.register([PathItem])
7073
def _path_item(self, _, obj, app):
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"get":{
3+
"response":{
4+
"description":"path_item, get, response"
5+
}
6+
},
7+
"put":{
8+
"response":{
9+
"description":"path_item, put, response"
10+
}
11+
}
12+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type":"array",
3+
"items":{
4+
"$ref":"file:///root/swagger.json#/definitions/s3"
5+
}
6+
}

pyswagger/tests/data/v2_0/ex/root/swagger.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
"paths":{
1616
"/full":{
1717
"$ref":"file:///full/swagger.json#/paths/~1user"
18+
},
19+
"/partial":{
20+
"$ref":"file:///partial/path_item/swagger.json"
1821
}
1922
},
2023
"definitions":{
@@ -23,6 +26,15 @@
2326
},
2427
"s2":{
2528
"$ref":"file:///full/swagger.json#/definitions/fs2"
29+
},
30+
"s3":{
31+
"type":"string"
32+
},
33+
"s4":{
34+
"type":"array",
35+
"items":{
36+
"$ref":"file:///partial/schema/swagger.json"
37+
}
2638
}
2739
}
28-
}
40+
}

pyswagger/tests/test_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ def test_jr_split(self):
117117
self.assertEqual(utils.jr_split(
118118
'#'), (
119119
'', '#'))
120+
self.assertEqual(utils.jr_split(
121+
'//'), (
122+
'', '#'))
120123

121124

122125
class WalkTestCase(unittest.TestCase):

pyswagger/tests/v1_2/test_app.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ def test_ref(self):
125125
""" test ref function """
126126
self.assertRaises(ValueError, self.app.resolve, None)
127127
self.assertRaises(ValueError, self.app.resolve, '')
128-
self.assertRaises(ValueError, self.app.resolve, '//')
129128

130129
self.assertTrue(isinstance(self.app.resolve('#/definitions/user!##!User'), Schema))
131130
self.assertTrue(isinstance(self.app.resolve('#/paths/~1api~1user~1{username}/put'), Operation))

pyswagger/tests/v2_0/test_ex.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import unittest
44
import os
55
import six
6-
import weakref
76

87

98
folder = get_test_data_folder(version='2.0', which='ex')
@@ -28,7 +27,7 @@ def setUpClass(kls):
2827
global folder
2928

3029
kls.app = SwaggerApp.load(
31-
url='root',
30+
url='file:///root/swagger.json',
3231
url_load_hook=_hook
3332
)
3433
kls.app.prepare()
@@ -38,13 +37,13 @@ def test_resolve(self):
3837
is the same as resolve with JSON pointer.
3938
"""
4039
p = self.app.resolve('#/paths/~1full')
41-
p_ = self.app.resolve('file:///root#/paths/~1full')
40+
p_ = self.app.resolve('file:///root/swagger.json#/paths/~1full')
4241
# refer to
4342
# http://stackoverflow.com/questions/10246116/python-dereferencing-weakproxy
4443
# for how to dereferencing weakref
4544
self.assertEqual(p.__repr__(), p_.__repr__())
4645

47-
def test_path_item(self):
46+
def test_full_path_item(self):
4847
""" make sure PathItem is correctly merged
4948
"""
5049
p = self.app.resolve('#/paths/~1full')
@@ -57,12 +56,37 @@ def test_path_item(self):
5756
self.assertTrue('default' in another_p.get.responses)
5857
self.assertTrue('404' in another_p.get.responses)
5958

60-
def test_path_item_url(self):
59+
def test_full_path_item_url(self):
6160
""" make sure url is correctly patched
6261
"""
6362
p = self.app.resolve('#/paths/~1full')
6463
self.assertEqual(p.get.url, 'test.com/v1/full')
6564

66-
another_p = self.app.resolve('file:///full/swagger.json#/paths/~1user')
67-
self.assertEqual(another_p.get.url, 'test1.com/v2/user')
65+
original_p = self.app.resolve('file:///full/swagger.json#/paths/~1user')
66+
self.assertEqual(original_p.get.url, 'test1.com/v2/user')
67+
68+
def test_partial_path_item(self):
69+
""" make sure partial swagger.json with PathItem
70+
loaded correctly.
71+
"""
72+
p = self.app.resolve('#/paths/~1partial')
73+
self.assertEqual(p.get.url, 'test.com/v1/partial')
74+
75+
original_p = self.app.resolve('file:///partial/path_item/swagger.json')
76+
self.assertEqual(original_p.get.url, None)
77+
78+
def test_partial_schema(self):
79+
""" make sure partial swagger.json with Schema
80+
loaded correctly.
81+
"""
82+
p = self.app.resolve('#/definitions/s4')
83+
original_p = self.app.resolve('file:///partial/schema/swagger.json')
84+
85+
# refer to
86+
# http://stackoverflow.com/questions/10246116/python-dereferencing-weakproxy
87+
# for how to dereferencing weakref
88+
self.assertEqual(p.items.ref_obj.__repr__(), original_p.__repr__())
89+
90+
p_ = self.app.resolve('#/definitions/s3')
91+
self.assertEqual(p_.__repr__(), original_p.items.ref_obj.__repr__())
6892

0 commit comments

Comments
 (0)