Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
周伟奇
/
bmw-ocr
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
ff1b4142
authored
2020-06-12 10:22:39 +0800
by
Gruel
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
swagger
1 parent
f7cba4a5
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
423 additions
and
14 deletions
.gitignore
src/apps/doc/views.py
src/common/api_doc.py
src/common/management/commands/generate_api_doc.py
src/common/management/commands/validate_api_doc.py
src/common/response.py
src/readme.md
.gitignore
View file @
ff1b414
...
...
@@ -28,6 +28,7 @@ sftp-config.json
*.sqlite3
conf/*
docs/*
# 脚本
src/*.sh
\ No newline at end of file
...
...
src/apps/doc/views.py
View file @
ff1b414
from
django.shortcuts
import
render
from
webargs
import
fields
from
webargs
import
fields
,
validate
from
webargs.djangoparser
import
use_args
,
parser
from
common.mixins
import
GenericView
from
common
import
response
...
...
@@ -14,22 +14,22 @@ def load_data(request, schema):
return
request
.
data
application_data_args
=
{
'applicationId'
:
fields
.
Str
(
required
=
True
)}
application_data_args
=
{
'applicationId'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
64
)
)}
applicant_data_args
=
{
'mainApplicantName'
:
fields
.
Str
(
required
=
True
),
'coApplicantName'
:
fields
.
Str
(
required
=
True
),
'guarantor1Name'
:
fields
.
Str
(
required
=
True
),
'guarantor2Name'
:
fields
.
Str
(
required
=
True
),
'mainApplicantName'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
16
)
),
'coApplicantName'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
16
)
),
'guarantor1Name'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
16
)
),
'guarantor2Name'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
16
)
),
}
document_args
=
{
'documentName'
:
fields
.
Str
(
required
=
True
),
'documentScheme'
:
fields
.
Str
(
required
=
True
),
'businessType'
:
fields
.
Str
(
required
=
True
),
'documentName'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
255
)
),
'documentScheme'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
64
)
),
'businessType'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
64
)
),
'uploadFinishTime'
:
fields
.
DateTime
(
required
=
True
),
'dataSource'
:
fields
.
Str
(
required
=
True
),
'metadataVersionId'
:
fields
.
Str
(
required
=
True
),
'dataSource'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
64
)
),
'metadataVersionId'
:
fields
.
Str
(
required
=
True
,
validate
=
validate
.
Length
(
max
=
64
)
),
}
doc_upload_args
=
{
...
...
@@ -63,3 +63,80 @@ class DocView(GenericView):
)
self
.
running_log
.
info
(
'[doc upload success] [args={0}]'
.
format
(
args
))
return
response
.
ok
()
post
.
openapi_doc
=
'''
summary: pos上传文件信息
tags: [doc]
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
applicationData:
description: 申请信息
type: object
properties:
applicationId:
description: 申请id
type: string
example: CH-B0011010101
applicantData:
description: 申请人信息
type: object
properties:
mainApplicantName:
description: 主申请人
type: string
example: 王明阳
coApplicantName:
description: 共同申请人
type: string
example: 王明月
guarantor1Name:
description: 担保人1
type: string
example: 王明日
guarantor2Name:
description: 担保人2
type: string
example: 王明雨
document:
description: 文件信息
type: object
properties:
documentName:
description: 文件名
type: string
example: CH-B0011010101王明阳申请表
documentScheme:
description: 文件格式?
type: string
example: CO00001
businessType:
description: 业务类型
type: string
example: HIL
uploadFinishTime:
description: 上传完成时间
type: string
example: '2020-09-01 12:21:11'
dataSource:
description: 数据源
type: string
example: POS
metadataVersionId:
description: 元数据版本ID
type: string
example: '8410480'
responses:
200:
description: ok
content:
application/json:
schema:
type: object
$ref: '#/components/schemas/ApiResponse'
'''
...
...
src/common/api_doc.py
0 → 100644
View file @
ff1b414
base_part
=
'''
openapi: 3.0.0
info:
title: 接口文档
description: 宝马ocr/biz_logic接口文档
version: 1.0.0
servers:
- url: 'http://127.0.0.1:8000'
description: Development server
- url: 'http://127.0.0.1:8000'
description: sit
- url: 'http://127.0.0.1:8000'
description: uat
- url: 'http://127.0.0.1:8000'
description: prd
tags:
- name: user
description: 用户
- name: doc
description: 文件
security:
- bearerAuth: []
'''
# scheme: bearer
security_schemes
=
'''
bearerAuth: # arbitrary name for the security scheme
type: http
scheme: bearer
bearerFormat: JWT
description: >
token过期标志
1. Response Headers中的WWW-Authenticate中的status
status: -1 Invalid Authorization header
status: -2 Signature has expired beacause of expire_time
status: -3 Error decoding signature
status: -4 Signature has expired beacause new signature is generated
status: 0 valid Authorization
status: 1 new create, only login would set
2. Response Body中的meta -> status == 1
'''
responses
=
'''
ErrorResponse:
description: 调用异常, 具体情况请参考`HTTP`状态码和`meta -> status`字段
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
NoContent:
description: 后台接收请求,但是没有响应内容
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'''
parameters
=
''
schemas
=
'''
ApiResponse:
description: 响应对象,meta字段用于表示响应的状态; data字段用于存放响应内容
type: object
properties:
code:
type: integer
description: '0: success
1: need login
2: invalid params
3: internal error
4: object not exist
5: async wait
6: no permission
7: illegal operation'
msg:
type: string
nullable: true
data:
oneOf:
- type: object
nullable: true
- type: integer
nullable: true
- type: array
nullable: true
Pagination:
description: 分页参数
type: object
properties:
current:
description: 当前页面
type: integer
total:
description: 所有元素个数
type: integer
page_size:
description: 页面大小
type: integer
required:
- current
- total
- pageSize
'''
\ No newline at end of file
src/common/management/commands/generate_api_doc.py
0 → 100755
View file @
ff1b414
import
collections
import
re
from
django.core.management
import
BaseCommand
from
django.urls.resolvers
import
get_resolver
import
yaml
from
yaml.scanner
import
ScannerError
from
common.api_doc
import
(
base_part
,
security_schemes
,
responses
,
schemas
)
_mapping_tag
=
yaml
.
resolver
.
BaseResolver
.
DEFAULT_MAPPING_TAG
def
dict_representer
(
dumper
,
data
):
return
dumper
.
represent_dict
(
data
.
items
())
def
dict_constructor
(
loader
,
node
):
return
collections
.
OrderedDict
(
loader
.
construct_pairs
(
node
))
yaml
.
add_representer
(
collections
.
OrderedDict
,
dict_representer
)
yaml
.
add_constructor
(
_mapping_tag
,
dict_constructor
)
def
unify_url_path_format
(
string
):
return
'/
%
s'
%
re
.
sub
(
r'
%
\(([^/]+)\)s'
,
lambda
m
:
'{
%
s}'
%
m
.
group
(
1
),
string
)
DEFAULT_API_DOC
=
'''
summary: 未填写
responses:
200:
description: ok
'''
class
Command
(
BaseCommand
):
def
add_arguments
(
self
,
parser
):
parser
.
add_argument
(
'-o'
,
'--output_file'
,
help
=
'文件名,用于存储文档'
)
def
handle
(
self
,
*
args
,
**
kwargs
):
urls
=
get_resolver
()
api_doc_dct
=
{}
for
view
,
pattern
in
urls
.
reverse_dict
.
items
():
view_class
=
view
.
view_class
url_path
,
path_parameters
=
pattern
[
0
][
0
]
url_path
=
unify_url_path_format
(
url_path
)
url_path_paramters
=
getattr
(
view
,
'parameters_doc'
,
None
)
if
url_path_paramters
:
url_path_paramters
=
yaml
.
load
(
url_path_paramters
)
else
:
url_path_paramters
=
[{
'in'
:
'path'
,
'name'
:
parameter
,
'required'
:
True
,
'schema'
:
{
'type'
:
'string'
}
}
for
parameter
in
path_parameters
]
api_doc_dct
[
url_path
]
=
{}
if
url_path_paramters
:
api_doc_dct
[
url_path
][
'parameters'
]
=
url_path_paramters
for
method
in
view_class
.
http_method_names
:
method_handler
=
getattr
(
view_class
,
method
,
None
)
doc
=
getattr
(
method_handler
,
'openapi_doc'
,
None
)
if
not
method_handler
or
(
method
==
'options'
and
not
doc
):
continue
try
:
doc
=
yaml
.
load
(
doc
or
DEFAULT_API_DOC
)
except
ScannerError
as
err
:
raise
Exception
(
'failed to load doc: """
%
s"""
\n
err:
%
s'
%
(
doc
,
err
))
if
doc
.
get
(
'parameters'
):
for
parameter
in
doc
[
'parameters'
]:
if
parameter
[
'name'
]
in
path_parameters
:
doc
[
'parameters'
]
.
pop
(
parameter
)
api_doc_dct
[
url_path
][
method
]
=
doc
doc_dct
=
yaml
.
load
(
base_part
)
doc_dct
[
'paths'
]
=
api_doc_dct
doc_dct
[
'components'
]
=
{
'securitySchemes'
:
yaml
.
load
(
security_schemes
),
'responses'
:
yaml
.
load
(
responses
),
'schemas'
:
yaml
.
load
(
schemas
),
}
doc_str
=
yaml
.
dump
(
doc_dct
,
default_flow_style
=
False
,
allow_unicode
=
True
)
if
kwargs
.
get
(
'output_file'
):
with
open
(
kwargs
[
'output_file'
],
'w'
)
as
f
:
f
.
write
(
doc_str
)
self
.
stdout
.
write
(
self
.
style
.
SUCCESS
(
'api doc generated succssfully:
%
s'
%
kwargs
[
'output_file'
]))
else
:
self
.
stdout
.
write
(
doc_str
)
src/common/management/commands/validate_api_doc.py
0 → 100755
View file @
ff1b414
import
os
import
sys
from
django.core.management
import
BaseCommand
from
openapi_spec_validator
import
validate_spec_url
,
validate_v2_spec_url
from
openapi_spec_validator.exceptions
import
ValidationError
# from openapi_spec_validator/__main__.py
class
Command
(
BaseCommand
):
def
add_arguments
(
self
,
parser
):
parser
.
add_argument
(
'filename'
,
help
=
"Absolute or relative path to file"
)
parser
.
add_argument
(
'--schema'
,
help
=
"OpenAPI schema (default: 3.0.0)"
,
type
=
str
,
choices
=
[
'2.0'
,
'3.0.0'
],
default
=
'3.0.0'
)
def
handle
(
self
,
*
args
,
**
kwargs
):
filename
=
kwargs
[
'filename'
]
filename
=
os
.
path
.
abspath
(
filename
)
# choose the validator
if
kwargs
[
'schema'
]
==
'2.0'
:
validate_url
=
validate_v2_spec_url
elif
kwargs
[
'schema'
]
==
'3.0.0'
:
validate_url
=
validate_spec_url
# validate
try
:
validate_url
(
'file://'
+
filename
)
except
ValidationError
as
err
:
self
.
stdout
.
write
(
self
.
style
.
ERROR
(
err
))
sys
.
exit
(
1
)
except
Exception
as
err
:
self
.
stdout
.
write
(
self
.
style
.
ERROR
(
err
))
sys
.
exit
(
2
)
else
:
self
.
stdout
.
write
(
self
.
style
.
SUCCESS
(
'ok'
))
src/common/response.py
View file @
ff1b414
...
...
@@ -4,9 +4,10 @@ from .named_enum import NamedEnum
def
res_content
(
meta_status
,
msg
,
data
=
None
):
if
data
is
None
:
data
=
{}
return
{
'code'
:
meta_status
,
'message'
:
msg
,
'data'
:
data
}
res
=
{
'code'
:
meta_status
,
'message'
:
msg
}
if
data
is
not
None
:
res
[
'data'
]
=
data
return
res
@enum.unique
...
...
src/readme.md
0 → 100755
View file @
ff1b414
# 录题系统开发规范
## 代码规范
1.
参考
[
python_specification.md
](
python_specification.md
)
2.
帐号、密码严禁提交到git仓中
3.
参与开发前请阅读
[
十二要素应用宣言
](
https://12factor.net/zh_cn/
)
## 接口文档
接口文档使用
`openapi`
规范书写, 可以参考
[
demo
](
http://editor.swagger.io/
)
以获取直观感受。
目前,接口文档写在代码中, 并通过脚本将代码中的文档提取出来, 示例如下。
1.
编写文档
```python
class UserInfoView(APIView):
def get(self, request):
user_info = serializers.staff_detail.dump(request.user).data
return APIResponse.ok(user_info)
get.openapi_doc = '''
summary: 获取个人信息
tags:
- 帐号
responses:
200:
description: ok
content:
application/json:
schema:
type: object
properties:
meta:
$ref: '#/components/schemas/ApiResponseMeta'
data:
$ref: '#/components/schemas/AccountInfoDetail'
'''
```
2.
提取接口文档
```
$: python manage.py generate_api_doc -o ../docs/main.yaml
api doc generated succssfully: ../docs/main.yaml
```
3.
校对接口文档
```
$: python manage.py validate_api_doc ../docs/main.yaml
ok
```
4.
查看接口文档
* 方法1: 粘贴到[online editor](http://editor.swagger.io/)中查看
* 方法2: 安装 https://bitbucket.org/abrahamL/openapi_toolset.git
```
$: cd docs
$: ls
main.yaml
$: python -m openapi_toolset
* Serving Flask app "openapi_toolset" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:9090/ (Press CTRL+C to quit)
```
浏览器访问http://127.0.0.1:9090即可查看文档
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment