Skip to content

Commit 91bf4e3

Browse files
committed
v0.1.2
1 parent a50bed0 commit 91bf4e3

File tree

11 files changed

+1541
-533
lines changed

11 files changed

+1541
-533
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,23 @@ doc.add_api(
170170
from_md="orders.md",
171171
)
172172
```
173+
174+
## 更新说明
175+
176+
#### v0.1.2
177+
178+
- `UriParam` 改为 `PathParam`
179+
- api 中的 `uri` 改为 `url`
180+
- api 中的`uri_params` 改为 `path_params`
181+
- api 中的 `path_params` `query_params` `body_params` 统一为 `params`
182+
- `path_params` `query_params` `body_params` 现在作为 `property` 出现
183+
- template 添加打印按钮,并加 tips ok
184+
- 生成文档地步增加打印按钮
185+
- api 中添加 `make_body_example` 功能,可根据 `body_params` 生成简单示例
186+
- 添加单元测试 `test.py`
187+
- utils 中添加支持 `openapi` 导入功能
188+
- utils 中不再支持 `raml`
189+
190+
`0.1.x` 与前版本 `0.0.x` 不兼容
191+
192+

_trial_temp/_trial_marker

Whitespace-only changes.

eave/eave.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from step import Template
1212

1313

14-
__all__ = ['Doc', 'Note', 'Api', 'Param', 'PathParam', 'QueryParam', 'BodyParam', 'ResponseParam', 'PP', 'QP', 'BP', 'RP', 'readf']
14+
__all__ = ['Doc', 'Note', 'Api', 'Param', 'PathParam', 'QueryParam', 'BodyParam', 'PP', 'QP', 'BP', 'readf']
1515

1616
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
1717

@@ -304,14 +304,9 @@ class BodyParam(Param):
304304
category = 'body'
305305

306306

307-
class ResponseParam(Param):
308-
category = 'response'
309-
310-
311307
PP = PathParam
312308
QP = QueryParam
313309
BP = BodyParam
314-
RP = ResponseParam
315310

316311

317312
def readf(path, encoding='utf8'):

eave/utils.py

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import json
2+
3+
import dnode
24
import requests
35
import os
46

7+
import yaml
8+
59
from eave import *
610

7-
__all__ = ['auto_drf_apis']
11+
__all__ = ['auto_drf_apis', 'doc_from_openapi']
812

913

1014
def _get_request(url, testhost=''):
@@ -207,3 +211,174 @@ def auto_drf_apis(res_name, url, view_set, testhost='http://127.0.0.1:8000'):
207211
return api_list, api_post, api_detail, actions
208212

209213

214+
class DNode(dnode.DNode):
215+
216+
def __getattr__(self, item):
217+
if item not in self.fields:
218+
return None
219+
return super().__getattr__(item)
220+
221+
222+
def doc_from_openapi(s):
223+
"""
224+
[beta]
225+
make eave doc instance from a OpenAPI3 document
226+
s should be any of the following
227+
1. the url of openapi online, such as: http://some.domain.com/openapi.json or http://some.domain.com/openapi.yaml
228+
2. the file path of openapi document, such as: path/to/openapi.json or path/to/openapi.yaml
229+
3. the json string of openapi, such as: '{"openapi": "3.0.2", "paths": {...} }'
230+
4. the yaml string of openapi, such as: 'openapi: 3.0.2\npaths:\n ...'
231+
5. the dict of parsed document, such as: {"openapi": "3.0.2", "paths": {...} }
232+
"""
233+
234+
if isinstance(s, dict):
235+
data = s
236+
else:
237+
assert isinstance(s, str), f's param must be dict or str, not {s}'
238+
239+
if s.startswith('http'):
240+
url = s
241+
s = requests.get(url).text
242+
243+
if os.path.isfile(s):
244+
s = open(s).read()
245+
246+
if s.strip().startswith('{'):
247+
data = json.loads(s)
248+
else:
249+
data = yaml.safe_load(s)
250+
251+
doc = Doc()
252+
root = DNode(data)
253+
254+
info = root.info
255+
if info:
256+
doc.title = info.title or ''
257+
doc.version = info.version or doc.version
258+
doc.description = info.description or ''
259+
260+
servers = root.servers
261+
if servers:
262+
hosts = []
263+
for server in servers:
264+
h = server.url
265+
description = server.description
266+
if description:
267+
h += f'({description})'
268+
hosts.append(h)
269+
doc.host = ', '.join(hosts)
270+
271+
paths = root.paths or {}
272+
for path, operations in paths.items():
273+
# print(path)
274+
for method, operation in operations.items():
275+
operation = DNode(operation)
276+
# print(method)
277+
# print(operation)
278+
279+
api = Api()
280+
api.url = path
281+
api.title = operation.summary or operation.description or operation.operationId
282+
api.description = operation.description
283+
api.method = method.upper()
284+
api.params = []
285+
api.content_types = []
286+
287+
parameters = operation.parameters or []
288+
requestBody = operation.requestBody
289+
responses = operation.responses or {}
290+
291+
for parameter in parameters:
292+
category = parameter.get('in')
293+
name = parameter.name
294+
required = parameter.required or False
295+
description = parameter.description or ''
296+
297+
# todo: support deprecated and allowEmptyValue
298+
deprecated = parameter.deprecated
299+
allowEmptyValue = parameter.allowEmptyValue
300+
301+
type = 'string'
302+
example = ''
303+
schema = parameter.schema
304+
if schema:
305+
type = schema.type or type
306+
example = schema.example or example
307+
308+
# in: "path", "query", "cookie", "header"
309+
p = None
310+
if category == 'path':
311+
p = PathParam()
312+
elif category == 'query':
313+
p = QueryParam()
314+
p.required = required
315+
elif category == 'cookie':
316+
# todo: support cookie parameters
317+
pass
318+
elif category == 'header':
319+
# todo: support header parameters
320+
pass
321+
322+
if p:
323+
p.name = name
324+
p.description = description
325+
p.type = type
326+
p.example = example
327+
p.default = ''
328+
api.params.append(p)
329+
330+
if requestBody and requestBody.content:
331+
param_names = []
332+
# todo: support for $ref
333+
for content_type, value in requestBody.content.items():
334+
content = DNode(value)
335+
api.content_types.append(content_type)
336+
properties = content.schema and content.schema.properties or {}
337+
required_names = content.schema and content.schema.required or []
338+
for param_name, value in properties.items():
339+
if param_name in param_names:
340+
continue
341+
param = DNode(value)
342+
p = BodyParam()
343+
p.name = param_name
344+
p.type = param.type
345+
p.description = param.description or ''
346+
p.required = param_name in required_names
347+
api.params.append(p)
348+
param_names.append(param_name)
349+
350+
api.make_body_example()
351+
352+
codes = list(responses.keys())
353+
if codes:
354+
if '200' in codes:
355+
code = '200'
356+
else:
357+
code = codes[0]
358+
359+
response = responses[code]
360+
361+
if 'content' in response:
362+
# todo: improve real response example
363+
api.response_example = json.dumps(response['content'], ensure_ascii=False, indent=4)
364+
365+
api.response_description = response.get('description')
366+
367+
doc.add_api(api)
368+
369+
# todo: support more
370+
# root.security
371+
# root.externalDocs
372+
# root.components
373+
# root.components.schemas
374+
# root.components.responses
375+
# root.components.parameters
376+
# root.components.examples
377+
# root.components.requestBodies
378+
# root.components.headers
379+
# root.components.securitySchemes
380+
# root.components.links
381+
# root.components.callbacks
382+
383+
# doc.build('openapi.html', 'zh')
384+
return doc

openapi.html

Lines changed: 711 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)