Compare commits

...

222 Commits

Author SHA1 Message Date
shalousun 4e899a831a Merge remote-tracking branch 'origin/dev' into dev 2021-11-21 20:30:41 +08:00
shalousun 228cffc6ea Roll back the fix for #I4IIC4 issue . 2021-11-21 20:30:23 +08:00
cqmike 802acd31b4 fix(template): remove default group when default group not has childen
remove default group when default group not has childen

Closes https://gitee.com/smart-doc-team/smart-doc/issues/I4IIC4
2021-11-21 19:22:03 +08:00
cqmike a4abc7866f Merge remote-tracking branch 'origin/dev' into dev 2021-11-21 17:26:25 +08:00
cqmike 751b26b6cc fix(template): remove default group when default group not has childen
remove default group when default group not has childen

Closes https://gitee.com/smart-doc-team/smart-doc/issues/I4IIC4
2021-11-21 17:25:52 +08:00
shalousun 1ab06ff4d8 update image url 2021-11-20 22:06:21 +08:00
shalousun 1c61a3d52e update image url 2021-11-20 22:02:00 +08:00
shalousun 94b4a92523 add image 2021-11-20 21:58:02 +08:00
cqmike 88add1c32e feat(template): added hideDefaultGroup default value is false
Configurable for default group display, added hideDefaultGroup default
value is false.

Closes https://gitee.com/smart-doc-team/smart-doc/issues/I4IIC4
2021-11-20 17:00:47 +08:00
shalousun ae1738e9b8 Ignore case when searching . 2021-11-19 00:16:02 +08:00
shalousun a2c3b98b87 fix search title error 2021-11-18 23:50:08 +08:00
shalousun d996715bcd replace api 2021-11-18 23:44:18 +08:00
shalousun d82ed6744e Fix the request example error generated when RequestBody is a basic java type . 2021-11-18 23:29:01 +08:00
shalousun d4691dcbf4 update CHANGELOG.md 2021-11-18 00:08:26 +08:00
shalousun c54eab1076 update CHANGELOG.md 2021-11-18 00:06:58 +08:00
shalousun 91b6b87244 Optimize the generic parameter type, when the generic parameter is the basic type, the type is displayed as the basic type . 2021-11-18 00:02:26 +08:00
shalousun d9a630658d Fix document navigation error after using search. 2021-11-17 22:31:55 +08:00
xingzi 8d1dace67e fix openapi formdata list param type error 2021-11-17 11:51:08 +08:00
shalousun 38026fc3c8
Merge pull request #157 from durian530/master
support spring annotation @RequestAttribute
2021-11-16 10:17:47 +08:00
durian530 47c8a51400 support spring annotation @RequestAttribute 2021-11-16 08:30:43 +08:00
shalousun bb3739b4de update README_CN.md 2021-11-14 22:37:05 +08:00
shalousun 95b7c3b9d8 update README_CN.md 2021-11-14 22:34:21 +08:00
shalousun 7b24b37c0a supported controller implements interface. 2021-11-14 22:26:04 +08:00
shalousun a2bd0a041b optimised import 2021-11-14 00:23:46 +08:00
shalousun 849cdcc1c6 upgrade maven-gpg-plugin to 3.0.1 2021-11-14 00:19:57 +08:00
shalousun 6be5a6c747 upgrade junit to 5.8.1 2021-11-14 00:15:18 +08:00
shalousun 66d7ccc8ee fix mock not support enum. 2021-11-13 22:30:27 +08:00
shalousun 302f9b7da7 support set chinese character in header 2021-11-12 17:43:34 +08:00
shalousun 6f1e358c47 Ignore sessionAttribute 2021-11-10 20:06:01 +08:00
shalousun 3daa8333ad Add a limit on the number of recursions to avoid stack overflow when api is converted into a tree . 2021-11-06 21:45:11 +08:00
shalousun d803e4e881 release 2.3.0 2021-11-06 21:10:29 +08:00
shalousun e4e3846512 update README.md 2021-11-04 23:55:33 +08:00
shalousun b791298384 update README.md 2021-11-04 23:49:16 +08:00
shalousun fd0a80c784 update README_CN.md 2021-11-04 23:47:06 +08:00
shalousun 6778a4f6ad update README.md 2021-11-04 23:31:31 +08:00
shalousun 17168b93de update CHANGELOG.md 2021-11-04 19:13:10 +08:00
shalousun 196db481bd fix search.js lost 2021-11-04 19:03:06 +08:00
shalousun e4b7b9e181 ignore RedirectAttributesModelMap. 2021-11-04 14:45:23 +08:00
shalousun 83b3b327ce Optimize the random value generation of some fields. 2021-11-03 13:47:52 +08:00
shalousun ca1048c869 Fix the problem that when multiple packages are configured in packageFilters, the following packages do not take effect. 2021-11-02 23:37:05 +08:00
shalousun 627bd476a3 fix curl cmd error after debug 2021-11-02 20:41:53 +08:00
shalousun 19506d33f0 fix download option set error. 2021-11-02 17:08:54 +08:00
shalousun c123d83c69 release 2.2.9 2021-10-31 15:30:17 +08:00
xingzi f7a460754c Merge remote-tracking branch 'origin/dev' 2021-10-31 15:00:33 +08:00
xingzi d1bdf37d26 Merge remote-tracking branch 'origin/dev'
# Conflicts:
#	src/main/java/com/power/doc/builder/OpenApiBuilder.java
2021-10-31 15:00:16 +08:00
xingzi 4bac204e50 Merge remote-tracking branch 'origin/dev'
# Conflicts:
#	src/main/java/com/power/doc/builder/OpenApiBuilder.java
2021-10-31 13:40:45 +08:00
xingzi b9d108fd4b fix gitee #I4FWFT 2021-10-31 13:39:56 +08:00
shalousun d073d143b6 update README.md 2021-10-30 23:21:08 +08:00
xingzi 63a02559ea fix gitee #I4FWFT 2021-10-30 22:54:09 +08:00
xingzi b2b5a21775 fix github #155;fix list param torna example error 2021-10-30 21:39:25 +08:00
shalousun d65938c63e Fix the bug that the file upload interface pushes to Torna 2021-10-30 17:20:48 +08:00
shalousun 6d993ed2a4 update openapi create error for upload 2021-10-30 15:59:29 +08:00
shalousun 6e3def2f7e
Merge pull request #154 from tangcent/patch-1
chore: fix typo
2021-10-08 22:32:33 +08:00
tangcent a4deb5d901
chore: fix typo 2021-10-08 20:52:16 +08:00
shalousun 0c558c3a27 release 2.2.8 2021-10-07 14:58:02 +08:00
shalousun 96f46feeca fix #I4BSQ6 from gitee 2021-10-07 11:50:13 +08:00
shalousun 4643950d2c compress template 2021-10-06 21:58:54 +08:00
shalousun 77370faeb2 fix link error. 2021-10-06 21:12:40 +08:00
shalousun 92e6f9c4d5 fix link error. 2021-09-28 23:57:55 +08:00
shalousun fe7c6fc62d update README_CN.md . 2021-09-13 20:58:22 +08:00
shalousun 93ff84cdfc release 2.2.7 2021-09-12 23:18:12 +08:00
shalousun ee45b87df2 Fixed #138 . 2021-09-12 16:09:45 +08:00
Forget e2608debe4 fix group error;fix #138 at github 2021-09-09 15:27:04 +08:00
shalousun 835e25c613 Fix css style link error in dubbo document . 2021-09-06 22:33:29 +08:00
shalousun 1af03b725a release 2.2.6 2021-09-04 23:50:34 +08:00
shalousun 445ac56958 press html document templates. 2021-09-04 23:30:11 +08:00
shalousun 20a6b14e5d Fix link error in html document . 2021-09-04 16:26:32 +08:00
shalousun f26abd5ad2 Fix the bug that the deprecated mark disappears after searching for item . 2021-09-04 14:55:00 +08:00
shalousun 79abbcd876 Fix item error while clean search in html document 2021-09-04 01:28:43 +08:00
shalousun 7f16381e7c Fix style background error 2021-09-04 01:15:37 +08:00
shalousun 1198e25e4c format html 2021-09-03 21:58:10 +08:00
xzli15 210b748e99 Merge branch 'dev' of https://github.com/smart-doc-group/smart-doc 2021-09-03 09:11:26 +08:00
shalousun e64cfdcbda enhance pull #152 . 2021-09-02 23:12:14 +08:00
shalousun 2eedb8fa4b Merge branch 'master' into dev 2021-09-02 21:58:29 +08:00
shalousun 08d085de96
Merge pull request #152 from 290966751/patch-1
The parent class declares generics
2021-09-02 21:52:45 +08:00
xzli15 b8094d0ad4 fix:'replace', 'style' parammeter invalid 2021-09-01 16:53:20 +08:00
yao.hq fe3765f1cb
The parent class declares generics
example:
    class A extends B <C> {}
    class B<T> {
        List<T> lists;
    }
    class C {
        String name;
    }
2021-09-01 13:42:47 +08:00
shalousun 04a253b3d1
Merge pull request #150 from chenqi146/dev
[fix] 🐛 fix group search in html
2021-08-31 09:52:41 +08:00
chenqi 40d99634df [fix] 🐛 fix group search in html 2021-08-30 23:16:17 +08:00
shalousun 47e132bc2d Fixed #147 . 2021-08-29 22:54:40 +08:00
shalousun 390b94b70b Add Jaxrs 2021-08-29 15:41:59 +08:00
shalousun ea3d13a9b5 Add options 2021-08-28 18:48:50 +08:00
cqmike e8f52eefa0
Update README_CN.md 2021-08-28 16:41:13 +08:00
cqmike 932d9a6aaf
Update README.md
add groups config
2021-08-28 16:39:33 +08:00
shalousun a433860946 Release 2.2.5 2021-08-28 12:45:47 +08:00
shalousun c6db6a8f4d Fixed out of bounds error. 2021-08-28 00:54:56 +08:00
shalousun b59a6007d5 fixed #https://github.com/smart-doc-group/smart-doc/issues/139 2021-08-28 00:04:39 +08:00
shalousun d1deef32f4 supported #https://github.com/smart-doc-group/smart-doc/issues/140 2021-08-27 23:02:57 +08:00
xzli15 686ad49e32 fix can't load static resource 2021-08-27 10:32:49 +08:00
xzli15 d896d18770 change js and css to static resource 2021-08-27 09:25:17 +08:00
shalousun 115486f30e debug-all.html supports group directories 2021-08-27 00:31:02 +08:00
shalousun 5359af7dbc AllInOne.html supports group directories 2021-08-27 00:18:13 +08:00
shalousun 68d04dadf3 Revert AllInOne.html 2021-08-26 23:05:29 +08:00
xzli15 5c7166d046 change js and css to static resource 2021-08-26 19:45:12 +08:00
xzli15 9b449ab7e0 fix when @mock mark at list or map param, analysis breaked 2021-08-24 20:37:16 +08:00
shalousun 86ade55b84
Merge pull request #143 from chenqi146/dev
fix: fix group in smart-doc.json
2021-08-23 23:26:46 +08:00
chenqi 1b7661c6db Merge remote-tracking branch 'upstream/dev' into dev 2021-08-23 23:12:30 +08:00
chenqi df53344b3f Merge remote-tracking branch 'origin/dev' into dev 2021-08-23 23:11:31 +08:00
chenqi be9d40ecf0 fix: fix null exception to group in smart-doc.json 2021-08-23 23:11:07 +08:00
shalousun 599ec18890 Fix the error when Boolean type fields start with is 2021-08-18 23:29:25 +08:00
shalousun 76be2eb854
Merge pull request #136 from chenqi146/dev
feat: add group and tag
2021-08-18 23:09:04 +08:00
shalousun cf6452c5d1
Merge branch 'dev' into dev 2021-08-18 23:08:41 +08:00
shalousun a50b3a01cf release 2.2.4 2021-08-08 21:49:46 +08:00
chenqi 1330e887c8 merge 2021-08-07 18:15:32 +08:00
xzli15 31fbc38394 fix gitee #I43JQR #I43555 add isReplace property 2021-08-07 14:45:08 +08:00
shalousun bfb9fe054a update CHANGELOG.md 2021-08-04 20:58:03 +08:00
shalousun 8c030e1418 update README_CN.md 2021-08-04 20:45:48 +08:00
chenqi 388c178662 feat: add group 2021-08-01 23:19:39 +08:00
xingzi d024ea9ac5 新增@size @length支持;修改torna推送字典数据重复问题 2021-08-01 20:06:57 +08:00
chenqi 44397612c4 feat: add custom tag of @tag to implements tag 2021-07-31 15:40:43 +08:00
chenqi 77f315ca9c Merge remote-tracking branch 'upstream/dev' into dev 2021-07-31 07:11:21 +08:00
shalousun af46ba63d8 format code 2021-07-24 15:26:11 +08:00
chenqi 70171b7bb7 Merge remote-tracking branch 'upstream/dev' into dev 2021-07-24 14:43:16 +08:00
shalousun c783e3d1ed Add RemoveLineBreaks function of beetl 2021-07-24 01:16:27 +08:00
shalousun c563680c8a Add RemoveLineBreaks function of beetl 2021-07-24 01:04:29 +08:00
shalousun 27d77cd0a0 format code 2021-07-24 00:42:29 +08:00
shalousun 9d04d2552b Merge branch 'master' into dev 2021-07-24 00:21:57 +08:00
shalousun 65616c797a remove blank 2021-07-24 00:20:32 +08:00
shalousun 58c45db77c
Merge pull request #134 from daisy-yangyang/test-pr#91
将回到顶部替换为top
2021-07-23 23:24:25 +08:00
Daisy 0eb6f620ea 将回到顶部替换为top 2021-07-21 16:26:05 +08:00
song_my 7c8cf1f96c Fix gitee #I41G2E 2021-07-20 12:56:15 +08:00
chenqi 86f4e43ba9 Merge remote-tracking branch 'upstream/dev' into dev 2021-07-18 20:44:49 +08:00
shalousun 273170f7db release 2.2.3 2021-07-18 18:51:02 +08:00
shalousun 1347e13940 release 2.2.3 2021-07-18 18:47:17 +08:00
shalousun 31cd6112ed update README.md and README_CN.md 2021-07-18 18:44:20 +08:00
Forget 11c775a2b0 修改apidocTest配置 修复@mock 标记的map时未推送到torna问题 2021-07-18 15:37:14 +08:00
Forget 30999e0489 修改apidocTest配置 2021-07-18 15:37:14 +08:00
shalousun 97a0c5ef05 update README.md and README_CN.md 2021-07-17 23:38:56 +08:00
shalousun 1b5b3a84b9
Merge pull request #133 from chenqi146/dev
feat: add requestParams for git #132
2021-07-17 21:30:58 +08:00
chenqi 3dee302e76 feat: add requestParams for git #132 2021-07-17 20:39:38 +08:00
chenqi 47d6a96509 feat: add requestParams for git #132 2021-07-17 18:11:43 +08:00
shalousun d6d23f37e9 update readme 2021-07-17 12:09:35 +08:00
shalousun 6f3c4e2f06 Fixed #131 . 2021-07-17 11:59:14 +08:00
shalousun 0874de6e6c Optimised responseAdvice 2021-07-17 00:57:31 +08:00
shalousun 364a8204d8 Fixed #https://gitee.com/smart-doc-team/smart-doc/issues/I40ZGE 2021-07-16 23:10:37 +08:00
shalousun f9359ac14d Fixed #https://gitee.com/smart-doc-team/smart-doc/issues/I40RDV 2021-07-16 22:41:21 +08:00
shalousun bba038ef00 update issue templates 2021-07-16 12:42:12 +08:00
shalousun 980b1d4170 Update issue templates 2021-07-16 12:36:43 +08:00
shalousun 05019d9782 Update issue templates 2021-07-16 12:35:14 +08:00
shalousun ce9ba20063
Create issue_template.md 2021-07-16 12:28:21 +08:00
shalousun 91d1637dca compress css 2021-07-14 01:02:28 +08:00
shalousun dda70ab3a2 compress html template 2021-07-14 01:01:19 +08:00
shalousun fcd9594aca compress AllInOne.html 2021-07-14 00:16:51 +08:00
shalousun 2f152f4f27 compress debug-all.html 2021-07-13 23:59:51 +08:00
shalousun 7d05b3e956 compress debug-all.html 2021-07-13 23:47:14 +08:00
shalousun 69724ace31
Merge pull request #126 from daisy-yangyang/test-pr#91
解决 #91 问题
2021-07-13 22:38:52 +08:00
shalousun 49b67e35b1 Supported FeignClient path #130. 2021-07-13 22:32:56 +08:00
shalousun 16e50651a0 Fixed parsing error when enum class is defined as inner class #https://gitee.com/smart-doc-team/smart-doc/issues/I3YOBY . 2021-07-12 16:51:13 +08:00
Daisy 10cdc1e0d7 resolve-#91 2021-07-12 09:10:35 +08:00
shalousun fb215cc74b Supported user constants in header setting, #125 . 2021-07-12 00:12:38 +08:00
shalousun 64d1dc8529 Supported user constants in header setting, # 125 . 2021-07-11 21:11:09 +08:00
shalousun 4cc4c8e00a Support use FastJson JSONType annotation and Jackson JsonIgnoreProperties annotation to ignore fields. #https://gitee.com/smart-doc-team/smart-doc/issues/I401KH . 2021-07-11 18:34:45 +08:00
shalousun 79e1b1d176 update jetbrains logo set 2021-07-10 10:42:48 +08:00
shalousun f72021d48b update jetbrains logo set 2021-07-10 10:40:30 +08:00
shalousun 7aa678c5da Add jetbrains logo 2021-07-10 10:33:57 +08:00
shalousun 3756a97e24 Add shunfeng logo 2021-07-10 00:05:16 +08:00
shalousun 72e1dd7bd0 Update logo url 2021-07-10 00:00:04 +08:00
shalousun 01a45f9fa0 Add known User 2021-07-09 23:55:52 +08:00
shalousun d9bdf58d95 Add pathPrefix to config context-path 2021-07-09 23:26:13 +08:00
shalousun 21cbef4b82 Merge branch 'master' into dev 2021-07-09 21:28:47 +08:00
shalousun 6d8182dd08
Merge pull request #129 from Taogang00/master
feat:添加支持Path前缀
2021-07-09 21:27:23 +08:00
shalousun c52ffb8dd7 update README_CN.md 2021-07-09 21:21:18 +08:00
taogang d7a5faa6eb feat:添加支持Path前缀 2021-07-09 17:26:25 +08:00
Daisy ae77ba4106 test-pr#91 2021-07-08 19:26:51 +08:00
Daisy 158fbf1d16 test-pr#91 2021-07-07 13:56:25 +08:00
shalousun 561c552c94 update CHANGELOG.md for 2.2.2 2021-07-04 21:32:01 +08:00
shalousun f925694fb3 update CHANGELOG.md for 2.2.2 2021-07-04 21:28:15 +08:00
shalousun 8e9c91e101 Format code 2021-07-04 20:36:38 +08:00
shalousun b7a42bcb8d Enhance packageFilters config #I3YKZ4 2021-07-04 20:35:38 +08:00
xingzi 129b603eff 修改推送torna dubbo文档时参数显示异常;commonRequest/Responsefiled配置 当参数相同类不同覆盖问题 2021-07-04 17:40:23 +08:00
shalousun 7415c19c90 Support replace Jpa Pageable 2021-07-03 00:36:00 +08:00
shalousun 8e2b86b39d Add PageableAsQueryParam 2021-06-30 23:38:09 +08:00
shalousun b1afdea45e ignore HttpServlet 2021-06-30 23:07:23 +08:00
shalousun 5c79eeb4e2 Fixed #119 . 2021-06-29 22:54:11 +08:00
shalousun 4c45a17f65 update CHANGELOG.md 2021-06-28 21:58:09 +08:00
shalousun 5f67a3d727 Fix gitee #I3Y640 2021-06-28 21:57:31 +08:00
shalousun 69e6859cfc Add Framework extend support 2021-06-28 21:54:59 +08:00
shalousun c59d58134a Add Framework template create factory 2021-06-27 23:59:07 +08:00
shalousun 9b9451e41f Update Path match 2021-06-27 22:55:21 +08:00
shalousun 777eab2fcd Update README_CN.md 2021-06-27 01:14:49 +08:00
shalousun b50f84f142 Fix gitee #I3XO31 2021-06-26 22:05:38 +08:00
songhaozhi 489cd11a22 fix:gitee #I3XF0L 2021-06-26 18:45:22 +08:00
shalousun 7019620c01 Optimised JsonUtil 2021-06-26 16:15:38 +08:00
shalousun 01cd6f18d3 Optimised JsonBuildHelper 2021-06-26 16:06:46 +08:00
shalousun ee230dcd62 Optimised JsonUtil 2021-06-26 15:49:09 +08:00
songhaozhi 3ba32a65f2 Merge branch 'dev' of https://github.com/smart-doc-group/smart-doc into dev 2021-06-26 15:45:13 +08:00
songhaozhi 84c9af7a1f fix:gitee #I3W9DY 2021-06-26 15:38:07 +08:00
shalousun 3f63bf3310 Merge remote-tracking branch 'origin/dev' into dev 2021-06-26 12:27:31 +08:00
shalousun 219f16c5f5 Fix gitee #I3XSE5 2021-06-26 12:27:12 +08:00
songhaozhi 021c5253cd Merge remote-tracking branch 'origin/dev' into dev 2021-06-26 12:06:30 +08:00
songhaozhi 7064028106 fix:gitee I3W9DY 2021-06-26 11:54:06 +08:00
shalousun 86fc20031c add logo 2021-06-26 11:03:27 +08:00
shalousun 4290104f5c add logo 2021-06-26 10:44:43 +08:00
shalousun 5a147bac10 release 2.2.1 2021-06-20 22:31:31 +08:00
shalousun 83c492bce1 remove system.out print 2021-06-20 10:58:32 +08:00
shalousun 63107fd075 remove YapiJsonBuilder.java 2021-06-20 09:50:08 +08:00
shalousun 118fd81080 release 2.2.0 2021-06-20 09:42:18 +08:00
shalousun 4972dfb4fb update CHANGELOG.md 2021-06-20 00:28:08 +08:00
shalousun c815f12d06 support Character type in java 2021-06-20 00:26:51 +08:00
Forget ed273cebf4 修改changelog 2021-06-17 23:28:49 +08:00
shalousun 3c5330e8f3 support set response example 2021-06-15 23:11:16 +08:00
shalousun 5f0e156890 fix null point error,while CustomResponseField's owner class is not set. 2021-06-15 17:39:23 +08:00
xingzi 8932f145c8 Merge branch 'dev' of https://github.com/smart-doc-group/smart-doc 2021-06-06 18:46:03 +08:00
xingzi 764584d479 修改@Max解析错误 2021-06-06 18:24:08 +08:00
shalousun 18c28ed965 fix gitee #I3TYYP 2021-06-03 23:02:57 +08:00
shalousun 357a3e2be8 fix gitee #I3TLGU 2021-05-31 18:00:23 +08:00
shalousun 8850c08c70 release 2.1.9 2021-05-30 21:17:26 +08:00
shalousun 761edaf434 Merge branch 'master' into dev 2021-05-30 21:05:18 +08:00
shalousun 0a9f6223b1 Fix Null point. 2021-05-30 21:04:03 +08:00
xingzi e4c1863a66 修改-当无字典时不推送字典数据 2021-05-30 18:00:51 +08:00
shalousun 2eb97081d2 Fix the bug that ignore tag does not take effect 2021-05-30 15:09:43 +08:00
shalousun d837bfd77f fix gitee #I3BTFY . Analyze the LocalDateTime type field error in the parent class 2021-05-29 22:58:03 +08:00
Forget 37bcd3ff7c 新增torna排序 2021-05-29 14:31:11 +08:00
shalousun c9441c6469 fix gitee #I3T6UV . 2021-05-29 13:12:06 +08:00
shalousun 05f1432cc3 support auto mark file download 2021-05-29 01:27:08 +08:00
shalousun e67e46427e Change css cdn. 2021-05-28 00:03:12 +08:00
shalousun 5933e2ff17 fix value error in array field 2021-05-23 12:31:08 +08:00
shalousun 3597732de8 release 2.1.8 2021-05-22 22:43:36 +08:00
Forget 660eb38042 修改checkController方法 报错问题 2021-05-22 22:22:40 +08:00
shalousun e4ba5ec4ae fix null point while check controller 2021-05-22 20:09:25 +08:00
shalousun 9d957ef62e fix ApiDataBuilder Repeat call 2021-05-22 17:14:05 +08:00
shalousun e6069fc911 fix formdata error 2021-05-22 00:33:50 +08:00
111 changed files with 6135 additions and 2282 deletions

View File

@ -1,43 +1,37 @@
---
name: Bug report 🐞 about: Create a report to help us improve title: ''
labels: bug assignees: ''
name: Bug report 🐞
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Your Environment(您的使用环境)
<!--- Include as many relevant details about the environment you experienced the bug in -->
* smart-doc version:
* plugin version (e.g. smart-doc-maven-plugin or smart-doc-gradle-plugin):
* build tool version(maven or gradle):
## Expected Behavior(您期望的结果)
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior(当前结果)
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution(bug解决建议)
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Steps to Reproduce (Bug产生步骤请尽量提供用例代码)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
## Context(Bug影响描述)
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->

View File

@ -1,5 +1,7 @@
---
name: Feature Request 💡 about: Suggest a new idea for the project. labels: enhancement
name: Feature Request 💡
about: Suggest a new idea for the project.
labels: enhancement
---
## Summary(关于这个pr的描述)

View File

@ -1,8 +1,140 @@
## smart-doc版本
#### 版本号2.3.2
- 更新日期: 2021-11-21
- 更新内容:
1. 修复debug调试页搜索后目录锚点错误。
2. 修复生成openapi时List<String>等基本类型数组入参时类型转换错误。
3. 优化泛型类型显示,当泛型类型的实参类型是基本类型时直接显示为基本类型。
4. 新增对@RequestAttribute参数的忽略。
5. 修复@RequstBody使用基本类型时的请求参数示例错误
#### 版本号2.3.1
- 更新日期: 2021-11-13
- 更新内容:
1. 修复debug调试不支持请求头设置中文值的bug。
2. 修复response自定义tag设置返回未格式化换行问题。
3. 修复枚举类型字段指定mock不生效的问题。
4. 新增对@SessionAttribute参数做忽略。
5. 支持controller实现接口并使用default方法。
#### 版本号2.3.0
- 更新日期: 2021-11-07
- 更新内容:
1. 修复创建html文档丢失search.js文件问题。
2. 修复packageFilters配置多个包时只有第一个生效的问题。
3. 修复debug调试页面的curl指令错误。
4. 修复设置了下载文件接口加download标记后数据并未生效的bug。
5. 优化一些部分常见字段的随机值生成。
#### 版本号2.2.9
- 更新日期: 2021-10-31
- 更新内容:
1. 修复生成openapi文件上传错误问题。
2. 修复文件上传接口推送到torna参数被放置到query参数列表的问题。
3. 修改List<T>类型参数推送到torna错误的问题
4. 优化outPath配置只使用torna推送时可以不再要求配置outPath项。
#### 版本号2.2.8
- 更新日期: 2021-10-07
- 更新内容:
1. 修复html文档无接口注释时锚点跳转错误的问题。
2. 修复导出postman时服务端口配置成变量报错的问题。
#### 版本号2.2.7
- 更新日期: 2021-09-12
- 更新内容:
1. 修复dubbo文档css连接错误。
2. 修复分组后组归错误。
3. 修复路径常量相似度时替换错误的bug。
4. JSR303分组优化标记Null的分组字段将不再显示在文档中 。
#### 版本号2.2.6
- 更新日期: 2021-09-05
- 更新内容:
1. 修复html文档静态资源链接错误。
2. 不配置分组时不显示分组。
3. 修复分组后目录item搜索错误。
4. 优化maven插件提示 。
#### 版本号2.2.5
- 更新日期: 2021-08-08
- 更新内容:
1. 支持在html文档中不显示参数列表。
2. html文档使用的资源全部改成本地引用。
3. 修复Boolean类型字段命名为is前缀时is前缀被去除的bug。
4. 修复配置了字典码列表后枚举字段注释显示错误#139 。
5. 修复分析过程中出现的数组越界异常 。
6. 新增接口分组支持。
#### 版本号2.2.4
- 更新日期: 2021-08-08
- 更新内容:
1. 修复字典码推送torna错误 #https://gitee.com/smart-doc-team/smart-doc/issues/I43JQR。
2. 新增jsr303 @size和@length支持
3. 修改html的模板样式错误。
4. 修复postman错误#I41G2E 。
5. 新增isReplace配置 。
6. 修复当存在多个jsr注解时部分注解失效问题。
#### 版本号2.2.3
- 更新日期: 2021-07-18
- 更新内容:
1. 增加pathPrefix配置项用于配置上下文引入该配置项后serverUrl仅用于配置服务器地址。
2. 支持请求头常量设置解析。
3. 支持使用JsonIgnoreProperties和JSONType注解去忽略多字段。
4. 修改部分文档设置allInOneDocFileName无效的问题,#131 。
5. 修复dubbo rpc文档模板格式错误 #https://gitee.com/smart-doc-team/smart-doc/issues/I40ZGE .
6. 支持配置添加拦截器中设置全局请求参数,#https://github.com/smart-doc-group/smart-doc/issues/132 .
7. 修复部分类型mock未推送到torna的问题。
#### 版本号2.2.2
- 更新日期: 2021-07-04
- 更新内容:
1. 修复url中遇到正则表达解析错误问题.
2. 修复生成的json样例部分格式化后错误的bug,gitee #I3XSE5
3. 增强对文档中html特殊字符的处理防止html渲染后显示错误gitee #I3XO31
4. 请求头设置增强支持配置urlPatterns和excludePathPatterns两个属性去匹配对应的接口。
5. 优化了maven插件的提示优化后可以将加载了那些目录模块代码路径打印。
6. 提供了其它框架扩展文档解析的能力。
7. 修复doc模板错误gitee #I3Y640
8. 修复字典模板错误,#119。
9. 添加忽略HttpServlet对象。
10. 支持内置替换Jpa Pageable分页对象去除不必要的请求参数替换配置。
11. packageFilters增强可使用正则进行匹配gitee #I3YKZ4
12. 修复dubbo rpc文档推送到torna数据错误。
13. 修复customResponseFields和customRequestFields设置时不同类同名字段覆盖buggitee #I3Y6AL
14. 修复高版本gradle中使用implements添加依赖时gradle插件无法加载依赖导致返回空json的bug。
#### 版本号2.2.1
- 更新日期: 2021-06-20
- 更新内容:
1. 移除代码中System.out.print打印.
#### 版本号2.2.0
- 更新日期: 2021-06-20
- 更新内容:
1. 修复参数多行注释时注释提取错误gitee #I3TYYP.
2. 修复部分代码可能出现的空指针问题。
3. 添加@response tag。支持自己设置response example
4. 修复推送到torna请求或返回为数组时示例显示错误
5. Character类型解析支持。
6. 修复使用Quartz中JobDataMap类型解析错误。
7. 移除YapiBuilder。smart-doc不在支持其他第三方接口系统请使用torna.
#### 版本号2.1.9
- 更新日期: 2021-05-29
- 更新内容:
1. 修复inlineEnum为false时枚举展示在参数中的问题。
2. 返回Spring文件下载对象支持自动识别为文件下载减少手动标记@download tag。
3. smart-doc使用的css cdn更换默认使用国内cdn提升国内的加载速度切换英文环境使用google的cdn.
4. 添加多层泛型嵌套的解析支持。gitee #I3T6UV .
5. 修复父类是泛型时父类中LocalDateTime类型字段生成json样例错误。
6. 添加将接口排序order推送到torna中。
7. 修复类上的@ignore tag不生效bug.
8. 优化字典码推送空字典码不会像torna发起推送请求。
#### 版本号2.1.8
- 更新日期: 2020-05-17
- 更新日期: 2021-05-22
- 更新内容:
1. 修复推送接口到torna丢失部分mock值的问题。
2. 修复在参数注释中配置类替换时将非类名解析成类名的bug 。
@ -11,17 +143,17 @@
5. 修复maven插件torna-rest和torna-rpc两个task未加编译前缀的问题。
6. 修复生成json用例中数组类型json错误的问题。
7. 修复customRequestFields中设置字段value在用例中不生效的bug。
8. 添加@JsonProperty支持,支持JsonProperty.Access控制字段。
#### 版本号2.1.7
- 更新日期: 2020-05-12
- 更新日期: 2021-05-12
- 更新内容:
1. 添加推送接口作者信息到torna数据错误的bug。
2. 修复空参数curl命令多余号问题github 。
#### 版本号2.1.6
- 更新日期: 2020-05-10
- 更新日期: 2021-05-10
- 更新内容:
1. 修复不允许List中放文件上传对象错误的bug。
2. 添加推送接口作者信息到torna,通过配置author设置推送人不配置默认为计算机用户名。
@ -29,14 +161,14 @@
#### 版本号2.1.5
- 更新日期: 2020-05-05
- 更新日期: 2021-05-05
- 更新内容:
1. 修复requestBodyAdvice请求样例丢之。
2. 添加dubbo文档到torna的推送。
#### 版本号2.1.4
- 更新日期: 2020-04-24
- 更新日期: 2021-04-24
- 更新内容:
1. 修复Controller继承时父类的Mapping未继承的问题。
2. 修复配置responseBodyAdvice后controller中void方法返回显示错误。
@ -48,7 +180,7 @@
#### 版本号2.1.3
- 更新日期: 2020-04-11
- 更新日期: 2021-04-11
- 更新内容:
1. 增强对文件上传的支持。
2. 增加customRequestFields配置项#97。
@ -58,21 +190,21 @@
#### 版本号2.1.2
- 更新日期: 2020-03-29
- 更新日期: 2021-03-29
- 更新内容:
1. 修复Map嵌套在某些结构体中栈溢出问题gitee #I3CCLY
2. 修复Torna数据推送问题。
#### 版本号2.1.1
- 更新日期: 2020-03-24
- 更新日期: 2021-03-24
- 更新内容:
1. 修复Map嵌套在某些结构体中栈溢出问题gitee #I3CCLY
2. 修复Torna数据推送问题。
#### 版本号2.1.0
- 更新日期: 2020-03-21
- 更新日期: 2021-03-21
- 更新内容:
1. 导出的postman的url资源下添加缺失的protocol。
2. 增加@ignoreParams自定义tag来过滤掉不想显示在文档中参数。
@ -82,7 +214,7 @@
#### 版本号2.0.9
- 更新日期: 2020-03-12
- 更新日期: 2021-03-12
- 更新内容:
1. 支持UUID和ZonedDateTime字段类型#89。
2. 对map参数增加开关来兼容旧项目还是不建议使用map参数。
@ -90,7 +222,7 @@
#### 版本号2.0.8
- 更新日期: 2020-02-26
- 更新日期: 2021-02-26
- 更新内容:
1. 修复文件上传的参数丢失的注释。
2. 修复2.0.7新增忽略接口方法后解析父类字段缺失注释bug。
@ -98,7 +230,7 @@
#### 版本号2.0.7
- 更新日期: 2020-01-30
- 更新日期: 2021-01-30
- 更新内容:
1. 修复postman的url中不附加的context-path的问题。
2. 修复带正则的path路径参数解析出现截取越界的问题。
@ -108,21 +240,21 @@
#### 版本号2.0.6
- 更新日期: 2020-01-15
- 更新日期: 2021-01-15
- 更新内容:
1. 修复带正则的path路径参数在postman中用例问题。
2. 增强对祖传不良代码的分析兼容。
#### 版本号2.0.5
- 更新日期: 2020-01-09
- 更新日期: 2021-01-09
- 更新内容:
1. 修复集合类无泛型参数作为入参出参时的数组越界。
2. 修复新开tab访问的url拼接问题。
#### 版本号2.0.3-2.0.4
- 更新日期: 2020-01-01
- 更新日期: 2021-01-01
- 更新内容:
1. 修改页面的错误列表标题显示。
2. 修改debug页面curl header语法错误。
@ -131,7 +263,7 @@
#### 版本号2.0.2
- 更新日期: 2020-02-27
- 更新日期: 2020-12-27
- 更新内容:
1. 修改创建openapi时的空指针异常。
2. 修改debug页面时未使用mock值的问题。

266
README.md
View File

@ -5,7 +5,7 @@
![number of issues closed](https://img.shields.io/github/issues-closed-raw/shalousun/smart-doc)
![closed pull requests](https://img.shields.io/github/issues-pr-closed/shalousun/smart-doc)
![java version](https://img.shields.io/badge/JAVA-1.8+-green.svg)
[![chinese](https://img.shields.io/badge/chinese-中文文档-brightgreen)](https://github.com/shalousun/smart-doc/blob/master/README_CN.md)
[![chinese](https://img.shields.io/badge/chinese-中文文档-brightgreen)](https://smart-doc-group.github.io/#/zh-cn/)
## Introduce
@ -14,6 +14,7 @@ based on interface source code analysis to generate interface documents, and zer
write Javadoc comments when developing, smart-doc can help you generate Markdown or HTML5 document. smart-doc does not
need to inject annotations into the code like Swagger.
[quick start](https://smart-doc-group.github.io/#/)
## Features
- Zero annotation, zero learning cost, only need to write standard JAVA document comments.
@ -36,252 +37,7 @@ smart-doc + [Torna](http://torna.cn) form an industry-leading document generatio
smart-doc to complete Java source code analysis and extract annotations to generate API documents without intrusion, and
automatically push the documents to the Torna enterprise-level interface document management platform.
![smart-doc+torna](https://raw.githubusercontent.com/shalousun/smart-doc/master/images/smart-doc-torna.png)
## Getting Started
[Smart-doc Samples](https://github.com/shalousun/smart-doc-demo.git)。
```
# git clone https://github.com/shalousun/smart-doc-demo.git
```
This example already provides a static html document generated in advance. You can start the Spring Boot project and
then go directly to `http://localhost:8080/doc/api.html` to view the interface documentation generated by smart-doc. Of
course, you can also browse `http://localhost:8080/doc/api.html`, which looks a html like generated
by `asciidoctor-maven-plugin` plugin.
### Add Maven Plugin
Add [smart-doc-maven-plugin](https://github.com/smart-doc-group/smart-doc-maven-plugin) in your pom.xml.
```
<plugin>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>[latest]</version>
<configuration>
<!--Specify the configuration file used to generate the document-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--smart-doc implements automatic analysis of the dependency tree to load the source code of third-party dependencies. If some framework dependency libraries are not loaded, an error is reported, then use excludes to exclude-->
<excludes>
<!--The format is: groupId: artifactId; refer to the following-->
<exclude>com.google.guava:guava</exclude>
</excludes>
<!--Since version 1.0.8, the plugin provides includes support-->
<!--smart-doc can automatically analyze the dependency tree to load all dependent source code. In principle, it will affect the efficiency of document construction, so you can use includes to let the plugin load the components you configure.-->
<includes>
<!--The format is: groupId: artifactId; refer to the following-->
<include>com.alibaba:fastjson</include>
</includes>
</configuration>
<executions>
<execution>
<!--Comment out phase if you don't need to start smart-doc when compiling-->
<phase>compile</phase>
<goals>
<!--smart-doc provides html, openapi, markdown, adoc and other goals-->
<goal>html</goal>
</goals>
</execution>
</executions>
</plugin>
```
### Configuration
Create a JSON configuration file in your project. The smart-doc-maven-plugin plugin will use this configuration
information. For example, create `/src/main/resources/smart-doc.json` in the project. The configuration contents are as
follows.
**Minimize configuration:**
```
{
"allInOne": true, // whether to merge documents into one file, generally recommended as true
"isStrict": false,//If the strict mode is set to true, Smart-doc forces that the public method in each interface in the code has a comment.
"outPath": "src/main/resources/static/doc" //Set the api document output path.
}
```
Only the above three simple configuration items can make smart-doc-maven-plugin work. In fact, only the outPath
configuration item is necessary.
**Detailed configuration content:**
When you need to use smart-doc to generate more API document information, you can add detailed configuration content.
```
{
"serverUrl": "http://127.0.0.1", // Set the server address, not required
"isStrict": false, // whether to enable strict mode
"allInOne": true, // whether to merge documents into one file, generally recommended as true
"outPath": "D: // md2", // Specify the output path of the document
"coverOld": true, // Whether to overwrite old files, mainly used for mardown file overwrite
"style":"xt256", //set highlight
"createDebugPage": true,//Create a page that can be used to test your APIs like swagger
"language":"ENGLISH",//support ENGLISH and CHINESE
"packageFilters": "", // controller package filtering, multiple package names separated by commas
"md5EncryptedHtmlName": false, // only used if each controller generates an html file
"projectName": "smart-doc", // Configure your own project name
"skipTransientField": true, // Not currently implemented
"requestFieldToUnderline":true, //convert request field to underline
"responseFieldToUnderline":true,//convert response field to underline
"sortByTitle":false,//Sort by interface title, the default value is false
"showAuthor":true,// display author,default is true
"inlineEnum":true,// Set to true to display enumeration details in the parameter table
"recursionLimit":7,// Set the number of recursive executions to avoid stack overflow, the default is 7
"allInOneDocFileName":"index.html",//Customize the output document name
"requestExample":"true",//Whether to display the request example in the document, the default value is true.
"responseExample":"true",//Whether to display the response example in the document, the default is true.
"displayActualType":false,//display actual type of generic,
"urlSuffix":".do",//Support the url suffix of the old SpringMVC project,@since 2.1.0
"appKey": "xxx",// torna appKey, @since 2.0.9
"appToken": "xxx", //torna appToken,@since 2.0.9
"secret": "xx",//torna secret@since 2.0.9
"openUrl": "torna server/api/",//torna server url,@since 2.0.9
"tornaDebug":false,"// show log while set true
"ignoreRequestParams":[ //The request parameter object will be discarded when generating the document.@since 1.9.2
"org.springframework.ui.ModelMap"
],
"dataDictionaries": [{// Configure the data dictionary, no need to set
"title": "Order Status", // The name of the data dictionary
"enumClassName": "com.power.doc.enums.OrderEnum", // Data dictionary enumeration class name
"codeField": "code", // The field name corresponding to the data dictionary dictionary code
"descField": "desc" // Data dictionary object description information dictionary
}],
"errorCodeDictionaries": [{// error code list, no need to set
"title": "title",
"enumClassName": "com.power.doc.enums.ErrorCodeEnum", // Error code enumeration class
"codeField": "code", // Code field name of the error code
"descField": "desc" // Field name corresponding to the error code description
}],
"revisionLogs": [{// Set document change records, no need to set
"version": "1.0", // Document version number
"revisionTime": "2020-12-31 10:30", //revision time
"author": "author", // Document change author
"status": "update", // Change operation status, generally: create, update, etc.
"remarks": "desc" // Change description
}],
"customResponseFields": [{// Customly add fields and comments. If api-doc encounters a field with the same name later, directly add a comment to the corresponding field. It is not necessary.
"name": "code", // Override the response code field
"desc": "Response code", // Override field comment of response code
"value": "00000" // Set the value of the response code
}],
"customRequestFields": [{//@since 2.1.3
"name":"code", //Override the request code field
"desc":"request code", //Override field comment of response code
"ownerClassName":"com.xxx.constant.entity.Result",
"value":"200", // Set the value of the response code
"required":true,
"ignore":false
}],
"apiObjectReplacements": [{ // Supports replacing specified objects with custom objects to complete document rendering
"className": "org.springframework.data.domain.Pageable",
"replacementClassName": "com.power.doc.model.PageRequestDto" //Use custom PageRequestDto instead of JPA Pageable for document rendering.
}],
"rpcApiDependencies":[{ // Your Apache Dubbo api interface module dependency description.
"artifactId":"SpringBoot2-Dubbo-Api",
"groupId":"com.demo",
"version":"1.0.0"
}],
"apiConstants": [{//Configure your own constant class, smart-doc automatically replaces with a specific value when parsing to a constant
"constantsClassName": "com.power.doc.constants.RequestParamConstant"
}],
"responseBodyAdvice":{ //Support ResponseBodyAdvice
"className":"com.power.common.model.CommonResult" // Standard POJO for Response
},
"requestBodyAdvice":{ //Support ResponseBodyAdvice
"className":"com.power.common.model.CommonResult" // Standard POJO for Request
},
"rpcConsumerConfig": "src/main/resources/consumer-example.conf",//dubbo consumer config example
"requestHeaders": [{// Set global request headers, no need to set
"name": "token",
"type": "string",
"desc": "desc",
"required": false,
"since": "-"
}]
}
```
**Note:** This JSON configuration can be converted into JSON using smart-doc's ApiConfig Object. So the project
configuration can also refer to the introduction of smart-doc.
### Generated document
#### Run Plugin with MAVEN command
```
// Generate html
mvn -Dfile.encoding=UTF-8 smart-doc:html
// Generate markdown
mvn -Dfile.encoding=UTF-8 smart-doc:markdown
// Generate adoc
mvn -Dfile.encoding=UTF-8 smart-doc:adoc
// Generate postman collection
mvn -Dfile.encoding=UTF-8 smart-doc:postman
// Generate document and send to Torna
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rest
// Apache Dubbo RPC
// Generate html
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-html
// Generate markdown
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-markdown
// Generate adoc
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-adoc
// Push dubbo rpc document to Torna
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rpc
```
**Note:** Under the window system, if you use the maven command line to perform document generation, non-English
characters may be garbled, so you need to specify `-Dfile.encoding = UTF-8` during execution.
View maven's coding
```
# mvn -version
Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T19:57:37+08:00)
Maven home: D:\ProgramFiles\maven\bin\..
Java version: 1.8.0_191, vendor: Oracle Corporation
Java home: D:\ProgramFiles\Java\jdk1.8.0_191\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "dos"
```
#### Run Plugin in IDEA
On Use IntelliJ IDE, if you have added smart-doc-maven-plugin to the project, you can directly find the plugin smart-doc
plugin and click to generate API documentation.
![smart-doc-maven-plugin](https://raw.githubusercontent.com/shalousun/smart-doc-maven-plugin/master/images/idea.png)
### Use Gradle Plugin
If you use gradle to build the project, you can refer to the documentation of the gradle plugin to integrate,
[smart-doc-gradle-plugin](https://github.com/smart-doc-group/smart-doc-gradle-plugin/blob/master/README.md)
### Generated document example
#### Interface header rendering
![header](https://images.gitee.com/uploads/images/2019/1231/223538_be45f8a9_144669.png "header.png")
#### Request parameter example rendering
![request-params](https://images.gitee.com/uploads/images/2019/1231/223710_88933f55_144669.png "request.png")
#### Response parameter example renderings
![response-fields](https://images.gitee.com/uploads/images/2019/1231/223817_32bea6dc_144669.png "response.png")
## Integration through unit tests
You can generate documentation by adding smart-doc dependencies directly to your project and then writing unit tests to
start smart-doc. we still recommend that you use the smart-doc-maven-plugin plugin.
[Use smart-doc by junit test](https://github.com/smart-doc-group/smart-doc/wiki/Use-smart-doc-by-junit-test)
![smart-doc+torna](https://raw.githubusercontent.com/shalousun/smart-doc/master/images/smart-doc-torna-en.png)
## Building
@ -295,28 +51,30 @@ mvn clean install -Dmaven.test.skip=true
- Jakarta RS-API 2.x
## Other reference
- [Smart-doc manual](https://github.com/shalousun/smart-doc/wiki)
## Who is using
These are only part of the companies using smart-doc, for reference only. If you are using smart-doc,
please [add your company here](https://github.com/shalousun/smart-doc/issues/12) to tell us your scenario to make
please [add your company here](https://github.com/smart-doc-group/smart-doc/issues/12) to tell us your scenario to make
smart-doc better.
![IFLYTEK](https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/iflytek.png)
&nbsp;&nbsp;<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/oneplus.png" title="OnePlus" width="83px" height="83px"/>
&nbsp;&nbsp;<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/xiaomi.png" title="Xiaomi" width="170px" height="83px"/>
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/yuanmengjiankang.png" title="yuanmengjiankang" width="260px" height="83px"/>
&nbsp;&nbsp;<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/neusoft.png" title="东软集团" width="170px" height="83px"/>
&nbsp;&nbsp;<img src="https://www.hand-china.com/static/img/hand-logo.svg" title="上海汉得信息技术股份有限公司" width="260px" height="83px"/>
&nbsp;&nbsp;<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/shunfeng.png" title="顺丰" width="83px" height="83px"/>
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/zhongkezhilian.png" title="zhongkezhilian" width="272px" height="83px"/>
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/yuanmengjiankang.png" title="yuanmengjiankang" width="260px" height="83px"/>
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/puqie_gaitubao_100x100.jpg" title="puqie" width="83px" height="83px"/>
&nbsp;&nbsp;
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/known-users/tianbo-tech.png" title="tianbo tech" width="127px" height="70px"/>
## Acknowledgements
Thanks to [JetBrains SoftWare](https://www.jetbrains.com) for providing free Open Source license for this open source project.
<img src="https://raw.githubusercontent.com/shalousun/smart-doc/dev/images/jetbrains-variant-3.png" width="260px" height="220px"/>
## License
Smart-doc is under the Apache 2.0 license. See the [LICENSE](https://github.com/shalousun/smart-doc/blob/master/LICENSE)
Smart-doc is under the Apache 2.0 license. See the [LICENSE](https://github.com/smart-doc-group/smart-doc/blob/master/LICENSE)
file for details.
## Contact

View File

@ -14,8 +14,8 @@ Collection2.0+、OpenAPI 3.0+的文档。
$\color{red}{你给我的star胜过所有读过的诗—smart-doc}$
> 无论你是很有经验的大佬、还是刚入行的萌新。遇到使用疑惑时我们希望你能仔细阅读smart-doc官方码云的wiki文档。我们将smart-doc及其插件的 每一个配置项和可能在日常中遇到的问题都整理到了文档中。仔细阅读文档就是对开源项目最大的支持。
[wiki文档](https://gitee.com/smart-doc-team/smart-doc/wikis/HOME?sort_id=3127893)
查看[快速开始](https://smart-doc-group.github.io/#/zh-cn/) 了解详情
## Features
- 零注解、零学习成本、只需要写标准JAVA注释。
@ -43,234 +43,6 @@ smart-doc + [Torna](http://torna.cn) 组成行业领先的文档生成和管理
> Torna是由smart-doc官方独家推动联合研发的企业级文档管理系统因此smart-doc官方不会对接其它任何的外部文档管理系统例如像showdoc、yapi 之类的对接请自定内部处理也不要再给我们提其他文档系统对接的PR。我们核心是把smart-doc+Torna的这套方案打造好。
## Getting Started
smart-doc使用和测试可参考[smart-doc demo](https://gitee.com/devin-alan/api-doc-test.git)。
```
# git clone https://gitee.com/devin-alan/api-doc-test.git
```
你可以启动这个Spring Boot的项目然后访问`http://localhost:8080/doc/api.html`来浏览smart-doc生成的接口文档。
### Add Maven Plugin
smart-doc官方目前已经开发完成[Maven插件](https://gitee.com/smart-doc-team/smart-doc-maven-plugin)
和[Gradle插件](https://gitee.com/smart-doc-team/smart-doc-gradle-plugin)
你可以根据自己的构建工具来选择使用Maven插件或者是Gradle插件。
#### Add Plugin
```
<plugin>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>[最新版本]</version>
<configuration>
<!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
<configFile>./src/main/resources/smart-doc.json</configFile>
<!--指定项目名称-->
<projectName>测试</projectName>
<!--smart-doc实现自动分析依赖树加载第三方依赖的源码如果一些框架依赖库加载不到导致报错这时请使用excludes排除掉-->
<excludes>
<!--格式为groupId:artifactId;参考如下-->
<exclude>com.alibaba:fastjson</exclude>
</excludes>
<!--自1.0.8版本开始插件提供includes支持-->
<!--smart-doc能自动分析依赖树加载所有依赖源码原则上会影响文档构建效率因此你可以使用includes来让插件加载你配置的组件-->
<includes>
<!--格式为groupId:artifactId;参考如下-->
<include>com.alibaba:fastjson</include>
</includes>
</configuration>
<executions>
<execution>
<!--如果不需要在执行编译时启动smart-doc则将phase注释掉-->
<phase>compile</phase>
<goals>
<!--smart-doc提供了html、openapi、markdown等goal可按需配置-->
<goal>html</goal>
</goals>
</execution>
</executions>
</plugin>
```
#### Configuration
在项目中添加创建一个`smart-doc.json`配置文件,插件读取这个配置来生成项目的文档, 这个配置内容实际上就是以前采用单元测试编写的`ApiConfig`转成json后的结果因此关于配置项说明可以参考原来单元测试的配置。
**最小配置单元:**
```
{
"outPath": "D://md2" //指定文档的输出路径,相对路径时请用./开头eg:./src/main/resources/static/doc
}
```
> 如果你想把html文档也打包到应用中一起访问则建议你配置路径为src/main/resources/static/doc
仅仅需要上面一行配置就能启动smart-doc-maven-plugin插件根据自己项目情况更多详细的配置参考下面。
**详细配置说明**
```
{
"serverUrl": "http://127.0.0.1", //服务器地址,非必须。导出postman建议设置成http://{{server}}方便直接在postman直接设置环境变量
"isStrict": false, //是否开启严格模式
"allInOne": true, //是否将文档合并到一个文件中一般推荐为true
"outPath": "D://md2", //指定文档的输出路径
"coverOld": true, //是否覆盖旧的文件主要用于mardown文件覆盖
"createDebugPage": true,//@since 2.0.0 smart-doc支持创建可以测试的html页面仅在AllInOne模式中起作用。
"packageFilters": "",//controller包过滤多个包用英文逗号隔开
"md5EncryptedHtmlName": false,//只有每个controller生成一个html文件是才使用
"style":"xt256", //基于highlight.js的代码高设置,可选值很多可查看码云wiki喜欢配色统一简洁的同学可以不设置
"projectName": "smart-doc",//配置自己的项目名称
"skipTransientField": true,//目前未实现
"sortByTitle":false,//接口标题排序默认为false,@since 1.8.7版本开始
"showAuthor":true,//是否显示接口作者名称默认是true,不想显示可关闭
"requestFieldToUnderline":true,//自动将驼峰入参字段在文档中转为下划线格式,//@since 1.8.7版本开始
"responseFieldToUnderline":true,//自动将驼峰入参字段在文档中转为下划线格式,//@since 1.8.7版本开始
"inlineEnum":true,//设置为true会将枚举详情展示到参数表中默认关闭//@since 1.8.8版本开始
"recursionLimit":7,//设置允许递归执行的次数用于避免一些对象解析卡主默认是7正常为3次以内//@since 1.8.8版本开始
"allInOneDocFileName":"index.html",//自定义设置输出文档名称, @since 1.9.0
"requestExample":"true",//是否将请求示例展示在文档中默认true@since 1.9.0
"responseExample":"true",//是否将响应示例展示在文档中默认为true@since 1.9.0
"urlSuffix":".do",//支持SpringMVC旧项目的url后缀,@since 2.1.0
"displayActualType":false,//配置true会在注释栏自动显示泛型的真实类型短类名@since 1.9.6
"appKey": "20201216788835306945118208",// torna平台对接appKey,, @since 2.0.9
"appToken": "c16931fa6590483fb7a4e85340fcbfef", //torna平台appToken,@since 2.0.9
"secret": "W.ZyGMOB9Q0UqujVxnfi@.I#V&tUUYZR",//torna平台secret@since 2.0.9
"openUrl": "http://localhost:7700/api",//torna平台地址填写自己的私有化部署地址@since 2.0.9
"debugEnvName":"测试环境", //torna环境名称
"debugEnvUrl":"http://127.0.0.1",//推送torna配置接口服务地址
"tornaDebug":false,//启用会推送日志
"ignoreRequestParams":[ //忽略请求参数对象,把不想生成文档的参数对象屏蔽掉,@since 1.9.2
"org.springframework.ui.ModelMap"
],
"dataDictionaries": [{ //配置数据字典,没有需求可以不设置
"title": "http状态码字典", //数据字典的名称
"enumClassName": "com.power.common.enums.HttpCodeEnum", //数据字典枚举类名称
"codeField": "code",//数据字典字典码对应的字段名称
"descField": "message"//数据字典对象的描述信息字典
}],
"errorCodeDictionaries": [{ //错误码列表,没有需求可以不设置
"title": "title",
"enumClassName": "com.power.common.enums.HttpCodeEnum", //错误码枚举类
"codeField": "code",//错误码的code码字段名称
"descField": "message"//错误码的描述信息对应的字段名
}],
"revisionLogs": [{ //文档变更记录
"version": "1.0", //文档版本号
"revisionTime": "2020-12-31 10:30", //文档修订时间
"status": "update", //变更操作状态,一般为:创建、更新等
"author": "author", //文档变更作者
"remarks": "desc" //变更描述
}
],
"customResponseFields": [{ //自定义添加字段和注释api-doc后期遇到同名字段则直接给相应字段加注释非必须
"name": "code",//覆盖响应码字段
"desc": "响应代码",//覆盖响应码的字段注释
"ownerClassName": "org.springframework.data.domain.Pageable", //指定你要添加注释的类名
"value": "00000"//设置响应码的值
}],
"customRequestFields": [{ //自定义请求体的注释,@since 2.1.3
"name":"code", //属性名
"desc":"状态码", //描述
"ownerClassName":"com.xxx.constant.entity.Result", //属性对应的类全路径
"value":"200", //默认值或者mock值
"required":true, //是否必填
"ignore":false //是否忽略
}],
"requestHeaders": [{ //设置请求头,没有需求可以不设置
"name": "token",//请求头名称
"type": "string",//请求头类型
"desc": "desc",//请求头描述信息
"value":"token请求头的值",//不设置默认null
"required": false,//是否必须
"since": "-"//什么版本添加的改请求头
}],
"rpcApiDependencies":[{ // 项目开放的dubbo api接口模块依赖配置后输出到文档方便使用者集成
"artifactId":"SpringBoot2-Dubbo-Api",
"groupId":"com.demo",
"version":"1.0.0"
}],
"rpcConsumerConfig": "src/main/resources/consumer-example.conf",//文档中添加dubbo consumer集成配置用于方便集成方可以快速集成
"apiObjectReplacements": [{ // 自smart-doc 1.8.5开始你可以使用自定义类覆盖其他类做文档渲染,非必须
"className": "org.springframework.data.domain.Pageable",
"replacementClassName": "com.power.doc.model.PageRequestDto" //自定义的PageRequestDto替换Pageable做文档渲染
}],
"apiConstants": [{//从1.8.9开始配置自己的常量类smart-doc在解析到常量时自动替换为具体的值
"constantsClassName": "com.power.doc.constants.RequestParamConstant"
}],
"responseBodyAdvice":{ //自smart-doc 1.9.8起ResponseBodyAdvice统一返回设置可用ignoreResponseBodyAdvice tag来忽略
"className":"com.power.common.model.CommonResult" //通用响应体
},
"requestBodyAdvice":{ ////自smart-doc 2.1.4 起支持设置RequestBodyAdvice统一请求包装类
"className":"com.power.common.model.CommonResult"
},
"sourceCodePaths": [{ //设置代码路径, 插件已经能够自动下载发布的源码包,没必要配置
"path": "src/main/java",
"desc": "测试"
}]
}
```
上面的JSON配置实例中只有"outPath"是必填项。
**注意:** 对于老用户完全可以通过`Fastjson`或者是`Gson`库将`ApiConfig`转化成JSON配置。
#### Use Maven Command
添加好插件和配置文件后可以直接运行Maven命令生成文档。
```
//生成html
mvn -Dfile.encoding=UTF-8 smart-doc:html
//生成markdown
mvn -Dfile.encoding=UTF-8 smart-doc:markdown
//生成adoc
mvn -Dfile.encoding=UTF-8 smart-doc:adoc
//生成postman json数据
mvn -Dfile.encoding=UTF-8 smart-doc:postman
// 生成 Open Api 3.0+,Since smart-doc-maven-plugin 1.1.5
mvn -Dfile.encoding=UTF-8 smart-doc:openapi
// 生成文档推送到Torna平台
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rest
// Apache Dubbo RPC文档
// Generate html
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-html
// Generate markdown
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-markdown
// Generate adoc
mvn -Dfile.encoding=UTF-8 smart-doc:rpc-adoc
// 生成dubbo接口文档推送到torna
mvn -Dfile.encoding=UTF-8 smart-doc:torna-rpc
```
**注意:** 尤其在window系统下如果实际使用Maven命令行执行文档生成可能会出现乱码因此需要在执行时指定`-Dfile.encoding=UTF-8`。
#### Use in IDEA
![idea中smart-doc-maven插件使用](https://gitee.com/smart-doc-team/smart-doc-maven-plugin/raw/master/images/idea.png "maven_plugin_tasks.png")
### Use gradle plugin
如果你使用Gradle来构建项目你可以参考Gradle插件的使用文档来集成
[smart-doc-gradle-plugin](https://gitee.com/smart-doc-team/smart-doc-gradle-plugin/blob/master/README_CN.md)
### Use Junit Test
从smart-doc 1.7.9开始官方提供了Maven插件使用smart-doc的Maven插件后不再需要创建单元测试。
[单元测试生成文档](https://gitee.com/smart-doc-team/smart-doc/wikis/单元测试集成smart-doc?sort_id=1990284)
> 单元测试集成存在很多绝限性: <br/>1. 对于多模块项目源码注释很麻烦,也不符合正常开发团队的开发协作。<br/>2. 不方便与CI工具集成
### Generated document example
[点击查看文档生成文档效果图](https://gitee.com/smart-doc-team/smart-doc/wikis/文档效果图?sort_id=1652819)
## Building
如果你需要自己构建smart-doc那可以使用下面命令构建需要依赖Java 1.8。
@ -283,11 +55,6 @@ mvn clean install -Dmaven.test.skip=true
- Jakarta RS-API 2.x
## Other reference
- [smart-doc功能使用介绍](https://my.oschina.net/u/1760791/blog/2250962)
- [smart-doc官方wiki](https://gitee.com/smart-doc-team/smart-doc/wikis/Home?sort_id=1652800)
## License
smart-doc is under the Apache 2.0 license. See
@ -302,18 +69,33 @@ the [LICENSE](https://gitee.com/smart-doc-team/smart-doc/blob/master/LICENSE) fi
![IFLYTEK](https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/iflytek.png)
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/oneplus.png" title="一加" width="83px" height="83px"/>
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/xiaomi.png" title="小米" width="170px" height="83px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/yuanmengjiankang.png" title="远盟健康" width="260px" height="83px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/zhongkezhilian.png" title="中科智链" width="272px" height="83px"/>
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/neusoft.png" title="东软集团" width="180px" height="83px"/>
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/zhongkezhilian.png" title="中科智链" width="272px" height="83px"/>
&nbsp;&nbsp;<img src="https://www.hand-china.com/static/img/hand-logo.svg" title="上海汉得信息技术股份有限公司" width="260px" height="83px"/>
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/shunfeng.png" title="顺丰" width="83px" height="83px"/>
&nbsp;&nbsp;<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/yuanmengjiankang.png" title="远盟健康" width="260px" height="83px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/puqie_gaitubao_100x100.jpg" title="普切信息科技" width="83px" height="83px"/>
&nbsp;&nbsp;
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/tianbo-tech.png" title="杭州天铂云科" width="127px" height="70px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/known-users/tianbo-tech.png" title="杭州天铂云科" width="135px" height="83px"/>
&nbsp;&nbsp;
## Award situation
- 2020 年度 OSC 中国开源项目评选”活动中获得「最积极运营项目」
## Acknowledgements
感谢[JetBrains SoftWare](https://www.jetbrains.com) 为本开源项目提供的免费Open Source license。
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/jetbrains-variant-3.png" width="260px" height="220px"/>
## Contact
愿意参与构建smart-doc或者是需要交流问题可以加入qq群
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/smart-doc-qq.png" title="qq群" width="200px" height="200px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/smart-doc-qq.png" title="qq群" width="200px" height="210px"/>
<img src="https://gitee.com/smart-doc-team/smart-doc/raw/master/images/smart-doc-qq2.jpeg" title="qq群2" width="200px" height="210px"/>
> 1群已满有问题请加2群。
## Donate
如果您觉得我们的开源软件对你有所帮助,请扫下方二维码打赏我们一杯咖啡
<img src="https://images.gitee.com/uploads/images/2020/0831/225756_9aecdd4d_144669.png" width="200px" height="210px"/>

View File

@ -1,150 +0,0 @@
关于list结构的返回json数据测试
# List<String>结构
api-doc对于List中返回基础数据类型都是支持的
```
/**
* List<String>
*
* @return
*/
@GetMapping(value = "listString")
public List<String> testList() {
return null;
}
```
api-doc生成的响应数据
```
[ "ivvqah","isrz5x"]
```
# List<Map<String,String>>结构
```
/**
*
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Map<String,String>> testMap() {
return null;
}
```
api-doc生成的响应数据
```
[{
"mapKey1": "o9mibj",
"mapKey2": "3dnnrn"
}]
```
# List<Map<String,T>>结构
```
@GetMapping(value = "/map/Primitive")
public List<Map<String,Student>> testMap() {
return null;
}
```
相应数据省略
# 测试List<T>结构
```
/**
* 测试List<T>结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Teacher> testMap() {
return null;
}
```
# List<T<M,N>>结构
```
/**
* 测试List<T<M,N>>结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Teacher<User,User>> testMap() {
return null;
}
```
# List<Map<M,N<P,k>>>超复杂结构
```
/**
* 测试List<Map<M,N<P,k>>>超复杂结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public List<Map<String,Teacher<User,User>>> testMap() {
return null;
}
```
api-doc自动返回的数据
```
[{
"mapKey": {
"data": {
"userName": "lxh2yi",
"userAddress": "6jfp3h",
"userAge": 741
},
"data1": {
"userName": "1wp54g",
"userAddress": "8ul6m4",
"userAge": 550
},
"age": 10
}
}]
```
# List<T<List<M>,List<M>,List<M>>>超复杂结构
```
/**
* List<T<List<M>,List<M>,List<M>>>
* @return
*/
@GetMapping(value = "listString")
public List<Teacher<List<User>,List<User>,List<User>>> testListString(){
return null;
}
```
# 其他复杂结构
```
/**
* List<T<List<M>,List<M>,List<M>>>
*
* @return
*/
@GetMapping(value = "listString")
public List<Teacher<Teacher<User,User,User>,User,User>> testListString() {
return null;
}
@GetMapping(value = "listString")
public List<Teacher<Teacher<User,User,User>,Teacher<User,User,User>,Teacher<User,User,User>>> testListString() {
return null;
}
```
**注意:** api-doc为了传入的复杂泛型结构数据做了许多情况的测试目前基本能兼容系统开发中95%以上的List返回接口 也提供了一些能够处理的很复杂的泛型结构,但是这种复杂的泛型结构在开发中是不被推荐的。

View File

@ -1,171 +0,0 @@
Api-doc对于api中map结构数据的json化处理多组测试用例,对于map返回的json结构 目前基本仅仅支持String类型的key。
**基础数据类型:** json支持的基本java数据类型(不包含byte,包含String)
# map使用基础数据类型
```
/**
* 测试map使用基础数据类型
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Integer> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey1": 721,
"mapKey2": 280
}
```
# map使用Object
因为api-doc使用的是无侵入静态分析生成api文档因此对于直接使用Object做map value的接口api-doc无法准确的生成json。
所以api-doc返回是会在默认json中加一段警告使用者需要自己去修改返回数据或者是使用显示的类型数据结构。
```
/**
* 测试map使用基础数据类型
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Object> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"waring": "You may use java.util.Object for Map value;Api-doc can't be handle."
}
}
```
# map中属于自己定义的简单数据结构
User对象的属性仅仅是基本数据类型
```
/**
* 测试map使用自定义数据结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,User> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"userName": "7t2ccy",
"userAddress": "3ipy7g",
"userAge": 280
}
}
```
# map中属于自己定义的复杂数据结构
Student对象的属性有基本类型又有User类型和Map类型的属性。
```
/**
* 测试map使用自定义数据结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Student> testMap() {
return null;
}
```
api-doc 生成的json:
```
{
"mapKey": {
"stuName": "9cwzml",
"stuAge": 792,
"stuAddress": "rdfmtx",
"user": {
"userName": "fjglql",
"userAddress": "yy6vkf",
"userAge": 398
},
"userMap": {
"mapKey": {
"userName": "paw90w",
"userAddress": "mnmz42",
"userAge": 937
}
},
"user1": {
"userName": "rr3v6g",
"userAddress": "rbeorq",
"userAge": 399
}
}
}
```
# Map<M,N<P,k>>复杂结构
```
{
"mapKey":{
"data":{
"userName":"tumrit",
"userAddress":"v8fvdi",
"userAge":465
},
"data1":{
"userName":"f7wbwk",
"userAddress":"brdh8j",
"userAge":345
},
"age":194
}
}
```
# Map<String,T<List<M>,N>超复杂结构
```
/**
* Map<String,T<List<M>,N>超复杂结构
* @return
*/
@GetMapping(value = "/map/Primitive")
public Map<String,Teacher<List<User>,User>> testMap() {
return null;
}
```
# Map其他复杂结构
对于map的key采用多泛型的情况目前api-doc也是支持的。
```
/**
* Map<String,T<List<M>,N>超复杂结构
* @return
*/
public Map<String,Teacher<Map<String,User>,Map<String,User>,Map<String,User>>> testMap() {
return null;
}
```
**注意:** api-doc为了传入的复杂泛型结构数据做了许多情况的测试目前基本能兼容系统开发中95%以上的Map返回接口 也提供了一些能够处理的很复杂的泛型结构,但是这种复杂的泛型结构在开发中是不被推荐的。

View File

@ -1,31 +0,0 @@
api-doc对Spring mvc或者SpringBoot应用的Controller接口返回做了一些强制规约一旦在代码中使用 这些被api-doc不推荐的接口返回类型api-doc将会直接报错。
# 违反规约的实例
## 直接返回Object
```
/**
* 返回object
* @return
*/
@GetMapping("/test/Object")
public Object getMe(){
return null;
}
```
报错提示Please do not return java.lang.Object directly in api interface.
## 将非String对象作为Map的key然后将map作为接口中返回
```
/**
* 测试object的作为map的key
* @return
*/
@GetMapping("/test/map")
public Map<Object,Object> objectMap(){
return null;
}
```

View File

@ -1,74 +0,0 @@
{
"components": {
"schemas": {
"Pet": {
"type": "object",
"discriminator": {
"propertyName": "petType"
},
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string"
}
},
"required": [
"name",
"petType"
]
},
"Cat": {
"description": "A representation of a cat. Note that `Cat` will be used as the discriminator value.",
"allOf": [
{
"$ref": "#/components/schemas/Pet"
},
{
"type": "object",
"properties": {
"huntingSkill": {
"type": "string",
"description": "The measured skill for hunting",
"default": "lazy",
"enum": [
"clueless",
"lazy",
"adventurous",
"aggressive"
]
}
},
"required": [
"huntingSkill"
]
}
]
},
"Dog": {
"description": "A representation of a dog. Note that `Dog` will be used as the discriminator value.",
"allOf": [
{
"$ref": "#/components/schemas/Pet"
},
{
"type": "object",
"properties": {
"packSize": {
"type": "integer",
"format": "int32",
"description": "the size of the pack the dog is from",
"default": 0,
"minimum": 0
}
},
"required": [
"packSize"
]
}
]
}
}
}
}

View File

@ -1,130 +0,0 @@
```java
/**
* Get fields
*
* @param cls1 The JavaClass object
* @param counter Recursive counter
* @param addedFields added fields,Field deduplication
* @return list of JavaField
*/
public static List<DocJavaField> getFields(JavaClass cls1,int counter,HashMap<String, DocJavaField> addedFields){
List<DocJavaField> fieldList=new ArrayList<>();
if(null==cls1){
return fieldList;
}else if("Object".equals(cls1.getSimpleName())||"Timestamp".equals(cls1.getSimpleName())||
"Date".equals(cls1.getSimpleName())||"Locale".equals(cls1.getSimpleName())
||"ClassLoader".equals(cls1.getSimpleName())||JavaClassValidateUtil.isMap(cls1.getFullyQualifiedName())
||cls1.isEnum()||"Serializable".equals(cls1.getSimpleName())){
return fieldList;
}else{
String className=cls1.getFullyQualifiedName();
if(cls1.isInterface()&&
!JavaClassValidateUtil.isCollection(className)&&
!JavaClassValidateUtil.isMap(className)){
List<JavaMethod> methods=cls1.getMethods();
for(JavaMethod javaMethod:methods){
String methodName=javaMethod.getName();
int paramSize=javaMethod.getParameters().size();
boolean enable=false;
if(methodName.startsWith("get")&&!"get".equals(methodName)&&paramSize==0){
methodName=StringUtil.firstToLowerCase(methodName.substring(3));
enable=true;
}else if(methodName.startsWith("is")&&!"is".equals(methodName)&&paramSize==0){
methodName=StringUtil.firstToLowerCase(methodName.substring(2));
enable=true;
}
if(!enable||addedFields.containsKey(methodName)){
continue;
}
String comment=javaMethod.getComment();
JavaField javaField=new DefaultJavaField(javaMethod.getReturns(),methodName);
DocJavaField docJavaField=DocJavaField.builder()
.setJavaField(javaField)
.setComment(comment)
.setDocletTags(javaMethod.getTags())
.setAnnotations(javaMethod.getAnnotations())
.setFullyQualifiedName(javaField.getType().getFullyQualifiedName())
.setGenericCanonicalName(javaField.getType().getGenericCanonicalName());
addedFields.put(methodName,docJavaField);
}
}
// ignore enum parent class
if(!cls1.isEnum()){
JavaClass parentClass=cls1.getSuperJavaClass();
getFields(parentClass,counter,addedFields);
List<JavaType> implClasses=cls1.getImplements();
for(JavaType type:implClasses){
JavaClass javaClass=(JavaClass)type;
getFields(javaClass,counter,addedFields);
}
}
Map<String, JavaType> actualJavaTypes=getActualTypesMap(cls1);
List<JavaMethod> javaMethods=cls1.getMethods();
for(JavaMethod method:javaMethods){
String methodName=method.getName();
int paramSize=method.getParameters().size();
if(methodName.startsWith("get")&&!"get".equals(methodName)&&paramSize==0){
methodName=StringUtil.firstToLowerCase(methodName.substring(3));
}else if(methodName.startsWith("is")&&!"is".equals(methodName)&&paramSize==0){
methodName=StringUtil.firstToLowerCase(methodName.substring(2));
}
if(addedFields.containsKey(methodName)){
String comment=method.getComment();
DocJavaField docJavaField=addedFields.get(methodName);
docJavaField.setAnnotations(method.getAnnotations());
docJavaField.setComment(comment);
addedFields.put(methodName,docJavaField);
}
}
for(JavaField javaField:cls1.getFields()){
String fieldName=javaField.getName();
DocJavaField docJavaField=DocJavaField.builder();
boolean typeChecked=false;
String gicName=javaField.getType().getGenericCanonicalName();
String subTypeName=javaField.getType().getFullyQualifiedName();
String actualType=null;
if(JavaClassValidateUtil.isCollection(subTypeName)&&
!JavaClassValidateUtil.isCollection(gicName)){
String[]gNameArr=DocClassUtil.getSimpleGicName(gicName);
actualType=JavaClassUtil.getClassSimpleName(gNameArr[0]);
docJavaField.setArray(true);
typeChecked=true;
}
if(JavaClassValidateUtil.isPrimitive(subTypeName)&&!typeChecked){
docJavaField.setPrimitive(true);
typeChecked=true;
}
if(JavaClassValidateUtil.isFile(subTypeName)&&!typeChecked){
docJavaField.setFile(true);
typeChecked=true;
}
if(javaField.getType().isEnum()&&!typeChecked){
docJavaField.setEnum(true);
}
for(Map.Entry<String, JavaType> entry:actualJavaTypes.entrySet()){
String key=entry.getKey();
JavaType value=entry.getValue();
if(gicName.contains(key)){
subTypeName=subTypeName.replaceAll(key,value.getFullyQualifiedName());
gicName=gicName.replaceAll(key,value.getGenericCanonicalName());
actualType=value.getFullyQualifiedName();
}
}
docJavaField.setComment(javaField.getComment())
.setJavaField(javaField).setFullyQualifiedName(subTypeName)
.setGenericCanonicalName(gicName).setActualJavaType(actualType)
.setAnnotations(javaField.getAnnotations());
if(addedFields.containsKey(fieldName)){
addedFields.put(fieldName,docJavaField);
continue;
}
addedFields.put(fieldName,docJavaField);
}
List<DocJavaField> parentFieldList=addedFields.values().stream()
.filter(v->Objects.nonNull(v)).collect(Collectors.toList());
fieldList.addAll(parentFieldList);
}
return fieldList;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
images/smart-doc-qq2.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

20
pom.xml
View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>smart-doc</artifactId>
<packaging>jar</packaging>
<version>2.1.7</version>
<version>2.3.2</version>
<name>smart-doc</name>
<url>https://github.com/smart-doc-group/smart-doc.git</url>
@ -34,19 +34,19 @@
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flexmark.version>0.62.2</flexmark.version>
<junit.jupiter.version>5.8.1</junit.jupiter.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.3.1.RELEASE</version>
<version>3.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
@ -67,17 +67,17 @@
<dependency>
<groupId>com.github.shalousun</groupId>
<artifactId>common-util</artifactId>
<version>2.0.6</version>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
<version>1.7.32</version>
</dependency>
</dependencies>
<build>
@ -132,7 +132,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>

View File

@ -22,10 +22,10 @@
*/
package com.power.doc.builder;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.util.List;
@ -61,14 +61,16 @@ public class AdocDocBuilder {
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
config.setParamsDataToTree(false);
config.setAdoc(true);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
if (config.isAllInOne()) {
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, ALL_IN_ONE_ADOC_TPL, INDEX_DOC);
String docName = builderTemplate.allInOneDocName(config,INDEX_DOC,".adoc");
apiDocList = docBuildTemplate.handleApiGroup(apiDocList, config);
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, ALL_IN_ONE_ADOC_TPL, docName);
} else {
builderTemplate.buildApiDoc(apiDocList, config, API_DOC_ADOC_TPL, API_EXTENSION);
builderTemplate.buildErrorCodeDoc(config, ERROR_CODE_LIST_ADOC_TPL, ERROR_CODE_LIST_ADOC);
@ -86,7 +88,7 @@ public class AdocDocBuilder {
config.setAdoc(false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, new JavaProjectBuilder());
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
builderTemplate.buildSingleApi(configBuilder, controllerName, API_DOC_ADOC_TPL, API_EXTENSION);
}
}

View File

@ -39,7 +39,7 @@ public class ApiDataBuilder {
* @return List of ApiDoc
*/
public static ApiAllData getApiData(ApiConfig config) {
return getApiData(config, false);
return getApiData(config, Boolean.FALSE);
}
/**
@ -49,7 +49,7 @@ public class ApiDataBuilder {
* @return List of ApiDoc
*/
public static ApiAllData getApiDataTree(ApiConfig config) {
return getApiData(config, true);
return getApiData(config, Boolean.TRUE);
}
private static ApiAllData getApiData(ApiConfig config, boolean toTree) {
@ -57,7 +57,7 @@ public class ApiDataBuilder {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInitForGetApiData(config);
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
builderTemplate.getApiData(config, javaProjectBuilder);
return builderTemplate.getApiData(config, javaProjectBuilder);
ApiAllData apiAllData = builderTemplate.getApiData(config, javaProjectBuilder);
return apiAllData;
}
}

View File

@ -23,10 +23,10 @@
package com.power.doc.builder;
import com.power.common.util.DateTimeUtil;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.util.List;
@ -60,15 +60,17 @@ public class ApiDocBuilder {
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
config.setAdoc(false);
config.setParamsDataToTree(false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
if (config.isAllInOne()) {
String version = config.isCoverOld() ? "" : "-V" + DateTimeUtil.long2Str(System.currentTimeMillis(), DATE_FORMAT);
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, ALL_IN_ONE_MD_TPL, "AllInOne" + version + ".md");
String docName = builderTemplate.allInOneDocName(config,"AllInOne" + version + ".md",".md");
apiDocList = docBuildTemplate.handleApiGroup(apiDocList, config);
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, ALL_IN_ONE_MD_TPL, docName);
} else {
builderTemplate.buildApiDoc(apiDocList, config, API_DOC_MD_TPL, API_EXTENSION);
builderTemplate.buildErrorCodeDoc(config, ERROR_CODE_LIST_MD_TPL, ERROR_CODE_LIST_MD);
@ -87,7 +89,7 @@ public class ApiDocBuilder {
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
builderTemplate.buildSingleApi(configBuilder, controllerName, API_DOC_MD_TPL, API_EXTENSION);
}
}

View File

@ -22,20 +22,29 @@
*/
package com.power.doc.builder;
import com.power.common.util.CollectionUtil;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.EnumUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.DocLanguage;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.constants.TemplateVariable;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiErrorCode;
import com.power.doc.model.ApiErrorCodeDictionary;
import com.power.doc.model.RevisionLog;
import org.apache.commons.lang3.StringUtils;
import org.beetl.core.Resource;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import java.util.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static com.power.doc.constants.DocGlobalConstants.CSS_CDN;
import static com.power.doc.constants.DocGlobalConstants.CSS_CDN_CH;
/**
* @author yu 2020/5/16.
@ -48,10 +57,11 @@ public class BaseDocBuilderTemplate {
* check condition and init
*
* @param config Api config
* @param checkOutPath check out path
*/
public void checkAndInit(ApiConfig config) {
public void checkAndInit(ApiConfig config,boolean checkOutPath) {
this.checkAndInitForGetApiData(config);
if (StringUtil.isEmpty(config.getOutPath())) {
if (StringUtil.isEmpty(config.getOutPath())&&!checkOutPath) {
throw new RuntimeException("doc output path can't be null or empty");
}
}
@ -83,6 +93,10 @@ public class BaseDocBuilderTemplate {
.setStatus("auto")
);
}
if (StringUtil.isEmpty(config.getFramework())) {
config.setFramework(FrameworkEnum.SPRING.getFramework());
}
}
public Map<String, String> setDirectoryLanguageVariable(ApiConfig config, Template mapper) {
@ -107,4 +121,47 @@ public class BaseDocBuilderTemplate {
}
return titleMap;
}
public void setCssCDN(ApiConfig config, Template template) {
if (DocLanguage.CHINESE.equals(config.getLanguage())) {
template.binding(TemplateVariable.CSS_CND.getVariable(), CSS_CDN_CH);
} else {
template.binding(TemplateVariable.CSS_CND.getVariable(), CSS_CDN);
}
}
public String allInOneDocName(ApiConfig apiConfig, String fileName, String suffix) {
String allInOneName = apiConfig.getAllInOneDocFileName();
if (StringUtils.isNotEmpty(apiConfig.getAllInOneDocFileName())) {
if (allInOneName.endsWith(suffix)) {
return allInOneName;
} else {
return allInOneName + suffix;
}
} else if (StringUtil.isNotEmpty(fileName) && fileName.endsWith(suffix)) {
return fileName;
} else {
return fileName + suffix;
}
}
public static void copyJarFile(String source, String target) {
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/template/");
Resource resource = resourceLoader.getResource(source);
try (FileWriter fileWriter = new FileWriter(target, false);
Reader reader = resource.openReader()) {
char[] c = new char[1024 * 1024];
int temp;
int len = 0;
while ((temp = reader.read()) != -1) {
c[len] = (char) temp;
len++;
}
reader.close();
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write(c, 0, len);
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -22,16 +22,18 @@
*/
package com.power.doc.builder;
import com.power.common.util.*;
import com.power.common.util.CollectionUtil;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.FileUtil;
import com.power.doc.constants.DocLanguage;
import com.power.doc.constants.HighlightStyle;
import com.power.doc.constants.TemplateVariable;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.*;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.utils.BeetlTemplateUtil;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import org.beetl.core.Template;
import java.util.ArrayList;
@ -39,8 +41,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import static com.power.doc.constants.DocGlobalConstants.FILE_SEPARATOR;
import static com.power.doc.constants.DocGlobalConstants.SEARCH_JS_OUT;
import static com.power.doc.constants.DocGlobalConstants.*;
/**
* @author yu 2019/9/26.
@ -123,6 +124,7 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
Template tpl = BeetlTemplateUtil.getByName(template);
String style = config.getStyle();
tpl.binding(TemplateVariable.STYLE.getVariable(), style);
tpl.binding(TemplateVariable.HIGH_LIGHT_CSS_LINK.getVariable(), config.getHighlightStyleLink());
tpl.binding(TemplateVariable.BACKGROUND.getVariable(), HighlightStyle.getBackgroundColor(style));
tpl.binding(TemplateVariable.API_DOC_LIST.getVariable(), apiDocList);
tpl.binding(TemplateVariable.ERROR_CODE_LIST.getVariable(), errorCodeList);
@ -133,6 +135,9 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
tpl.binding(TemplateVariable.PROJECT_NAME.getVariable(), config.getProjectName());
tpl.binding(TemplateVariable.REQUEST_EXAMPLE.getVariable(), config.isRequestExample());
tpl.binding(TemplateVariable.RESPONSE_EXAMPLE.getVariable(), config.isResponseExample());
tpl.binding(TemplateVariable.DISPLAY_REQUEST_PARAMS.getVariable(), config.isRequestParamsTable());
tpl.binding(TemplateVariable.DISPLAY_RESPONSE_PARAMS.getVariable(), config.isResponseParamsTable());
setCssCDN(config, tpl);
if (CollectionUtil.isEmpty(errorCodeList)) {
tpl.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 1);
} else {
@ -158,33 +163,48 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
apiDoc1.setOrder(apiDocs.size() + 1);
apiDocs.add(apiDoc1);
}
boolean isOnlyDefaultGroup = apiDocList.size() == 1;
Map<String, String> titleMap = setDirectoryLanguageVariable(config, tpl);
// set error code
if (CollectionUtil.isNotEmpty(errorCodeList)) {
ApiDoc apiDoc1 = new ApiDoc();
apiDoc1.setOrder(apiDocs.size() + 1);
apiDoc1.setOrder((isOnlyDefaultGroup ? apiDocs.get(0).getChildrenApiDocs().size() : apiDocs.size()) + 1);
apiDoc1.setDesc(titleMap.get(TemplateVariable.ERROR_LIST_TITLE.getVariable()));
apiDoc1.setList(new ArrayList<>(0));
apiDoc1.setLink("error_code_list");
apiDoc1.setAlias("error");
apiDocs.add(apiDoc1);
apiDoc1.setGroup(apiDoc1.getDesc());
if (isOnlyDefaultGroup) {
apiDocs.get(0).getChildrenApiDocs().add(apiDoc1);
} else {
apiDocs.add(apiDoc1);
}
}
// set dict list
List<ApiDocDict> apiDocDictList = DocUtil.buildDictionary(config, javaProjectBuilder);
ApiDoc apiDoc1 = new ApiDoc();
apiDoc1.setOrder(apiDocs.size() + 1);
apiDoc1.setLink("dict_list");
apiDoc1.setAlias("dict");
apiDoc1.setDesc(titleMap.get(TemplateVariable.DICT_LIST_TITLE.getVariable()));
List<ApiMethodDoc> methodDocs = new ArrayList<>();
for (ApiDocDict apiDocDict : apiDocDictList) {
ApiMethodDoc methodDoc = new ApiMethodDoc();
methodDoc.setOrder(apiDocDict.getOrder());
methodDoc.setDesc(apiDocDict.getTitle());
methodDocs.add(methodDoc);
if (CollectionUtil.isNotEmpty(apiDocDictList)) {
ApiDoc apiDoc1 = new ApiDoc();
apiDoc1.setOrder((isOnlyDefaultGroup ? apiDocs.get(0).getChildrenApiDocs().size() : apiDocs.size()) + 1);
apiDoc1.setLink("dict_list");
apiDoc1.setAlias("dict");
apiDoc1.setDesc(titleMap.get(TemplateVariable.DICT_LIST_TITLE.getVariable()));
apiDoc1.setGroup(apiDoc1.getDesc());
List<ApiMethodDoc> methodDocs = new ArrayList<>();
for (ApiDocDict apiDocDict : apiDocDictList) {
ApiMethodDoc methodDoc = new ApiMethodDoc();
methodDoc.setOrder(apiDocDict.getOrder());
methodDoc.setDesc(apiDocDict.getTitle());
methodDocs.add(methodDoc);
}
apiDoc1.setList(methodDocs);
if (isOnlyDefaultGroup) {
apiDocs.get(0).getChildrenApiDocs().add(apiDoc1);
} else {
apiDocs.add(apiDoc1);
}
}
apiDoc1.setList(methodDocs);
apiDocs.add(apiDoc1);
tpl.binding(TemplateVariable.API_DOC_LIST.getVariable(), apiDocs);
FileUtil.nioWriteFile(tpl.render(), config.getOutPath() + FILE_SEPARATOR + SEARCH_JS_OUT);
}
@ -200,10 +220,11 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
public void buildErrorCodeDoc(ApiConfig config, String template, String outPutFileName) {
List<ApiErrorCode> errorCodeList = DocUtil.errorCodeDictToList(config);
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
Template mapper = BeetlTemplateUtil.getByName(template);
mapper.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
mapper.binding(TemplateVariable.LIST.getVariable(), errorCodeList);
FileUtil.nioWriteFile(mapper.render(), config.getOutPath() + FILE_SEPARATOR + outPutFileName);
Template tpl = BeetlTemplateUtil.getByName(template);
setCssCDN(config, tpl);
tpl.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
tpl.binding(TemplateVariable.LIST.getVariable(), errorCodeList);
FileUtil.nioWriteFile(tpl.render(), config.getOutPath() + FILE_SEPARATOR + outPutFileName);
}
/**
@ -223,12 +244,15 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
Template errorTemplate = BeetlTemplateUtil.getByName(template);
errorTemplate.binding(TemplateVariable.PROJECT_NAME.getVariable(), config.getProjectName());
String style = config.getStyle();
errorTemplate.binding(TemplateVariable.HIGH_LIGHT_CSS_LINK.getVariable(), config.getHighlightStyleLink());
errorTemplate.binding(TemplateVariable.STYLE.getVariable(), style);
if (CollectionUtil.isEmpty(errorCodeList)) {
errorTemplate.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 1);
} else {
errorTemplate.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 2);
}
// set css cdn
setCssCDN(config, errorTemplate);
List<ApiDocDict> apiDocDictList = DocUtil.buildDictionary(config, javaProjectBuilder);
errorTemplate.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
errorTemplate.binding(TemplateVariable.VERSION.getVariable(), now);
@ -259,13 +283,22 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
mapper.binding(TemplateVariable.PROJECT_NAME.getVariable(), config.getProjectName());
String style = config.getStyle();
mapper.binding(TemplateVariable.HIGH_LIGHT_CSS_LINK.getVariable(), config.getHighlightStyleLink());
mapper.binding(TemplateVariable.STYLE.getVariable(), style);
List<ApiErrorCode> errorCodeList = DocUtil.errorCodeDictToList(config);
if (CollectionUtil.isEmpty(errorCodeList)) {
mapper.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 1);
// set css cdn
setCssCDN(config, mapper);
if (DocLanguage.CHINESE.equals(config.getLanguage())) {
mapper.binding(TemplateVariable.CSS_CND.getVariable(), CSS_CDN_CH);
} else {
mapper.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 2);
mapper.binding(TemplateVariable.CSS_CND.getVariable(), CSS_CDN);
}
if (CollectionUtil.isNotEmpty(errorCodeList)) {
mapper.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 2);
} else {
mapper.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 1);
}
mapper.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
mapper.binding(TemplateVariable.VERSION.getVariable(), now);
mapper.binding(TemplateVariable.API_DOC_LIST.getVariable(), apiDocList);
@ -289,6 +322,8 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
List<ApiDocDict> directoryList = DocUtil.buildDictionary(config, javaProjectBuilder);
Template mapper = BeetlTemplateUtil.getByName(template);
setDirectoryLanguageVariable(config, mapper);
// set css cdn
setCssCDN(config, mapper);
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
mapper.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
mapper.binding(TemplateVariable.DICT_LIST.getVariable(), directoryList);
@ -307,7 +342,7 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
public void buildSingleApi(ProjectDocConfigBuilder projectBuilder, String controllerName, String template, String fileExtension) {
ApiConfig config = projectBuilder.getApiConfig();
FileUtil.mkdirs(config.getOutPath());
IDocBuildTemplate<ApiDoc> docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate<ApiDoc> docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
ApiDoc doc = docBuildTemplate.getSingleApiData(projectBuilder, controllerName);
Template mapper = BeetlTemplateUtil.getByName(template);
mapper.binding(TemplateVariable.DESC.getVariable(), doc.getDesc());
@ -321,7 +356,8 @@ public class DocBuilderTemplate extends BaseDocBuilderTemplate {
this.checkAndInitForGetApiData(config);
config.setMd5EncryptedHtmlName(true);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
return docBuildTemplate.getApiData(configBuilder);
}
}

View File

@ -23,10 +23,10 @@
package com.power.doc.builder;
import com.power.common.util.FileUtil;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.utils.BeetlTemplateUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import org.apache.commons.lang3.StringUtils;
@ -65,14 +65,19 @@ public class HtmlApiDocBuilder {
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
config.setParamsDataToTree(false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
Template indexCssTemplate = BeetlTemplateUtil.getByName(ALL_IN_ONE_CSS);
FileUtil.nioWriteFile(indexCssTemplate.render(), config.getOutPath() + FILE_SEPARATOR + ALL_IN_ONE_CSS);
FileUtil.nioWriteFile(indexCssTemplate.render(), config.getOutPath() + FILE_SEPARATOR + ALL_IN_ONE_CSS_OUT);
builderTemplate.copyJarFile("js/" + HIGH_LIGHT_JS, config.getOutPath() + FILE_SEPARATOR + HIGH_LIGHT_JS);
builderTemplate.copyJarFile("css/" + FONT_STYLE, config.getOutPath() + FILE_SEPARATOR + FONT_STYLE);
builderTemplate.copyJarFile("js/" + JQUERY, config.getOutPath() + FILE_SEPARATOR + JQUERY);
builderTemplate.copyJarFile("css/" + HIGH_LIGHT_STYLE, config.getOutPath() + FILE_SEPARATOR + HIGH_LIGHT_STYLE);
if (config.isAllInOne()) {
apiDocList = docBuildTemplate.handleApiGroup(apiDocList, config);
if (config.isCreateDebugPage()) {
INDEX_HTML = DEBUG_PAGE_ALL_TPL;
if (StringUtils.isNotEmpty(config.getAllInOneDocFileName())) {

View File

@ -22,16 +22,16 @@
*/
package com.power.doc.builder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.power.common.util.CollectionUtil;
import com.power.common.util.FileUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.Methods;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.*;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.utils.DocUtil;
import com.power.doc.utils.JsonUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.util.*;
@ -41,7 +41,6 @@ import java.util.*;
*/
public class OpenApiBuilder {
//过滤url中的特殊字符
private static final String PATH_REGEX = "[/{};\\t+]";
/**
@ -52,7 +51,7 @@ public class OpenApiBuilder {
public static void buildOpenApi(ApiConfig config) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config, false);
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
openApiCreate(config, configBuilder);
@ -66,7 +65,7 @@ public class OpenApiBuilder {
*/
public static void buildOpenApi(ApiConfig config, JavaProjectBuilder projectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config, false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, projectBuilder);
openApiCreate(config, configBuilder);
}
@ -79,7 +78,7 @@ public class OpenApiBuilder {
*/
private static void openApiCreate(ApiConfig config, ProjectDocConfigBuilder configBuilder) {
config.setParamsDataToTree(true);
SpringBootDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
Map<String, Object> json = new HashMap<>(8);
json.put("openapi", "3.0.3");
@ -90,15 +89,14 @@ public class OpenApiBuilder {
String filePath = config.getOutPath();
filePath = filePath + DocGlobalConstants.OPEN_API_JSON;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String data = gson.toJson(json);
String data = JsonUtil.toPrettyJson(json);
FileUtil.nioWriteFile(data, filePath);
}
/**
* info 信息
* Build openapi info
*
* @param apiConfig 文档配置信息
* @param apiConfig ApiConfig
* @return
*/
private static Map<String, Object> buildInfo(ApiConfig apiConfig) {
@ -109,9 +107,9 @@ public class OpenApiBuilder {
}
/**
* server 信息
* Build Servers
*
* @param config 文档配置信息
* @param config ApiConfig
* @return
*/
private static List<Map<String, Object>> buildServers(ApiConfig config) {
@ -123,9 +121,9 @@ public class OpenApiBuilder {
}
/**
* 构建path数据 url请求
* Build openapi paths
*
* @param apiDocList api列表
* @param apiDocList List of api
* @return
*/
private static Map<String, Object> buildPaths(List<ApiDoc> apiDocList) {
@ -135,7 +133,7 @@ public class OpenApiBuilder {
List<ApiMethodDoc> apiMethodDocs = a.getList();
apiMethodDocs.forEach(
method -> {
//设置paths的请求url 将双斜杠替换成单斜杠
//replace '//' to '/'
String url = method.getPath().replace("//", "/");
Map<String, Object> request = buildPathUrls(method, a);
//pathMap.put(method.getPath().replace("//", "/"), buildPathUrls(method, a));
@ -153,10 +151,10 @@ public class OpenApiBuilder {
}
/**
* paths 设置url请求方式
* Build path urls
*
* @param apiMethodDoc 方法参数
* @param apiDoc 类参数
* @param apiMethodDoc Method
* @param apiDoc ApiDoc
* @return
*/
private static Map<String, Object> buildPathUrls(ApiMethodDoc apiMethodDoc, ApiDoc apiDoc) {
@ -190,9 +188,9 @@ public class OpenApiBuilder {
}
/**
* 请求体构建 只针对post请求 并且请求体不为null
* Build request body
*
* @param apiMethodDoc 方法参数
* @param apiMethodDoc ApiMethodDoc
* @return
*/
private static Map<String, Object> buildRequestBody(ApiMethodDoc apiMethodDoc) {
@ -200,7 +198,7 @@ public class OpenApiBuilder {
boolean isPost = (apiMethodDoc.getType().equals(Methods.POST.getValue())
|| apiMethodDoc.getType().equals(Methods.PUT.getValue()) ||
apiMethodDoc.getType().equals(Methods.PATCH.getValue()));
//如果是post请求 且包含请求体
//add content of post method
if (isPost) {
requestBody.put("content", buildContent(apiMethodDoc, false));
return requestBody;
@ -242,19 +240,17 @@ public class OpenApiBuilder {
if (!isRep && apiMethodDoc.getContentType().equals(DocGlobalConstants.MULTIPART_TYPE)) {
// formdata
Map<String, Object> map = new LinkedHashMap<>();
if (apiMethodDoc.isListParam()) {
map.put("type", DocGlobalConstants.ARRAY);
} else {
map.put("type", "object");
}
map.put("type", "object");
Map<String, Object> properties = new LinkedHashMap<>();
Map<String, Object> detail;
for (ApiParam apiParam : apiMethodDoc.getQueryParams()) {
detail = new HashMap<>();
detail.put("type", apiParam.getType());
detail.put("description", apiParam.getDesc());
detail.put("example", DocUtil.handleJsonStr(apiParam.getValue()));
if ("file".equals(apiParam.getType())) {
detail.remove("example");
if (apiParam.isHasItems()) {
detail.put("type", "array");
Map<String, Object> items = new HashMap<>();
@ -263,6 +259,7 @@ public class OpenApiBuilder {
detail.put("items", items);
} else {
detail.put("format", "binary");
detail.put("type", "string");
}
}
properties.put(apiParam.getField(), detail);
@ -379,7 +376,7 @@ public class OpenApiBuilder {
}
//如果包含请求头
if (!CollectionUtil.isEmpty(apiMethodDoc.getRequestHeaders())) {
for (ApiReqHeader header : apiMethodDoc.getRequestHeaders()) {
for (ApiReqParam header : apiMethodDoc.getRequestHeaders()) {
parameters = new HashMap<>(20);
parameters.put("name", header.getName());
parameters.put("description", header.getDesc());
@ -419,6 +416,9 @@ public class OpenApiBuilder {
schema.put("format", "binary");
} else if ("enum".equals(apiParam.getType())) {
schema.put("enum", apiParam.getEnumValues());
}else if("array".equals(apiParam.getType())) {
schema.put("type","array");
schema.put("items",new HashMap<>());
}
} else {
schema.put("format", "int16".equals(apiParam.getType()) ? "int32" : apiParam.getType());
@ -432,7 +432,7 @@ public class OpenApiBuilder {
* @param header 参数信息
* @return
*/
private static Map<String, Object> buildParametersSchema(ApiReqHeader header) {
private static Map<String, Object> buildParametersSchema(ApiReqParam header) {
Map<String, Object> schema = new HashMap<>(10);
String openApiType = DocUtil.javaTypeToOpenApiTypeConvert(header.getType());
schema.put("type", openApiType);
@ -441,9 +441,9 @@ public class OpenApiBuilder {
}
/**
* 构建返回信息
* build response
*
* @param apiMethodDoc 方法参数
* @param apiMethodDoc ApiMethodDoc
* @return
*/
private static Map<String, Object> buildResponses(ApiMethodDoc apiMethodDoc) {
@ -453,9 +453,9 @@ public class OpenApiBuilder {
}
/**
* 构建返回信息实体
* response body
*
* @param apiMethodDoc 方法参数
* @param apiMethodDoc ApiMethodDoc
* @return
*/
private static Map<String, Object> buildResponsesBody(ApiMethodDoc apiMethodDoc) {
@ -466,9 +466,9 @@ public class OpenApiBuilder {
}
/**
* 构建component
* component schema
*
* @param apiDocs 请求列表
* @param apiDocs List of ApiDoc
* @return
*/
private static Map<String, Object> buildComponentsSchema(List<ApiDoc> apiDocs) {
@ -501,9 +501,9 @@ public class OpenApiBuilder {
}
/**
* component schema properties 信息
* component schema properties
*
* @param apiParam 参数列表
* @param apiParam list of ApiParam
* @return
*/
private static Map<String, Object> buildProperties(List<ApiParam> apiParam) {
@ -539,9 +539,9 @@ public class OpenApiBuilder {
}
/**
* component schema properties 实体信息构建
* component schema properties data
*
* @param apiParam 参数基本信息
* @param apiParam ApiParam
* @return
*/
private static Map<String, Object> buildPropertiesData(ApiParam apiParam) {

View File

@ -22,12 +22,11 @@
*/
package com.power.doc.builder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.power.common.util.CollectionUtil;
import com.power.common.util.FileUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.*;
import com.power.doc.model.postman.InfoBean;
import com.power.doc.model.postman.ItemBean;
@ -38,13 +37,12 @@ import com.power.doc.model.postman.request.RequestBean;
import com.power.doc.model.postman.request.body.BodyBean;
import com.power.doc.model.postman.request.header.HeaderBean;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.utils.PathUtil;
import com.power.doc.utils.DocPathUtil;
import com.power.doc.utils.JsonUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -57,13 +55,13 @@ public class PostmanJsonBuilder {
private static final String MSG = "Interface name is not set.";
/**
* 构建postman json
* build postman json
*
* @param config 配置文件
* @param config Smart-doc ApiConfig
*/
public static void buildPostmanCollection(ApiConfig config) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
postManCreate(config, configBuilder);
@ -77,17 +75,17 @@ public class PostmanJsonBuilder {
*/
public static void buildPostmanCollection(ApiConfig config, JavaProjectBuilder projectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,false);
config.setParamsDataToTree(false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, projectBuilder);
postManCreate(config, configBuilder);
}
/**
* 第一层的Item
* Build the first layer of Postman Item
*
* @param apiDoc
* @return
* @param apiDoc Documentation for each Controller
* @return First layer of Postman Item
*/
private static ItemBean buildItemBean(ApiDoc apiDoc) {
ItemBean itemBean = new ItemBean();
@ -105,10 +103,10 @@ public class PostmanJsonBuilder {
}
/**
* 构建第二层的item
* Build the second layer of Postman item
*
* @param apiMethodDoc
* @return
* @param apiMethodDoc Documentation for each method
* @return The second layer of Postman item
*/
private static ItemBean buildItem(ApiMethodDoc apiMethodDoc) {
ItemBean item = new ItemBean();
@ -130,37 +128,19 @@ public class PostmanJsonBuilder {
}
private static UrlBean buildUrlBean(ApiMethodDoc apiMethodDoc) {
UrlBean urlBean = new UrlBean();
UrlBean urlBean = new UrlBean(apiMethodDoc.getServerUrl());
String url = Optional.ofNullable(apiMethodDoc.getRequestExample().getUrl()).orElse(apiMethodDoc.getUrl());
urlBean.setRaw(PathUtil.toPostmanPath(url));
try {
URL javaUrl = new java.net.URL(apiMethodDoc.getServerUrl());
if (javaUrl.getPort() == -1) {
urlBean.setPort(null);
} else {
urlBean.setPort(String.valueOf(javaUrl.getPort()));
}
// set protocol
String protocol = javaUrl.getProtocol();
if (StringUtil.isNotEmpty(protocol)) {
urlBean.setProtocol(protocol);
}
List<String> hosts = new ArrayList<>();
hosts.add(javaUrl.getHost());
urlBean.setHost(hosts);
List<String> paths = new ArrayList<>();
paths.add(javaUrl.getPath());
urlBean.setPath(paths);
} catch (MalformedURLException e) {
}
String shortUrl = PathUtil.toPostmanPath(apiMethodDoc.getPath());
urlBean.setRaw(DocPathUtil.toPostmanPath(url));
String shortUrl = DocPathUtil.toPostmanPath(apiMethodDoc.getPath());
String[] paths = shortUrl.split("/");
List<String> pathList = new ArrayList<>();
if (CollectionUtil.isNotEmpty(urlBean.getPath()) && shortUrl.indexOf(urlBean.getPath().get(0)) < 0) {
pathList.add(urlBean.getPath().get(0).replaceAll("/", ""));
String serverPath = CollectionUtil.isNotEmpty(urlBean.getPath()) ? urlBean.getPath().get(0) : "";
// Add server path
if (CollectionUtil.isNotEmpty(urlBean.getPath()) && !shortUrl.contains(serverPath)) {
String[] serverPaths = serverPath.split("/");
pathList.addAll(Arrays.asList(serverPaths));
}
// Add mapping path
for (String str : paths) {
if (StringUtil.isNotEmpty(str)) {
pathList.add(str);
@ -193,21 +173,20 @@ public class PostmanJsonBuilder {
}
/**
* 构造请求体
* Build payload
*
* @param apiMethodDoc
* @return
* @return Body payload
*/
private static BodyBean buildBodyBean(ApiMethodDoc apiMethodDoc) {
BodyBean bodyBean;
if (apiMethodDoc.getContentType().contains(DocGlobalConstants.JSON_CONTENT_TYPE)) {
bodyBean = new BodyBean(false);
bodyBean = new BodyBean(Boolean.FALSE);// Json request
bodyBean.setMode(DocGlobalConstants.POSTMAN_MODE_RAW);
if (apiMethodDoc.getRequestExample() != null) {
bodyBean.setRaw(apiMethodDoc.getRequestExample().getJsonBody());
}
} else {
bodyBean = new BodyBean(true);
bodyBean = new BodyBean(Boolean.TRUE); //Formdata
bodyBean.setMode(DocGlobalConstants.POSTMAN_MODE_FORMDATA);
bodyBean.setFormdata(apiMethodDoc.getRequestExample().getFormDataList());
}
@ -216,15 +195,13 @@ public class PostmanJsonBuilder {
}
/**
* 构造请求头
* Build header
*
* @param apiMethodDoc
* @return
* @return List of header
*/
private static List<HeaderBean> buildHeaderBeanList(ApiMethodDoc apiMethodDoc) {
List<HeaderBean> headerBeans = new ArrayList<>();
List<ApiReqHeader> headers = apiMethodDoc.getRequestHeaders();
List<ApiReqParam> headers = apiMethodDoc.getRequestHeaders();
headers.forEach(
apiReqHeader -> {
HeaderBean headerBean = new HeaderBean();
@ -241,7 +218,7 @@ public class PostmanJsonBuilder {
}
private static void postManCreate(ApiConfig config, ProjectDocConfigBuilder configBuilder) {
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
RequestItem requestItem = new RequestItem();
requestItem.setInfo(new InfoBean(config.getProjectName()));
@ -255,8 +232,7 @@ public class PostmanJsonBuilder {
requestItem.setItem(itemBeans);
String filePath = config.getOutPath();
filePath = filePath + DocGlobalConstants.POSTMAN_JSON;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String data = gson.toJson(requestItem);
String data = JsonUtil.toPrettyJson(requestItem);
FileUtil.nioWriteFile(data, filePath);
}

View File

@ -136,7 +136,7 @@ public class ProjectDocConfigBuilder {
private void initCustomResponseFieldsMap(ApiConfig config) {
if (CollectionUtil.isNotEmpty(config.getCustomResponseFields())) {
for (CustomField field : config.getCustomResponseFields()) {
customRespFieldMap.put(field.getName(), field);
customRespFieldMap.put(field.getOwnerClassName() + "." + field.getName(), field);
}
}
}
@ -144,7 +144,7 @@ public class ProjectDocConfigBuilder {
private void initCustomRequestFieldsMap(ApiConfig config) {
if (CollectionUtil.isNotEmpty(config.getCustomRequestFields())) {
for (CustomField field : config.getCustomRequestFields()) {
customReqFieldMap.put(field.getName(), field);
customReqFieldMap.put(field.getOwnerClassName() + "." + field.getName(), field);
}
}
}
@ -193,32 +193,41 @@ public class ProjectDocConfigBuilder {
}
}
/**
* 设置高亮样式
*/
private void setHighlightStyle() {
String style = apiConfig.getStyle();
if (DocGlobalConstants.HIGH_LIGHT_DEFAULT_STYLE.equals(style)) {
// use local css file
apiConfig.setHighlightStyleLink(DocGlobalConstants.HIGH_LIGHT_CSS_DEFAULT);
return;
}
if (HighlightStyle.containsStyle(style)) {
apiConfig.setHighlightStyleLink(String.format(DocGlobalConstants.HIGH_LIGHT_CSS_URL_FORMAT, style));
return;
}
Random random = new Random();
if ("randomLight".equals(style)) {
if (DocGlobalConstants.HIGH_LIGHT_CSS_RANDOM_LIGHT.equals(style)) {
// Eliminate styles that do not match the template
style = HighlightStyle.randomLight(random);
if (HighlightStyle.containsStyle(style)) {
apiConfig.setStyle(style);
apiConfig.setHighlightStyleLink(String.format(DocGlobalConstants.HIGH_LIGHT_CSS_URL_FORMAT, style));
} else {
apiConfig.setStyle("null");
apiConfig.setStyle(null);
}
} else if ("randomDark".equals(style)) {
apiConfig.setStyle(HighlightStyle.randomDark(random));
} else if (DocGlobalConstants.HIGH_LIGHT_CSS_RANDOM_DARK.equals(style)) {
style = HighlightStyle.randomDark(random);
if (DocGlobalConstants.HIGH_LIGHT_DEFAULT_STYLE.equals(style)) {
apiConfig.setHighlightStyleLink(DocGlobalConstants.HIGH_LIGHT_CSS_DEFAULT);
} else {
apiConfig.setHighlightStyleLink(String.format(DocGlobalConstants.HIGH_LIGHT_CSS_URL_FORMAT, style));
}
apiConfig.setStyle(style);
} else {
// Eliminate styles that do not match the template
apiConfig.setStyle("null");
apiConfig.setStyle(null);
}
}
public JavaProjectBuilder getJavaProjectBuilder() {
return javaProjectBuilder;
}

View File

@ -23,15 +23,17 @@
package com.power.doc.builder;
import com.google.gson.Gson;
import com.power.common.util.CollectionUtil;
import com.power.common.util.OkHttp3Util;
import com.power.common.util.StringUtil;
import com.power.doc.constants.TornaConstants;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.torna.Apis;
import com.power.doc.model.torna.TornaApi;
import com.power.doc.model.torna.TornaDic;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.utils.DocUtil;
import com.power.doc.utils.TornaUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
@ -73,9 +75,11 @@ public class TornaBuilder {
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
config.setParamsDataToTree(true);
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
builderTemplate.checkAndInit(config,true);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
List<ApiDoc> apiDocList = new SpringBootDocBuildTemplate().getApiData(configBuilder);
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
apiDocList = docBuildTemplate.handleApiGroup(apiDocList, config);
buildTorna(apiDocList, config, javaProjectBuilder);
}
@ -84,37 +88,56 @@ public class TornaBuilder {
*
* @param apiDocs apiData
* @param apiConfig ApiConfig
* @param builder JavaProjectBuilder
* @param builder JavaProjectBuilder
*/
public static void buildTorna(List<ApiDoc> apiDocs, ApiConfig apiConfig, JavaProjectBuilder builder) {
TornaApi tornaApi = new TornaApi();
tornaApi.setAuthor(StringUtil.isEmpty(apiConfig.getAuthor()) ? System.getProperty("user.name") : apiConfig.getAuthor());
tornaApi.setIsReplace((apiConfig.getReplace() == null || apiConfig.getReplace()) ? 1 : 0);
Apis api;
List<Apis> apisList = new ArrayList<>();
//添加接口数据
for (ApiDoc a : apiDocs) {
List<Apis> groupApiList = new ArrayList<>();
//Convert ApiDoc to Apis
for (ApiDoc groupApi : apiDocs) {
List<Apis> apisList = new ArrayList<>();
List<ApiDoc> childrenApiDocs = groupApi.getChildrenApiDocs();
for (ApiDoc a : childrenApiDocs) {
api = new Apis();
api.setName(StringUtils.isBlank(a.getDesc()) ? a.getName() : a.getDesc());
api.setItems(buildApis(a.getList(), TornaUtil.setDebugEnv(apiConfig, tornaApi)));
api.setIsFolder(TornaConstants.YES);
api.setAuthor(a.getAuthor());
api.setOrderIndex(a.getOrder());
apisList.add(api);
}
api = new Apis();
api.setName(StringUtils.isBlank(a.getDesc()) ? a.getName() : a.getDesc());
api.setItems(buildApis(a.getList(), TornaUtil.setDebugEnv(apiConfig, tornaApi)));
api.setName(StringUtils.isBlank(groupApi.getDesc()) ? groupApi.getName() : groupApi.getDesc());
api.setAuthor(tornaApi.getAuthor());
api.setOrderIndex(groupApi.getOrder());
api.setIsFolder(TornaConstants.YES);
api.setAuthor(a.getAuthor());
apisList.add(api);
api.setItems(apisList);
groupApiList.add(api);
}
tornaApi.setCommonErrorCodes(buildErrorCode(apiConfig));
tornaApi.setApis(apisList);
//推送文档信息
// delete default group when only default group
tornaApi.setApis(groupApiList.size() == 1 ? groupApiList.get(0).getItems() : groupApiList);
//Build push document information
Map<String, String> requestJson = TornaConstants.buildParams(PUSH, new Gson().toJson(tornaApi), apiConfig);
//推送字典信息
//Push dictionary information
Map<String, Object> dicMap = new HashMap<>(2);
List<TornaDic> docDicts = TornaUtil.buildTornaDic(DocUtil.buildDictionary(apiConfig, builder));
dicMap.put("enums", docDicts);
Map<String, String> dicRequestJson = TornaConstants.buildParams(ENUM_PUSH, new Gson().toJson(dicMap), apiConfig);
//获取返回结果
if (CollectionUtil.isNotEmpty(docDicts)) {
dicMap.put("enums", docDicts);
Map<String, String> dicRequestJson = TornaConstants.buildParams(ENUM_PUSH, new Gson().toJson(dicMap), apiConfig);
String dicResponseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(dicRequestJson));
TornaUtil.printDebugInfo(apiConfig, dicResponseMsg, dicRequestJson, ENUM_PUSH);
}
//Get the response result
String responseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(requestJson));
String dicResponseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(dicRequestJson));
//开启调试时打印请求信息
//Print the log of pushing documents to Torna
TornaUtil.printDebugInfo(apiConfig, responseMsg, requestJson, PUSH);
TornaUtil.printDebugInfo(apiConfig, dicResponseMsg, dicRequestJson, ENUM_PUSH);
}
}

View File

@ -1,264 +0,0 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.builder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.power.common.util.FileUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.TemplateVariable;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiParam;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.utils.BeetlTemplateUtil;
import com.power.doc.utils.Iterables;
import com.thoughtworks.qdox.JavaProjectBuilder;
import org.beetl.core.Template;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.power.doc.constants.DocGlobalConstants.YAPI_RESULT_TPL;
/**
* generate yapi's yapi json
*
* @author dai19470 2020/08/20.
*/
public class YapiJsonBuilder {
/**
* 构建yapi json
*
* @param config 配置文件
*/
public static void buildYapiCollection(ApiConfig config) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
yapiJsonCreate(config, configBuilder);
}
/**
* Only for smart-doc maven plugin and gradle plugin.
*
* @param config ApiConfig Object
* @param projectBuilder QDOX avaProjectBuilder
*/
public static void buildYapiCollection(ApiConfig config, JavaProjectBuilder projectBuilder) {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, projectBuilder);
yapiJsonCreate(config, configBuilder);
}
private static Set<String> getUrl(String url, String patter) {
Pattern pattern = Pattern.compile(patter);
Matcher matcher = pattern.matcher(url);
Set<String> result = new HashSet<>();
while (matcher.find()) {
result.add(matcher.group());
}
return result;
}
private static void yapiJsonCreate(ApiConfig config, ProjectDocConfigBuilder configBuilder) {
config.setParamsDataToTree(true);
IDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
List<Map<String, Object>> requestItem = new ArrayList<>();
Iterables.forEach(apiDocList, (index, apiDoc) -> {
Map<String, Object> module = new HashMap<>();
module.put("index", index);
module.put("name", apiDoc.getDesc());
module.put("parent_id", -1);
module.put("desc", apiDoc.getDesc());
module.put("add_time", System.currentTimeMillis() / 1000);
module.put("up_time", System.currentTimeMillis() / 1000);
List<Map<String, Object>> methods = new ArrayList();
Iterables.forEach(apiDoc.getList(), (idx, apiMethodDoc) -> {
Map<String, Object> method = new HashMap<>();
Map<String, Object> map = new HashMap<>();
map.put("path", apiMethodDoc.getPath());
map.put("params", new Object[]{});
method.put("query_path", map);
// method.put("owners",new String[]{apiMethodDoc.getAuthor()});
method.put("owners", new String[]{});
method.put("edit_uid", 0);
method.put("status", "done");
method.put("type", "static");
method.put("req_body_is_json_schema", true);
method.put("res_body_is_json_schema", true);
method.put("api_opened", false);
method.put("index", idx);
method.put("tag", new Object[]{});
method.put("method", apiMethodDoc.getType());
method.put("title", apiMethodDoc.getDesc());
method.put("desc", apiMethodDoc.getDetail());
method.put("name", apiMethodDoc.getName());
method.put("path", apiMethodDoc.getPath().replace("//", "/"));
method.put("req_body_form", Arrays.asList());
List<Map<String, Object>> req_params = new ArrayList();
Set<String> req_param = getUrl(apiMethodDoc.getPath(), "(?<=\\{)(.+?)(?=\\})");
Iterables.forEach(req_param, (j, param) -> {
ApiParam temp = apiMethodDoc.getRequestParams().stream().filter(apiParam -> apiParam.getField().equals(param)).findFirst().orElse(null);
if (temp != null) {
Map<String, Object> h = new HashMap<>();
h.put("example", "");
h.put("name", temp.getField());
h.put("type", temp.getType());
h.put("desc", temp.getDesc());
req_params.add(j, h);
}
});
method.put("req_params", req_params);
method.put("res_body_type", "json");
List<Map<String, Object>> querys = new ArrayList();
Iterables.forEach(apiMethodDoc.getRequestParams(), (j, res) -> {
Map<String, Object> h = new HashMap<>();
h.put("required", res.isRequired() ? "1" : "0");
h.put("desc", res.getDesc());
h.put("name", res.getField());
h.put("example", "");
h.put("type", res.getType());
querys.add(j, h);
});
method.put("req_query", querys);
List<Map<String, Object>> headers = new ArrayList();
Iterables.forEach(apiMethodDoc.getRequestHeaders(), (j, res) -> {
Map<String, Object> h = new HashMap<>();
h.put("required", res.isRequired() ? "1" : "0");
h.put("value", res.getValue());
h.put("name", res.getName());
h.put("desc", res.getDesc());
headers.add(j, h);
});
method.put("req_headers", headers);
Template apiTemplate = BeetlTemplateUtil.getByName(YAPI_RESULT_TPL);
apiTemplate.binding(TemplateVariable.RESPONSE_LIST.getVariable(), generateJson(apiMethodDoc.getResponseParams()));
String json = apiTemplate.render();
method.put("res_body", json);
if (StringUtil.isNotEmpty(apiMethodDoc.getResponseUsage())) {
method.put("desc", "<pre><code>\n" + apiMethodDoc.getResponseUsage() + "\n</code></pre>\n");
}
methods.add(idx, method);
});
module.put("list", methods);
requestItem.add(module);
});
String filePath = config.getOutPath();
filePath = filePath + DocGlobalConstants.YAPI_JSON;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String data = gson.toJson(requestItem);
FileUtil.nioWriteFile(data, filePath);
}
private static String generateJson(List<ApiParam> responseParams) {
StringBuffer re = new StringBuffer("\"type\":\"object\",\n\"properties\":{\n");
HashSet<String> required = new HashSet<>();
responseParams.stream().forEach(apiParam -> {
re.append(getTypeAndPropertiesJson(apiParam));
if (apiParam.isRequired()) {
required.add(apiParam.getField());
}
});
Gson gs = new Gson();
re.append("\"required\":\"" + gs.toJson(required.toArray()) + "\"");
re.append("\t}");
return re.toString();
}
/**
* 将字段类型转换为yapi的字段类型
*
* @param type java type
* @return String
*/
private static String changeType(String type) {
switch (type) {
case "boolean":
return "boolean";
case "int32":
return "integer";
case "int64":
return "number";
default:
return type;
}
}
/**
* 单个参数拼接字符串
*
* @param param ApiParam
* @return String
*/
private static String getTypeAndPropertiesJson(ApiParam param) {
StringBuffer resultJson = new StringBuffer();
resultJson.append("\"" + param.getField() + "\":{");
resultJson.append("\"type\":\"" + changeType(param.getType()) + "\", ");
if (param.getChildren() != null && param.getChildren().size() > 0) {
if (param.getType().equals("object")) {
resultJson.append(" \"properties\":{");
param.getChildren().forEach(child -> {
resultJson.append(getTypeAndPropertiesJson(child));
});
} else if (param.getType().equals("array")) {
resultJson.append(" \"items\":{");
resultJson.append("\"type\":\"object\",\n\"properties\":{\n");
param.getChildren().forEach(child -> {
resultJson.append(getTypeAndPropertiesJson(child));
});
resultJson.append("\t},");
}
resultJson.append("},");
}
resultJson.append("},");
return resultJson.toString();
}
}

View File

@ -23,9 +23,11 @@
package com.power.doc.builder.rpc;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.template.RpcDocBuildTemplate;
import com.power.doc.template.IDocBuildTemplate;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.util.List;
@ -58,15 +60,17 @@ public class RpcAdocBuilder {
* @param javaProjectBuilder ProjectDocConfigBuilder
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
config.setFramework(FrameworkEnum.DUBBO.getFramework());
config.setAdoc(true);
config.setShowJavaType(true);
RpcDocBuilderTemplate builderTemplate = new RpcDocBuilderTemplate();
builderTemplate.checkAndInit(config);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
RpcDocBuildTemplate docBuildTemplate = new RpcDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<RpcApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
if (config.isAllInOne()) {
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, RPC_ALL_IN_ONE_ADOC_TPL, INDEX_DOC);
String docName = builderTemplate.allInOneDocName(config, INDEX_DOC, ".adoc");
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, RPC_ALL_IN_ONE_ADOC_TPL, docName);
} else {
builderTemplate.buildApiDoc(apiDocList, config, RPC_API_DOC_ADOC_TPL, API_EXTENSION);
builderTemplate.buildErrorCodeDoc(config, ERROR_CODE_LIST_ADOC_TPL, ERROR_CODE_LIST_ADOC);

View File

@ -22,6 +22,8 @@
*/
package com.power.doc.builder.rpc;
import com.power.common.util.StringUtil;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.rpc.RpcApiAllData;
import com.thoughtworks.qdox.JavaProjectBuilder;
@ -39,6 +41,9 @@ public class RpcApiDataBuilder {
*/
public static RpcApiAllData getApiData(ApiConfig config) {
config.setShowJavaType(true);
if (StringUtil.isEmpty(config.getFramework())) {
config.setFramework(FrameworkEnum.DUBBO.getFramework());
}
RpcDocBuilderTemplate builderTemplate = new RpcDocBuilderTemplate();
builderTemplate.checkAndInitForGetApiData(config);
JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();

View File

@ -25,15 +25,17 @@ package com.power.doc.builder.rpc;
import com.power.common.util.CollectionUtil;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.FileUtil;
import com.power.common.util.StringUtil;
import com.power.doc.builder.BaseDocBuilderTemplate;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.constants.TemplateVariable;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiErrorCode;
import com.power.doc.model.rpc.RpcApiAllData;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.RpcDocBuildTemplate;
import com.power.doc.utils.BeetlTemplateUtil;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
@ -56,7 +58,10 @@ public class RpcDocBuilderTemplate extends BaseDocBuilderTemplate {
private static long now = System.currentTimeMillis();
public void checkAndInit(ApiConfig config) {
super.checkAndInit(config);
if (StringUtil.isEmpty(config.getFramework())) {
config.setFramework(FrameworkEnum.DUBBO.getFramework());
}
super.checkAndInit(config,false);
config.setOutPath(config.getOutPath() + FILE_SEPARATOR + RPC_OUT_DIR);
}
@ -112,6 +117,7 @@ public class RpcDocBuilderTemplate extends BaseDocBuilderTemplate {
tpl.binding(TemplateVariable.PROJECT_NAME.getVariable(), config.getProjectName());
tpl.binding(TemplateVariable.RPC_CONSUMER_CONFIG.getVariable(), rpcConfigConfigContent);
setDirectoryLanguageVariable(config, tpl);
setCssCDN(config, tpl);
FileUtil.nioWriteFile(tpl.render(), outPath + FILE_SEPARATOR + outPutFileName);
}
@ -188,7 +194,7 @@ public class RpcDocBuilderTemplate extends BaseDocBuilderTemplate {
this.checkAndInitForGetApiData(config);
config.setMd5EncryptedHtmlName(true);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new RpcDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
return docBuildTemplate.getApiData(configBuilder);
}

View File

@ -24,8 +24,11 @@ package com.power.doc.builder.rpc;
import com.power.common.util.FileUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.RpcDocBuildTemplate;
import com.power.doc.utils.BeetlTemplateUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
@ -68,10 +71,12 @@ public class RpcHtmlBuilder {
RpcDocBuilderTemplate builderTemplate = new RpcDocBuilderTemplate();
builderTemplate.checkAndInit(config);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
RpcDocBuildTemplate docBuildTemplate = new RpcDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<RpcApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
Template indexCssTemplate = BeetlTemplateUtil.getByName(ALL_IN_ONE_CSS);
FileUtil.nioWriteFile(indexCssTemplate.render(), config.getOutPath() + FILE_SEPARATOR + ALL_IN_ONE_CSS);
FileUtil.nioWriteFile(indexCssTemplate.render(), config.getOutPath() + FILE_SEPARATOR + ALL_IN_ONE_CSS_OUT);
builderTemplate.copyJarFile("css/" + FONT_STYLE, config.getOutPath() + FILE_SEPARATOR + FONT_STYLE);
builderTemplate.copyJarFile("js/" + JQUERY, config.getOutPath() + FILE_SEPARATOR + JQUERY);
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, RPC_ALL_IN_ONE_HTML_TPL, INDEX_HTML);
builderTemplate.buildSearchJs(apiDocList, config, RPC_ALL_IN_ONE_SEARCH_TPL, SEARCH_JS);
}

View File

@ -24,10 +24,10 @@ package com.power.doc.builder.rpc;
import com.power.common.util.DateTimeUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.RpcDocBuildTemplate;
import com.thoughtworks.qdox.JavaProjectBuilder;
import java.util.List;
@ -54,23 +54,24 @@ public class RpcMarkdownBuilder {
/**
* Only for smart-doc maven plugin and gradle plugin.
*
* @param config ApiConfig
* @param apiConfig ApiConfig
* @param javaProjectBuilder ProjectDocConfigBuilder
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
config.setAdoc(false);
config.setShowJavaType(true);
public static void buildApiDoc(ApiConfig apiConfig, JavaProjectBuilder javaProjectBuilder) {
apiConfig.setAdoc(false);
apiConfig.setShowJavaType(true);
RpcDocBuilderTemplate builderTemplate = new RpcDocBuilderTemplate();
builderTemplate.checkAndInit(config);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = new RpcDocBuildTemplate();
builderTemplate.checkAndInit(apiConfig);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(apiConfig, javaProjectBuilder);
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(apiConfig.getFramework());
List<RpcApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
if (config.isAllInOne()) {
String version = config.isCoverOld() ? "" : "-V" + DateTimeUtil.long2Str(System.currentTimeMillis(), DATE_FORMAT);
builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, RPC_ALL_IN_ONE_MD_TPL, "rpc-all" + version + ".md");
if (apiConfig.isAllInOne()) {
String version = apiConfig.isCoverOld() ? "" : "-V" + DateTimeUtil.long2Str(System.currentTimeMillis(), DATE_FORMAT);
String docName = builderTemplate.allInOneDocName(apiConfig, "rpc-all" + version, ".md");
builderTemplate.buildAllInOne(apiDocList, apiConfig, javaProjectBuilder, RPC_ALL_IN_ONE_MD_TPL, docName);
} else {
builderTemplate.buildApiDoc(apiDocList, config, RPC_API_DOC_MD_TPL, API_EXTENSION);
builderTemplate.buildErrorCodeDoc(config, ERROR_CODE_LIST_MD_TPL, ERROR_CODE_LIST_MD);
builderTemplate.buildApiDoc(apiDocList, apiConfig, RPC_API_DOC_MD_TPL, API_EXTENSION);
builderTemplate.buildErrorCodeDoc(apiConfig, ERROR_CODE_LIST_MD_TPL, ERROR_CODE_LIST_MD);
}
}
}

View File

@ -23,16 +23,19 @@
package com.power.doc.builder.rpc;
import com.google.gson.Gson;
import com.power.common.util.CollectionUtil;
import com.power.common.util.OkHttp3Util;
import com.power.common.util.StringUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.TornaConstants;
import com.power.doc.factory.BuildTemplateFactory;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.model.torna.Apis;
import com.power.doc.model.torna.DubboInfo;
import com.power.doc.model.torna.TornaApi;
import com.power.doc.model.torna.TornaDic;
import com.power.doc.template.IDocBuildTemplate;
import com.power.doc.template.RpcDocBuildTemplate;
import com.power.doc.utils.DocUtil;
import com.power.doc.utils.TornaUtil;
@ -76,14 +79,15 @@ public class RpcTornaBuilder {
RpcDocBuilderTemplate builderTemplate = new RpcDocBuilderTemplate();
builderTemplate.checkAndInit(config);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
RpcDocBuildTemplate docBuildTemplate = new RpcDocBuildTemplate();
IDocBuildTemplate docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
List<RpcApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
buildTorna(apiDocList, config,javaProjectBuilder);
buildTorna(apiDocList, config, javaProjectBuilder);
}
public static void buildTorna(List<RpcApiDoc> apiDocs, ApiConfig apiConfig,JavaProjectBuilder builder) {
public static void buildTorna(List<RpcApiDoc> apiDocs, ApiConfig apiConfig, JavaProjectBuilder builder) {
TornaApi tornaApi = new TornaApi();
tornaApi.setAuthor(StringUtil.isEmpty(apiConfig.getAuthor()) ? System.getProperty("user.name") : apiConfig.getAuthor());
tornaApi.setIsReplace((apiConfig.getReplace() == null || apiConfig.getReplace()) ? 1 : 0);
Apis api;
List<Apis> apisList = new ArrayList<>();
//添加接口数据
@ -100,23 +104,28 @@ public class RpcTornaBuilder {
.setVersion(a.getVersion())
.setDependency(TornaUtil.buildDependencies(apiConfig.getRpcApiDependencies()))
.setInterfaceName(a.getName()));
api.setOrderIndex(a.getOrder());
apisList.add(api);
}
tornaApi.setCommonErrorCodes(buildErrorCode(apiConfig));
tornaApi.setApis(apisList);
//推送文档信息
//Build push document information
Map<String, String> requestJson = TornaConstants.buildParams(PUSH, new Gson().toJson(tornaApi), apiConfig);
//推送字典信息
Map<String,Object> dicMap = new HashMap<>(2);
List<TornaDic> docDicts =TornaUtil.buildTornaDic(DocUtil.buildDictionary(apiConfig,builder));
dicMap.put("enums",docDicts);
//Push dictionary information
Map<String, Object> dicMap = new HashMap<>(2);
List<TornaDic> docDicts = TornaUtil.buildTornaDic(DocUtil.buildDictionary(apiConfig, builder));
if (CollectionUtil.isNotEmpty(docDicts)) {
dicMap.put("enums", docDicts);
Map<String, String> dicRequestJson = TornaConstants.buildParams(ENUM_PUSH, new Gson().toJson(dicMap), apiConfig);
String dicResponseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(dicRequestJson));
TornaUtil.printDebugInfo(apiConfig, dicResponseMsg, dicRequestJson, ENUM_PUSH);
}
Map<String, String> dicRequestJson = TornaConstants.buildParams(ENUM_PUSH, new Gson().toJson(dicMap), apiConfig);
//获取返回结果
//Get the response result
String responseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(requestJson));
String dicResponseMsg = OkHttp3Util.syncPostJson(apiConfig.getOpenUrl(), new Gson().toJson(dicRequestJson));
//开启调试时打印请求信息
TornaUtil.printDebugInfo(apiConfig, responseMsg, requestJson,PUSH);
TornaUtil.printDebugInfo(apiConfig, dicResponseMsg, dicRequestJson,ENUM_PUSH);
//Print the log of pushing documents to Torna
TornaUtil.printDebugInfo(apiConfig, responseMsg, requestJson, PUSH);
}
}

View File

@ -0,0 +1,32 @@
package com.power.doc.constants;
/**
* @author chen qi 2021-07-15 10:55
**/
public enum ApiReqParamInTypeEnum {
/**
* header param
*/
HEADER("header"),
/**
* query param
*/
QUERY("query"),
/**
* path param
*/
PATH("path");
private final String value;
ApiReqParamInTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}

View File

@ -39,6 +39,11 @@ public interface DocAnnotationConstants {
String SHORT_JSON_IGNORE = "JsonIgnore";
/**
* jackson JsonIgnoreProperties annotation
*/
String SHORT_JSON_IGNORE_PROPERTIES = "JsonIgnoreProperties";
String SHORT_JSON_PROPERTY = "JsonProperty";
String SHORT_JSON_FIELD = "JSONField";
@ -66,4 +71,20 @@ public interface DocAnnotationConstants {
String JSON_CREATOR = "JsonCreator";
String MAX = "max";
String SIZE = "size";
String LENGTH = "length";
String JSON_PROPERTY = "JsonProperty";
/**
* Fastjson JSONType annotation
*/
String SHORT_JSON_TYPE = "JSONType";
/**
* Fastjson JSONType annotation ignores prop
*/
String IGNORE_PROP = "ignores";
}

View File

@ -91,7 +91,17 @@ public interface DocGlobalConstants {
String SINGLE_DICT_HTML_TPL = "html/dict.html";
String ALL_IN_ONE_CSS = "AllInOne.css";
String ALL_IN_ONE_CSS = "css/AllInOne.css";
String ALL_IN_ONE_CSS_OUT = "AllInOne.css";
String FONT_STYLE = "font.css";
String HIGH_LIGHT_JS = "highlight.min.js";
String JQUERY = "jquery.min.js";
String HIGH_LIGHT_STYLE = "xt256.min.css";
String RPC_OUT_DIR = "rpc";
@ -137,10 +147,16 @@ public interface DocGlobalConstants {
String MODE_AND_VIEW_FULLY = "org.springframework.web.servlet.ModelAndView";
String FEIGN_CLIENT_FULLY = "org.springframework.cloud.netflix.feign.FeignClient";
String FEIGN_CLIENT = "FeignClient";
String MULTIPART_FILE_FULLY = "org.springframework.web.multipart.MultipartFile";
String JAVA_OBJECT_FULLY = "java.lang.Object";
String JAVA_BOOLEAN = "java.lang.Boolean";
String JAVA_STRING_FULLY = "java.lang.String";
String JAVA_MAP_FULLY = "java.util.Map";
@ -202,4 +218,59 @@ public interface DocGlobalConstants {
String DUBBO_SWAGGER = "org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerApiListingResource";
String ARRAY = "array";
String OBJECT = "object";
String JSON_PROPERTY_READ_WRITE = "JsonProperty.Access.READ_WRITE";
String JSON_PROPERTY_READ_ONLY = "JsonProperty.Access.READ_ONLY";
String JSON_PROPERTY_WRITE_ONLY = "JsonProperty.Access.WRITE_ONLY";
String CSS_CDN_CH = "https://fonts.googleapis.cnpmjs.org";
String CSS_CDN = "https://fonts.googleapis.com";
String PATH_DELIMITER = "/";
/**
* JAX-RS@PATH
*/
String JAX_PATH_FULLY = "javax.ws.rs.Path";
/**
* JAX-RS@Produces
*/
String JAX_PRODUCES_FULLY = "javax.ws.rs.Produces";
/**
* JAX-RS@Produces
*/
String JAX_CONSUMES_FULLY = "javax.ws.rs.Consumes";
/**
* JAX-RS@GET
*/
String JAX_GET_FULLY = "javax.ws.rs.GET";
/**
* JAX-RS@POST
*/
String JAX_POST_FULLY = "javax.ws.rs.POST";
/**
* JAX-RS@PUT
*/
String JAX_PUT_FULLY = "javax.ws.rs.PUT";
/**
* JAX-RS@DELETE
*/
String JAXB_DELETE_FULLY = "javax.ws.rs.DELETE";
String HIGH_LIGHT_CSS_URL_FORMAT = "https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/styles/%s.min.css";
String HIGH_LIGHT_DEFAULT_STYLE = "xt256";
String HIGH_LIGHT_CSS_DEFAULT = "xt256.min.css";
String HIGH_LIGHT_CSS_RANDOM_LIGHT = "randomLight";
String HIGH_LIGHT_CSS_RANDOM_DARK = "randomDark";
}

View File

@ -114,4 +114,19 @@ public interface DocTags {
String IGNORE_RESPONSE_BODY_ADVICE = "ignoreResponseBodyAdvice";
String IGNORE_REQUEST_BODY_ADVICE = "ignoreRequestBodyAdvice";
/**
* response tag @since 2.2.0
*/
String API_RESPONSE = "response";
/**
* custom @tag
*/
String TAG = "tag";
/**
* custom @dubboRest tag
*/
String DUBBO_REST = "dubboRest";
}

View File

@ -0,0 +1,87 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.constants;
import com.power.common.util.StringUtil;
/**
* Smart-doc Supported Framework
*
* @author yu 2021/6/27.
*/
public enum FrameworkEnum {
/**
* Apache Dubbo
*/
DUBBO("dubbo", "com.power.doc.template.RpcDocBuildTemplate"),
/**
* Spring Framework
*/
SPRING("spring", "com.power.doc.template.SpringBootDocBuildTemplate"),
/**
* JAX-RS
*/
JAX_RS("JAX-RS", "com.power.doc.template.JaxrsDocBuildTemplate");
/**
* Framework name
*/
private String framework;
/**
* Framework IDocBuildTemplate implement
*/
private String className;
FrameworkEnum(String framework, String className) {
this.framework = framework;
this.className = className;
}
public static String getClassNameByFramework(String framework) {
String className = "";
if (StringUtil.isEmpty(framework)) {
return className;
}
for (FrameworkEnum e : FrameworkEnum.values()) {
if (e.framework.equalsIgnoreCase(framework)) {
className = e.className;
break;
}
}
return className;
}
public String getFramework() {
return framework;
}
public String getClassName() {
return className;
}
}

View File

@ -229,7 +229,7 @@ public class HighlightStyle {
/**
* 随机一个 light style
* Randomly select a light style
*
* @param random Random
* @return String of random
@ -239,7 +239,7 @@ public class HighlightStyle {
}
/**
* 随机一个 dark style
* Randomly select a dark style
*
* @param random Random
* @return String of random
@ -249,7 +249,7 @@ public class HighlightStyle {
}
/**
* 随机一个 style
* Randomly select a style
*
* @param random Random
* @return String of random
@ -271,10 +271,9 @@ public class HighlightStyle {
}
/**
* |
* 高亮样式是否存在
* Does the highlight style exist?
*
* @param style 高亮样式
* @param style Highlight style
* @return boolean
*/
public static boolean containsStyle(String style) {

View File

@ -0,0 +1,54 @@
package com.power.doc.constants;
/**
* JAX-RS Annotations
*
* @author Zxq
*/
public interface JAXRSAnnotations {
/**
* JAX-RS@PATH
*/
String JAX_PATH = "Path";
/**
* JAX-RS@Produces
*/
String JAX_PRODUCES = "Produces";
/**
* JAX-RS@HeaderParam
*/
String JAX_HEADER_PARAM = "HeaderParam";
/**
* JAX-RS@PathParam
*/
String JAX_PATH_PARAM = "PathParam";
/**
* JAX-RS@QueryParam
*/
String JAX_QUERY_PARAM = "QueryParam";
/**
* JAX-RS@FormParam
*/
String JAX_FORM_PARAM = "FormParam";
/**
* JAX-RS@Consumes
*/
String JAX_CONSUMES = "Consumes";
/**
* JAX-RS@GET
*/
String GET = "GET";
/**
* JAX-RS@POST
*/
String POST = "POST";
/**
* JAX-RS@PUT
*/
String PUT = "PUT";
/**
* JAX-RS@DELETE
*/
String DELETE = "DELETE";
}

View File

@ -50,4 +50,8 @@ public interface SpringMvcAnnotations {
String REST_CONTROLLER = "RestController";
String PATH_VARIABLE = "PathVariable";
String SESSION_ATTRIBUTE = "SessionAttribute";
String REQUEST_ATTRIBUTE="RequestAttribute";
}

View File

@ -51,10 +51,14 @@ public enum TemplateVariable {
RPC_CONSUMER_CONFIG("consumerConfigExample"),
REQUEST_EXAMPLE("isRequestExample"),
RESPONSE_EXAMPLE("isResponseExample"),
DISPLAY_REQUEST_PARAMS("displayRequestParams"),
DISPLAY_RESPONSE_PARAMS("displayResponseParams"),
RESPONSE_LIST("respList"),
ORDER("order"),
INDEX_ALIAS("alias"),
DIRECTORY_TREE("directoryTree");
DIRECTORY_TREE("directoryTree"),
HIGH_LIGHT_CSS_LINK("highlightCssLink"),
CSS_CND("css_cdn");
private String variable;

View File

@ -72,7 +72,7 @@ public class TornaConstants {
if (StringUtils.isNotBlank(data)) {
data = URLEncoder.encode(data, "utf-8");
}
// 公共参数
// Public request parameters for pushing documents to Torna
param.put("name", name);
param.put("app_key", config.getAppKey());
param.put("data", data);
@ -112,23 +112,16 @@ public class TornaConstants {
}
/**
* 生成md5,全部大写
* Generate md5 and convert to uppercase
*
* @param message 消息
* @param message message
* @return String
*/
public static String md5(String message) {
try {
// 1 创建一个提供信息摘要算法的对象初始化为md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 2 将消息变成byte数组
byte[] input = message.getBytes();
// 3 计算后获得字节数组,这就是那128位了
byte[] buff = md.digest(input);
// 4 把数组每一字节一个字节占八位换成16进制连成md5字符串
return byte2hex(buff);
} catch (Exception e) {
throw new RuntimeException(e);
@ -136,8 +129,7 @@ public class TornaConstants {
}
/**
* 二进制转十六进制字符串
*
* Convert byte array to hex
* @param bytes byte array
* @return String
*/

View File

@ -0,0 +1,53 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.factory;
import com.power.doc.constants.FrameworkEnum;
import com.power.doc.template.IDocBuildTemplate;
/**
* @author yu 2021/6/27.
*/
public class BuildTemplateFactory {
/**
* Get Doc build template
*
* @param framework framework name
* @param <T> API doc type
* @return Implements of IDocBuildTemplate
*/
public static <T> IDocBuildTemplate<T> getDocBuildTemplate(String framework) {
String className = FrameworkEnum.getClassNameByFramework(framework);
try {
return (IDocBuildTemplate) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
throw new RuntimeException("The class=>" + className + " is not found , smart-doc currently supported framework name can only be set in [dubbo, spring].");
}
return null;
}
}

View File

@ -0,0 +1,39 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.function;
import org.beetl.core.Context;
import org.beetl.core.Function;
/**
* @author yu 2021/6/26.
*/
public class HtmlEscape implements Function {
@Override
public String call(Object[] paras, Context ctx) {
String str = String.valueOf(paras[0]).replaceAll("&", "&amp;")
.replaceAll("\"","&quot;");
return str;
}
}

View File

@ -0,0 +1,18 @@
package com.power.doc.function;
import org.beetl.core.Context;
import org.beetl.core.Function;
/**
* @author yu 2021/7/24.
*/
public class RemoveLineBreaks implements Function {
@Override
public String call(Object[] paras, Context ctx) {
String str = String.valueOf(paras[0])
.replaceAll("\n", " ")
.replaceAll("\r"," ");
return str;
}
}

View File

@ -0,0 +1,65 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.handler;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.JAXRSAnnotations;
import com.power.doc.model.ApiReqParam;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaMethod;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Zxq
*/
public class JaxrsHeaderHandler {
/**
* Handle JAX RS Header
* @param method method
* @param projectBuilder ProjectDocConfigBuilder
* @return list of ApiReqParam
*/
public List<ApiReqParam> handle(JavaMethod method, ProjectDocConfigBuilder projectBuilder) {
List<JavaAnnotation> annotations = method.getAnnotations();
List<ApiReqParam> ApiReqParams = new ArrayList<>();
for (JavaAnnotation annotation : annotations) {
// hit target head annotation
if (JAXRSAnnotations.JAX_HEADER_PARAM.equals(annotation.getType().getName())) {
ApiReqParam ApiReqParam = new ApiReqParam();
// Obtain header value
ApiReqParam.setValue(DocUtil.getRequestHeaderValue(annotation).replaceAll("\"", ""));
ApiReqParam.setName(DocUtil.getRequestHeaderValue(annotation).replaceAll("\"", ""));
ApiReqParam.setType("string");
ApiReqParam.setDesc("desc");
ApiReqParams.add(ApiReqParam);
}
}
return ApiReqParams;
}
}

View File

@ -0,0 +1,144 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.handler;
import com.power.common.util.StringUtil;
import com.power.common.util.UrlUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.*;
import com.power.doc.model.request.JaxrsPathMapping;
import com.power.doc.utils.DocUrlUtil;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaMethod;
import java.util.*;
import static com.power.doc.constants.DocTags.DEPRECATED;
import static com.power.doc.constants.DocTags.IGNORE;
/**
* dubbo Rest 注解处理器
*
* @author Zxq
*/
public class JaxrsPathHandler {
/**
* ANNOTATION_NAMES
*/
private static final Set<String> ANNOTATION_NAMES = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList(JAXRSAnnotations.DELETE,
DocGlobalConstants.JAXB_DELETE_FULLY,
JAXRSAnnotations.PUT, DocGlobalConstants.JAX_PUT_FULLY,
JAXRSAnnotations.GET, DocGlobalConstants.JAX_GET_FULLY,
JAXRSAnnotations.POST, DocGlobalConstants.JAX_POST_FULLY
)));
Map<String, String> constantsMap;
public JaxrsPathMapping handle(ProjectDocConfigBuilder projectBuilder, String baseUrl, JavaMethod method) {
List<JavaAnnotation> annotations = method.getAnnotations();
this.constantsMap = projectBuilder.getConstantsMap();
String url;
String methodType = null;
String shortUrl = null;
String mediaType = null;
String serverUrl = projectBuilder.getServerUrl();
String contextPath = projectBuilder.getApiConfig().getPathPrefix();
boolean deprecated = false;
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getName();
if (JAXRSAnnotations.JAX_PRODUCES.equals(annotationName)) {
mediaType = DocUtil.getRequestHeaderValue(annotation);
}
// Deprecated annotation on method
if (DocAnnotationConstants.DEPRECATED.equals(annotationName)) {
deprecated = true;
}
if (JAXRSAnnotations.JAX_PATH.equals(annotationName) ||
JAXRSAnnotations.JAX_PATH_PARAM.equals(annotationName) ||
DocGlobalConstants.JAX_PATH_FULLY
.equals(annotationName)) {
shortUrl = DocUtil.handleMappingValue(annotation);
}
if (ANNOTATION_NAMES.contains(annotationName)) {
methodType = annotationName;
}
}
// @deprecated tag on method
if (Objects.nonNull(method.getTagByName(DEPRECATED))) {
deprecated = true;
}
JaxrsPathMapping jaxrsPathMapping = getJaxbPathMapping(projectBuilder, baseUrl, method, shortUrl, serverUrl, contextPath);
if (jaxrsPathMapping != null) {
return jaxrsPathMapping.setDeprecated(deprecated)
.setMethodType(methodType)
.setMediaType(mediaType);
}
return null;
}
private JaxrsPathMapping getJaxbPathMapping(ProjectDocConfigBuilder projectBuilder,
String baseUrl, JavaMethod method,
String shortUrl,
String serverUrl,
String contextPath) {
String url;
if (Objects.nonNull(shortUrl)) {
if (Objects.nonNull(method.getTagByName(IGNORE))) {
return null;
}
shortUrl = StringUtil.removeQuotes(shortUrl);
List<String> urls = DocUtil.split(shortUrl);
url = String.join(DocGlobalConstants.PATH_DELIMITER, serverUrl, contextPath, baseUrl, shortUrl);
shortUrl = String.join(DocGlobalConstants.PATH_DELIMITER, DocGlobalConstants.PATH_DELIMITER, contextPath, baseUrl, shortUrl);
if (urls.size() > 1) {
url = DocUrlUtil.getMvcUrls(serverUrl, contextPath + "/" + baseUrl, urls);
shortUrl = DocUrlUtil.getMvcUrls(DocGlobalConstants.EMPTY, contextPath + "/" + baseUrl, urls);
}
for (Map.Entry<String, String> entry : constantsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (url.contains(key)) {
url = url.replace(key, value).replace("+", "");
}
if (shortUrl.contains(key)) {
shortUrl = shortUrl.replace(key, value).replace("+", "");
}
}
String urlSuffix = projectBuilder.getApiConfig().getUrlSuffix();
url = UrlUtil.simplifyUrl(url);
shortUrl = UrlUtil.simplifyUrl(shortUrl);
if (StringUtil.isNotEmpty(urlSuffix)) {
url += urlSuffix;
shortUrl += urlSuffix;
}
return JaxrsPathMapping.builder()
.setUrl(StringUtil.trim(url))
.setShortUrl(StringUtil.trim(shortUrl));
}
return null;
}
}

View File

@ -23,10 +23,11 @@
package com.power.doc.handler;
import com.power.common.util.StringUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.DocAnnotationConstants;
import com.power.doc.constants.DocTags;
import com.power.doc.constants.SpringMvcAnnotations;
import com.power.doc.model.ApiReqHeader;
import com.power.doc.model.ApiReqParam;
import com.power.doc.utils.DocClassUtil;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.model.JavaAnnotation;
@ -45,11 +46,13 @@ public class SpringMVCRequestHeaderHandler {
/**
* handle Spring MVC Request Header
*
* @param method JavaMethod
* @param method JavaMethod
* @param projectBuilder projectBuilder
* @return list of ApiReqHeader
*/
public List<ApiReqHeader> handle(JavaMethod method) {
List<ApiReqHeader> mappingHeaders = new ArrayList<>();
public List<ApiReqParam> handle(JavaMethod method, ProjectDocConfigBuilder projectBuilder) {
Map<String, String> constantsMap = projectBuilder.getConstantsMap();
List<ApiReqParam> mappingHeaders = new ArrayList<>();
List<JavaAnnotation> annotations = method.getAnnotations();
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getValue();
@ -71,20 +74,31 @@ public class SpringMVCRequestHeaderHandler {
processMappingHeaders(header, mappingHeaders);
}
}
List<ApiReqHeader> reqHeaders = new ArrayList<>();
List<ApiReqParam> reqHeaders = new ArrayList<>();
for (JavaParameter javaParameter : method.getParameters()) {
List<JavaAnnotation> javaAnnotations = javaParameter.getAnnotations();
String className = method.getDeclaringClass().getCanonicalName();
Map<String, String> paramMap = DocUtil.getParamsComments(method, DocTags.PARAM, className);
String paramName = javaParameter.getName();
ApiReqHeader apiReqHeader;
ApiReqParam apiReqHeader;
for (JavaAnnotation annotation : javaAnnotations) {
String annotationName = annotation.getType().getValue();
if (SpringMvcAnnotations.REQUEST_HERDER.equals(annotationName)) {
apiReqHeader = new ApiReqHeader();
apiReqHeader = new ApiReqParam();
Map<String, Object> requestHeaderMap = annotation.getNamedParameterMap();
if (requestHeaderMap.get(DocAnnotationConstants.VALUE_PROP) != null) {
apiReqHeader.setName(StringUtil.removeQuotes((String) requestHeaderMap.get(DocAnnotationConstants.VALUE_PROP)));
String attribute = DocUtil.handleRequestHeaderValue(annotation);
String constValue = ((String) requestHeaderMap.get(DocAnnotationConstants.VALUE_PROP)).replaceAll("\"", "");
if (StringUtil.isEmpty(attribute)) {
apiReqHeader.setName(constValue);
} else {
Object value = constantsMap.get(attribute);
if (value == null) {
apiReqHeader.setName(constValue);
} else {
apiReqHeader.setName((String) value);
}
}
} else {
apiReqHeader.setName(paramName);
}
@ -100,7 +114,8 @@ public class SpringMVCRequestHeaderHandler {
}
apiReqHeader.setDesc(desc.toString());
if (requestHeaderMap.get(DocAnnotationConstants.REQUIRED_PROP) != null) {
apiReqHeader.setRequired(!Boolean.FALSE.toString().equals(requestHeaderMap.get(DocAnnotationConstants.REQUIRED_PROP)));
apiReqHeader.setRequired(!Boolean.FALSE.toString()
.equals(requestHeaderMap.get(DocAnnotationConstants.REQUIRED_PROP)));
} else {
apiReqHeader.setRequired(true);
}
@ -111,8 +126,10 @@ public class SpringMVCRequestHeaderHandler {
}
}
}
List<ApiReqHeader> allApiReqHeaders = Stream.of(mappingHeaders, reqHeaders)
.flatMap(Collection::stream).distinct().collect(Collectors.toList());
List<ApiReqParam> allApiReqHeaders = Stream.of(mappingHeaders, reqHeaders)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
return allApiReqHeaders;
}
@ -130,11 +147,15 @@ public class SpringMVCRequestHeaderHandler {
}
}
public void processMappingHeaders(String header, List<ApiReqHeader> mappingHeaders) {
public void processMappingHeaders(String header, List<ApiReqParam> mappingHeaders) {
if (header.contains("!=")) {
String headerName = header.substring(0, header.indexOf("!"));
ApiReqHeader apiReqHeader = ApiReqHeader.builder().setName(headerName)
.setRequired(true).setValue(null).setDesc("header condition").setType("string");
ApiReqParam apiReqHeader = ApiReqParam.builder()
.setName(headerName)
.setRequired(true)
.setValue(null)
.setDesc("header condition")
.setType("string");
mappingHeaders.add(apiReqHeader);
} else {
String headerName;
@ -146,8 +167,12 @@ public class SpringMVCRequestHeaderHandler {
} else {
headerName = header;
}
ApiReqHeader apiReqHeader = ApiReqHeader.builder().setName(headerName)
.setRequired(true).setValue(headerValue).setDesc("header condition").setType("string");
ApiReqParam apiReqHeader = ApiReqParam.builder()
.setName(headerName)
.setRequired(true)
.setValue(headerValue)
.setDesc("header condition")
.setType("string");
mappingHeaders.add(apiReqHeader);
}
}

View File

@ -35,7 +35,6 @@ import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaMethod;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -64,6 +63,7 @@ public class SpringMVCRequestMappingHandler {
String shortUrl = null;
String mediaType = null;
String serverUrl = projectBuilder.getServerUrl();
String contextPath = projectBuilder.getApiConfig().getPathPrefix();
boolean deprecated = false;
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getName();
@ -108,37 +108,45 @@ public class SpringMVCRequestMappingHandler {
return null;
}
shortUrl = StringUtil.removeQuotes(shortUrl);
String[] urls = shortUrl.split(",");
if (urls.length > 1) {
url = DocUrlUtil.getMvcUrls(serverUrl, controllerBaseUrl, Arrays.asList(urls));
shortUrl = DocUrlUtil.getMvcUrls("", controllerBaseUrl, Arrays.asList(urls));
List<String> urls = DocUtil.split(shortUrl);
if (urls.size() > 1) {
url = DocUrlUtil.getMvcUrls(serverUrl, contextPath + "/" + controllerBaseUrl, urls);
shortUrl = DocUrlUtil.getMvcUrls(DocGlobalConstants.EMPTY, contextPath + "/" + controllerBaseUrl, urls);
} else {
url = serverUrl + "/" + controllerBaseUrl + "/" + shortUrl;
shortUrl = "/" + controllerBaseUrl + "/" + shortUrl;
url = String.join(DocGlobalConstants.PATH_DELIMITER, serverUrl, contextPath, controllerBaseUrl, shortUrl);
shortUrl = String.join(DocGlobalConstants.PATH_DELIMITER, DocGlobalConstants.PATH_DELIMITER, contextPath, controllerBaseUrl, shortUrl);
}
for (Map.Entry<String, String> entry : constantsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (url.contains(key)) {
url = url.replace(key, value);
url = url.replace("+", "");
}
if (shortUrl.contains(key)) {
shortUrl = shortUrl.replace(key, value);
shortUrl = shortUrl.replace("+", "");
}
url = delConstantsUrl(url, key, value);
shortUrl = delConstantsUrl(shortUrl, key, value);
}
String urlSuffix = projectBuilder.getApiConfig().getUrlSuffix();
if (StringUtil.isNotEmpty(urlSuffix)) {
url = UrlUtil.simplifyUrl(url) + urlSuffix;
shortUrl = UrlUtil.simplifyUrl(shortUrl) + urlSuffix;
url = UrlUtil.simplifyUrl(StringUtil.trim(url)) + urlSuffix;
shortUrl = UrlUtil.simplifyUrl(StringUtil.trim(shortUrl)) + urlSuffix;
} else {
url = UrlUtil.simplifyUrl(url);
shortUrl = UrlUtil.simplifyUrl(shortUrl);
url = UrlUtil.simplifyUrl(StringUtil.trim(url));
shortUrl = UrlUtil.simplifyUrl(StringUtil.trim(shortUrl));
}
return RequestMapping.builder().setMediaType(mediaType).setMethodType(methodType)
.setUrl(StringUtil.trim(url)).setShortUrl(StringUtil.trim(shortUrl)).setDeprecated(deprecated);
.setUrl(url).setShortUrl(shortUrl).setDeprecated(deprecated);
}
return null;
}
public static String delConstantsUrl(String url, String replaceKey, String replaceValue) {
url = StringUtil.trim(url);
url = url.replace("+", "");
url = UrlUtil.simplifyUrl(url);
String[] pathWords = url.split("/");
for (String word : pathWords) {
if (word.equals(replaceKey)) {
url = url.replace(replaceKey, replaceValue);
return url;
}
}
return url;
}
}

View File

@ -92,6 +92,9 @@ public class FormDataBuildHelper {
if (JavaClassValidateUtil.isArray(gicName)) {
gicName = gicName.substring(0, gicName.indexOf("["));
}
if (JavaClassValidateUtil.isPrimitive(gicName)) {
pre = pre.substring(0, pre.lastIndexOf("."));
}
formDataList.addAll(getFormData(gicName, registryClasses, counter, builder, pre + "[]"));
}
int n = 0;
@ -101,7 +104,7 @@ public class FormDataBuildHelper {
String fieldName = field.getName();
String subTypeName = docField.getFullyQualifiedName();
String fieldGicName = docField.getGenericCanonicalName();
JavaClass javaClass = builder.getJavaProjectBuilder().getClassByName(subTypeName);
JavaClass javaClass = field.getType();
if (field.isStatic() || "this$0".equals(fieldName) ||
JavaClassValidateUtil.isIgnoreFieldTypes(subTypeName)) {
continue;
@ -152,6 +155,9 @@ public class FormDataBuildHelper {
formDataList.add(formData);
} else if (javaClass.isEnum()) {
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.TRUE);
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
value = tagsMap.get(DocTags.MOCK);
}
FormData formData = new FormData();
formData.setKey(pre + fieldName);
formData.setType("text");

View File

@ -22,18 +22,21 @@
*/
package com.power.doc.helper;
import com.power.common.util.JsonFormatUtil;
import com.power.common.util.StringUtil;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.DocAnnotationConstants;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.DocTags;
import com.power.doc.constants.ValidatorAnnotations;
import com.power.doc.model.*;
import com.power.doc.utils.*;
import com.thoughtworks.qdox.model.*;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import java.util.*;
import static com.power.doc.constants.DocGlobalConstants.JSON_PROPERTY_READ_ONLY;
import static com.power.doc.constants.DocGlobalConstants.JSON_PROPERTY_WRITE_ONLY;
import static com.power.doc.constants.DocTags.IGNORE_RESPONSE_BODY_ADVICE;
@ -51,21 +54,45 @@ public class JsonBuildHelper {
*/
public static String buildReturnJson(DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder) {
JavaMethod method = docJavaMethod.getJavaMethod();
if (method.getReturns().isVoid() && Objects.isNull(builder.getApiConfig().getResponseBodyAdvice())) {
return "Doesn't return a value.";
String responseBodyAdvice = null;
if (Objects.nonNull(builder.getApiConfig().getResponseBodyAdvice())) {
responseBodyAdvice = builder.getApiConfig().getResponseBodyAdvice().getClassName();
}
if (method.getReturns().isVoid() && Objects.isNull(responseBodyAdvice)) {
return "Return void.";
}
DocletTag downloadTag = method.getTagByName(DocTags.DOWNLOAD);
if (Objects.nonNull(downloadTag)) {
return "File download.";
}
if (method.getReturns().isEnum() && Objects.isNull(responseBodyAdvice)) {
return StringUtil.removeQuotes(String.valueOf(JavaClassUtil.getEnumValue(method.getReturns(), Boolean.FALSE)));
}
if (method.getReturns().isPrimitive() && Objects.isNull(responseBodyAdvice)) {
String typeName = method.getReturnType().getCanonicalName();
return StringUtil.removeQuotes(DocUtil.jsonValueByType(typeName));
}
if (DocGlobalConstants.JAVA_STRING_FULLY.equals(method.getReturnType().getGenericCanonicalName())
&& Objects.isNull(responseBodyAdvice)) {
return "string";
}
String returnTypeGenericCanonicalName = method.getReturnType().getGenericCanonicalName();
if (Objects.nonNull(builder.getApiConfig().getResponseBodyAdvice())
if (Objects.nonNull(responseBodyAdvice)
&& Objects.isNull(method.getTagByName(IGNORE_RESPONSE_BODY_ADVICE))) {
String responseBodyAdvice = builder.getApiConfig().getResponseBodyAdvice().getClassName();
StringBuilder sb = new StringBuilder();
sb.append(responseBodyAdvice)
.append("<")
.append(returnTypeGenericCanonicalName).append(">");
returnTypeGenericCanonicalName = sb.toString();
if (!returnTypeGenericCanonicalName.startsWith(responseBodyAdvice)) {
StringBuilder sb = new StringBuilder();
sb.append(responseBodyAdvice)
.append("<")
.append(returnTypeGenericCanonicalName).append(">");
returnTypeGenericCanonicalName = sb.toString();
}
}
ApiReturn apiReturn = DocClassUtil.processReturnType(returnTypeGenericCanonicalName);
String typeName = apiReturn.getSimpleName();
if (JavaClassValidateUtil.isFileDownloadResource(typeName)) {
docJavaMethod.setDownload(true);
return "File download.";
}
Map<String, JavaType> actualTypesMap = docJavaMethod.getActualTypesMap();
String returnType = apiReturn.getGenericCanonicalName();
if (Objects.nonNull(actualTypesMap)) {
@ -80,7 +107,10 @@ public class JsonBuildHelper {
}
return StringUtil.removeQuotes(DocUtil.jsonValueByType(typeName));
}
return JsonFormatUtil.formatJson(buildJson(typeName, returnType, Boolean.TRUE, 0, new HashMap<>(), builder));
return JsonUtil.toPrettyFormat(buildJson(typeName, returnType, Boolean.TRUE, 0,
new HashMap<>(), new ArrayList<>(0), builder));
}
/**
@ -89,13 +119,13 @@ public class JsonBuildHelper {
* @param isResp Response flag
* @param counter Recursive counter
* @param registryClasses class container
* @param groupClasses valid group class
* @param builder project config builder
* @return String
*/
public static String buildJson(String typeName, String genericCanonicalName,
boolean isResp, int counter, Map<String, String> registryClasses, ProjectDocConfigBuilder builder) {
boolean isResp, int counter, Map<String, String> registryClasses, List<String> groupClasses, ProjectDocConfigBuilder builder) {
//存储泛型所对应的实体类
Map<String, String> genericMap = new HashMap<>(10);
JavaClass javaClass = builder.getJavaProjectBuilder().getClassByName(typeName);
ApiConfig apiConfig = builder.getApiConfig();
@ -118,16 +148,21 @@ public class JsonBuildHelper {
return StringUtil.removeQuotes(DocUtil.jsonValueByType(typeName));
}
if (javaClass.isEnum()) {
return String.valueOf(JavaClassUtil.getEnumValue(javaClass, Boolean.FALSE));
return StringUtil.removeQuotes(String.valueOf(JavaClassUtil.getEnumValue(javaClass, Boolean.FALSE)));
}
boolean skipTransientField = apiConfig.isSkipTransientField();
StringBuilder data0 = new StringBuilder();
JavaClass cls = builder.getClassByName(typeName);
data0.append("{");
String[] globGicName = DocClassUtil.getSimpleGicName(genericCanonicalName);
//添加泛型对应关系
if (Objects.isNull(globGicName) || globGicName.length < 1) {
// obtain generics from parent class
JavaClass superJavaClass = cls != null ? cls.getSuperJavaClass() : null;
if (Objects.nonNull(superJavaClass) && !"Object".equals(superJavaClass.getSimpleName())) {
globGicName = DocClassUtil.getSimpleGicName(superJavaClass.getGenericFullyQualifiedName());
}
}
JavaClassUtil.genericParamMap(genericMap, cls, globGicName);
StringBuilder data = new StringBuilder();
if (JavaClassValidateUtil.isCollection(typeName) || JavaClassValidateUtil.isArray(typeName)) {
@ -146,12 +181,12 @@ public class JsonBuildHelper {
data.append(DocUtil.jsonValueByType(gName));
} else if (gName.contains("<")) {
String simple = DocClassUtil.getSimpleName(gName);
String json = buildJson(simple, gName, isResp, nextLevel, registryClasses, builder);
String json = buildJson(simple, gName, isResp, nextLevel, registryClasses, groupClasses, builder);
data.append(json);
} else if (JavaClassValidateUtil.isCollection(gName)) {
data.append("\"any object\"");
} else {
String json = buildJson(gName, gName, isResp, nextLevel, registryClasses, builder);
String json = buildJson(gName, gName, isResp, nextLevel, registryClasses, groupClasses, builder);
data.append(json);
}
data.append("]");
@ -168,40 +203,41 @@ public class JsonBuildHelper {
}
String gicName = gNameTemp.substring(gNameTemp.indexOf(",") + 1, gNameTemp.lastIndexOf(">"));
if (DocGlobalConstants.JAVA_OBJECT_FULLY.equals(gicName)) {
data.append("{").append("\"mapKey\":").append("{\"waring\":\"You may use java.util.Object for Map value; smart-doc can't be handle.\"}").append("}");
data.append("{").append("\"mapKey\":").append("{\"waring\":\"You may use java.util.Object for Map value; smart-doc can't be handle.\"}")
.append("}");
} else if (JavaClassValidateUtil.isPrimitive(gicName)) {
data.append("{").append("\"mapKey1\":").append(DocUtil.jsonValueByType(gicName)).append(",");
data.append("\"mapKey2\":").append(DocUtil.jsonValueByType(gicName)).append("}");
} else if (gicName.contains("<")) {
String simple = DocClassUtil.getSimpleName(gicName);
String json = buildJson(simple, gicName, isResp, nextLevel, registryClasses, builder);
String json = buildJson(simple, gicName, isResp, nextLevel, registryClasses, groupClasses, builder);
data.append("{").append("\"mapKey\":").append(json).append("}");
} else {
data.append("{").append("\"mapKey\":").append(buildJson(gicName, gNameTemp, isResp, counter + 1, registryClasses, builder)).append("}");
data.append("{").append("\"mapKey\":").append(buildJson(gicName, gNameTemp, isResp, counter + 1, registryClasses, groupClasses, builder)).append("}");
}
return data.toString();
} else if (DocGlobalConstants.JAVA_OBJECT_FULLY.equals(typeName)) {
data.append("{\"object\":\" any object\"},");
// throw new RuntimeException("Please do not return java.lang.Object directly in api interface.");
} else if (JavaClassValidateUtil.isReactor(typeName)) {
data.append(buildJson(globGicName[0], typeName, isResp, nextLevel, registryClasses, builder));
data.append(buildJson(globGicName[0], typeName, isResp, nextLevel, registryClasses, groupClasses, builder));
return data.toString();
} else {
boolean requestFieldToUnderline = builder.getApiConfig().isRequestFieldToUnderline();
boolean responseFieldToUnderline = builder.getApiConfig().isResponseFieldToUnderline();
List<DocJavaField> fields = JavaClassUtil.getFields(cls, 0, new LinkedHashMap<>());
Map<String, String> ignoreFields = JavaClassUtil.getClassJsonIgnoreFields(cls);
out:
for (DocJavaField docField : fields) {
JavaField field = docField.getJavaField();
String subTypeName = docField.getFullyQualifiedName();
String fieldName = field.getName();
if (field.isStatic() || "this$0".equals(fieldName) ||
JavaClassValidateUtil.isIgnoreFieldTypes(subTypeName)) {
continue;
}
if (field.isTransient() && skipTransientField) {
continue;
}
String fieldName = docField.getFieldName();
if (ignoreFields.containsKey(fieldName)) {
continue;
}
String subTypeName = docField.getFullyQualifiedName();
if ((responseFieldToUnderline && isResp) || (requestFieldToUnderline && !isResp)) {
fieldName = StringUtil.camelToUnderline(fieldName);
}
@ -214,6 +250,25 @@ public class JsonBuildHelper {
List<JavaAnnotation> annotations = docField.getAnnotations();
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getValue();
if (ValidatorAnnotations.NULL.equals(annotationName) && !isResp) {
List<String> groupClassList = JavaClassUtil.getParamGroupJavaClass(annotation);
for (String groupClass : groupClassList) {
if (groupClasses.contains(groupClass)) {
continue out;
}
}
}
if (DocAnnotationConstants.JSON_PROPERTY.equalsIgnoreCase(annotationName)) {
AnnotationValue value = annotation.getProperty("access");
if (Objects.nonNull(value)) {
if (JSON_PROPERTY_READ_ONLY.equals(value.getParameterValue()) && !isResp) {
continue out;
}
if (JSON_PROPERTY_WRITE_ONLY.equals(value.getParameterValue()) && isResp) {
continue out;
}
}
}
if (DocAnnotationConstants.SHORT_JSON_IGNORE.equals(annotationName)) {
continue out;
} else if (DocAnnotationConstants.SHORT_JSON_FIELD.equals(annotationName)) {
@ -232,12 +287,13 @@ public class JsonBuildHelper {
}
String typeSimpleName = field.getType().getSimpleName();
String fieldGicName = docField.getGenericCanonicalName();
CustomField customResponseField = builder.getCustomRespFieldMap().get(fieldName);
CustomField customRequestField = builder.getCustomReqFieldMap().get(fieldName);
if (customRequestField != null && typeName.equals(customRequestField.getOwnerClassName()) && (customRequestField.isIgnore()) && !isResp) {
CustomField customResponseField = builder.getCustomRespFieldMap().get(typeName + "." + fieldName);
CustomField customRequestField = builder.getCustomReqFieldMap().get(typeName + "." + fieldName);
if (customRequestField != null && JavaClassUtil.isTargetChildClass(typeName, customRequestField.getOwnerClassName()) && (customRequestField.isIgnore()) && !isResp) {
continue;
}
if (customResponseField != null && typeName.equals(customResponseField.getOwnerClassName()) && (customResponseField.isIgnore()) && isResp) {
if (customResponseField != null && JavaClassUtil.isTargetChildClass(typeName, customResponseField.getOwnerClassName()) && (customResponseField.isIgnore()) && isResp) {
continue;
}
data0.append("\"").append(fieldName).append("\":");
@ -256,10 +312,10 @@ public class JsonBuildHelper {
if (StringUtil.isEmpty(fieldValue)) {
fieldValue = DocUtil.getValByTypeAndFieldName(typeSimpleName, field.getName());
}
if (null != customRequestField && !isResp && customRequestField.getOwnerClassName().equals(typeName)) {
if (Objects.nonNull(customRequestField) && !isResp && typeName.equals(customRequestField.getOwnerClassName())) {
JavaFieldUtil.buildCustomField(data0, typeSimpleName, customRequestField);
}
if (null != customResponseField && isResp && customResponseField.getOwnerClassName().equals(typeName)) {
if (Objects.nonNull(customResponseField) && isResp && typeName.equals(customResponseField.getOwnerClassName())) {
JavaFieldUtil.buildCustomField(data0, typeSimpleName, customResponseField);
}
if (data0.length() == data0Length) {
@ -293,7 +349,8 @@ public class JsonBuildHelper {
data0.append("[").append(DocUtil.jsonValueByType(gicName1)).append("]").append(",");
} else {
if (!typeName.equals(gicName1)) {
data0.append("[").append(buildJson(DocClassUtil.getSimpleName(gicName1), gicName1, isResp, nextLevel, registryClasses, builder)).append("]").append(",");
data0.append("[").append(buildJson(DocClassUtil.getSimpleName(gicName1), gicName1, isResp, nextLevel, registryClasses, groupClasses, builder))
.append("]").append(",");
} else {
data0.append("[{\"$ref\":\"..\"}]").append(",");
}
@ -310,7 +367,7 @@ public class JsonBuildHelper {
data0.append("[").append(value).append("],");
continue out;
}
data0.append("[").append(buildJson(gicName, fieldGicName, isResp, nextLevel, registryClasses, builder)).append("]").append(",");
data0.append("[").append(buildJson(gicName, fieldGicName, isResp, nextLevel, registryClasses, groupClasses, builder)).append("]").append(",");
} else {
data0.append("[{\"$ref\":\"..\"}]").append(",");
}
@ -326,18 +383,27 @@ public class JsonBuildHelper {
}
String gicName = fieldGicName.substring(fieldGicName.indexOf(",") + 1, fieldGicName.indexOf(">"));
if (gicName.length() == 1) {
String gicName1 = genericMap.get(gicName) == null ? globGicName[0] : genericMap.get(gicName);
String gicName1 = "";
if (Objects.nonNull(genericMap.get(gicName))) {
gicName1 = genericMap.get(gicName);
} else {
if (globGicName.length > 0) {
gicName1 = globGicName[0];
}
}
if (DocGlobalConstants.JAVA_STRING_FULLY.equals(gicName1)) {
data0.append("{").append("\"mapKey\":").append(DocUtil.jsonValueByType(gicName1)).append("},");
} else {
if (!typeName.equals(gicName1)) {
data0.append("{").append("\"mapKey\":").append(buildJson(DocClassUtil.getSimpleName(gicName1), gicName1, isResp, nextLevel, registryClasses, builder)).append("},");
data0.append("{").append("\"mapKey\":")
.append(buildJson(DocClassUtil.getSimpleName(gicName1), gicName1, isResp, nextLevel, registryClasses, groupClasses, builder)).append("},");
} else {
data0.append("{\"mapKey\":{}},");
}
}
} else {
data0.append("{").append("\"mapKey\":").append(buildJson(gicName, fieldGicName, isResp, nextLevel, registryClasses, builder)).append("},");
data0.append("{").append("\"mapKey\":").append(buildJson(gicName, fieldGicName, isResp, nextLevel, registryClasses, groupClasses, builder))
.append("},");
}
} else if (subTypeName.length() == 1) {
if (!typeName.equals(genericCanonicalName)) {
@ -346,10 +412,10 @@ public class JsonBuildHelper {
data0.append(DocUtil.jsonValueByType(gicName)).append(",");
} else {
String simple = DocClassUtil.getSimpleName(gicName);
data0.append(buildJson(simple, gicName, isResp, nextLevel, registryClasses, builder)).append(",");
data0.append(buildJson(simple, gicName, isResp, nextLevel, registryClasses, groupClasses, builder)).append(",");
}
} else {
data0.append("{\"waring\":\"You may have used non-display generics.\"},");
data0.append("{},");
}
} else if (DocGlobalConstants.JAVA_OBJECT_FULLY.equals(subTypeName)) {
if (StringUtil.isNotEmpty(field.getComment())) {
@ -359,26 +425,32 @@ public class JsonBuildHelper {
String gicName = genericMap.get(subTypeName) == null ? globGicName[0] : genericMap.get(subTypeName);
if (!typeName.equals(genericCanonicalName)) {
if (JavaClassValidateUtil.isPrimitive(gicName)) {
data0.append("\"").append(buildJson(gicName, genericCanonicalName, isResp, nextLevel, registryClasses, builder)).append("\",");
data0.append("\"").append(buildJson(gicName, genericCanonicalName, isResp, nextLevel, registryClasses, groupClasses, builder)).append("\",");
} else {
String simpleName = DocClassUtil.getSimpleName(gicName);
data0.append(buildJson(simpleName, gicName, isResp, nextLevel, registryClasses, builder)).append(",");
data0.append(buildJson(simpleName, gicName, isResp, nextLevel, registryClasses, groupClasses, builder)).append(",");
}
} else {
data0.append("{\"waring\":\"You may have used non-display generics.\"},");
data0.append("{},");
}
} else {
data0.append("{\"waring\":\"You may have used non-display generics.\"},");
data0.append("{},");
}
} else if (typeName.equals(subTypeName)) {
data0.append("{\"$ref\":\"...\"}").append(",");
} else {
javaClass = builder.getJavaProjectBuilder().getClassByName(subTypeName);
if (!isResp && javaClass.isEnum()) {
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.FALSE);
data0.append(value).append(",");
javaClass = field.getType();
if (javaClass.isEnum()) {
// Override old value
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
data0.append(tagsMap.get(DocTags.MOCK)).append(",");
} else {
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.FALSE);
data0.append(value).append(",");
}
} else {
data0.append(buildJson(subTypeName, fieldGicName, isResp, nextLevel, registryClasses, builder)).append(",");
fieldGicName = DocUtil.formatFieldTypeGicName(genericMap, globGicName, fieldGicName);
data0.append(buildJson(subTypeName, fieldGicName, isResp, nextLevel, registryClasses, groupClasses, builder)).append(",");
}
}
}
@ -390,6 +462,4 @@ public class JsonBuildHelper {
data0.append("}");
return data0.toString();
}
}

View File

@ -39,6 +39,7 @@ import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
@ -54,8 +55,7 @@ public class ParamsBuildHelper {
public static List<ApiParam> buildParams(String className, String pre, int level, String isRequired, boolean isResp,
Map<String, String> registryClasses, ProjectDocConfigBuilder projectBuilder,
List<String> groupClasses, int pid, boolean jsonRequest) {
String maxLength = null;
//存储泛型所对应的实体类
Map<String, String> genericMap = new HashMap<>(10);
if (StringUtil.isEmpty(className)) {
@ -82,7 +82,14 @@ public class ParamsBuildHelper {
String simpleName = DocClassUtil.getSimpleName(className);
String[] globGicName = DocClassUtil.getSimpleGicName(className);
JavaClass cls = projectBuilder.getClassByName(simpleName);
//如果存在泛型 则将泛型与类名的对应关系存起来
if (Objects.isNull(globGicName) || globGicName.length < 1) {
// obtain generics from parent class
JavaClass superJavaClass = cls != null ? cls.getSuperJavaClass() : null;
if (superJavaClass != null && !"Object".equals(superJavaClass.getSimpleName())) {
globGicName = DocClassUtil.getSimpleGicName(superJavaClass.getGenericFullyQualifiedName());
}
}
JavaClassUtil.genericParamMap(genericMap, cls, globGicName);
List<DocJavaField> fields = JavaClassUtil.getFields(cls, 0, new LinkedHashMap<>());
if (JavaClassValidateUtil.isPrimitive(simpleName)) {
@ -103,7 +110,7 @@ public class ParamsBuildHelper {
registryClasses, projectBuilder, groupClasses, pid, jsonRequest));
}
} else if (DocGlobalConstants.JAVA_OBJECT_FULLY.equals(className)) {
ApiParam param = ApiParam.of().setField(pre + "any object").setType("object").setPid(pid);
ApiParam param = ApiParam.of().setId(pid + 1).setField(pre + "any object").setType("object").setPid(pid);
if (StringUtil.isEmpty(isRequired)) {
param.setDesc(DocGlobalConstants.ANY_OBJECT_MSG).setVersion(DocGlobalConstants.DEFAULT_VERSION);
} else {
@ -111,22 +118,25 @@ public class ParamsBuildHelper {
}
paramList.add(param);
} else if (JavaClassValidateUtil.isReactor(simpleName)) {
paramList.addAll(buildParams(globGicName[0], pre, nextLevel, isRequired, isResp,
registryClasses, projectBuilder, groupClasses, pid, jsonRequest));
if (globGicName.length > 0) {
paramList.addAll(buildParams(globGicName[0], pre, nextLevel, isRequired, isResp,
registryClasses, projectBuilder, groupClasses, pid, jsonRequest));
}
} else {
Map<String, String> ignoreFields = JavaClassUtil.getClassJsonIgnoreFields(cls);
out:
for (DocJavaField docField : fields) {
String maxLength = null;
JavaField field = docField.getJavaField();
String fieldName = field.getName();
String subTypeName = docField.getFullyQualifiedName();
if (field.isStatic() || "this$0".equals(fieldName) ||
JavaClassValidateUtil.isIgnoreFieldTypes(subTypeName)) {
continue;
}
if (field.isTransient() && skipTransientField) {
continue;
}
String fieldName = docField.getFieldName();
if (ignoreFields.containsKey(fieldName)) {
continue;
}
String subTypeName = docField.getFullyQualifiedName();
if ((responseFieldToUnderline && isResp) || (requestFieldToUnderline && !isResp)) {
fieldName = StringUtil.camelToUnderline(fieldName);
}
@ -150,12 +160,12 @@ public class ParamsBuildHelper {
}
boolean strRequired = false;
int annotationCounter = 0;
CustomField customResponseField = responseFieldMap.get(fieldName);
CustomField customResponseField = responseFieldMap.get(simpleName + "." + fieldName);
if (customResponseField != null && JavaClassUtil.isTargetChildClass(simpleName, customResponseField.getOwnerClassName())
&& (customResponseField.isIgnore()) && isResp) {
continue;
}
CustomField customRequestField = projectBuilder.getCustomReqFieldMap().get(fieldName);
CustomField customRequestField = projectBuilder.getCustomReqFieldMap().get(simpleName + "." + fieldName);
if (customRequestField != null && JavaClassUtil.isTargetChildClass(simpleName, customRequestField.getOwnerClassName())
&& (customRequestField.isIgnore()) && !isResp) {
continue;
@ -163,8 +173,30 @@ public class ParamsBuildHelper {
an:
for (JavaAnnotation annotation : javaAnnotations) {
String simpleAnnotationName = annotation.getType().getValue();
if ("max".equalsIgnoreCase(simpleAnnotationName)) {
maxLength = annotation.getProperty(DocAnnotationConstants.VALUE_PROP).toString();
AnnotationValue annotationValue = null;
if (DocAnnotationConstants.MAX.equalsIgnoreCase(simpleAnnotationName)) {
annotationValue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP);
}
if (DocAnnotationConstants.SIZE.equalsIgnoreCase(simpleAnnotationName)) {
annotationValue = annotation.getProperty(DocAnnotationConstants.MAX);
}
if (DocAnnotationConstants.LENGTH.equalsIgnoreCase(simpleAnnotationName)) {
annotationValue = annotation.getProperty(DocAnnotationConstants.MAX);
}
if (!Objects.isNull(annotationValue)) {
maxLength = annotationValue.toString();
}
if (DocAnnotationConstants.JSON_PROPERTY.equalsIgnoreCase(simpleAnnotationName)) {
AnnotationValue value = annotation.getProperty("access");
if (Objects.nonNull(value)) {
if (JSON_PROPERTY_READ_ONLY.equals(value.getParameterValue()) && !isResp) {
continue out;
}
if (JSON_PROPERTY_WRITE_ONLY.equals(value.getParameterValue()) && isResp) {
continue out;
}
}
}
if (DocAnnotationConstants.SHORT_JSON_IGNORE.equals(simpleAnnotationName)) {
continue out;
@ -185,7 +217,7 @@ public class ParamsBuildHelper {
for (String javaClass : groupClassList) {
if (groupClasses.contains(javaClass)) {
strRequired = false;
break an;
continue out;
}
}
} else if (JavaClassValidateUtil.isJSR303Required(simpleAnnotationName) && !isResp) {
@ -276,25 +308,6 @@ public class ParamsBuildHelper {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND, since, strRequired);
}
} else {
ApiParam param = ApiParam.of().setField(pre + fieldName).setPid(pid).setMaxLength(maxLength);
JavaClass javaClass = projectBuilder.getJavaProjectBuilder().getClassByName(subTypeName);
if (javaClass.isEnum()) {
comment = comment + handleEnumComment(javaClass, projectBuilder);
param.setType(DocGlobalConstants.ENUM);
}
if (JavaClassValidateUtil.isCollection(subTypeName)) {
String gNameTemp = fieldGicName;
// like Student<T>, Student class has a field List<T> lists.
if (globGicName.length > 0 && JAVA_LIST_FULLY.equals(gNameTemp)) {
gNameTemp = gNameTemp + "<T>";
}
String[] gNameArr = DocClassUtil.getSimpleGicName(gNameTemp);
if (gNameArr.length > 0) {
String gName = DocClassUtil.getSimpleGicName(gNameTemp)[0];
JavaClass javaClass1 = projectBuilder.getJavaProjectBuilder().getClassByName(gName);
comment = comment + handleEnumComment(javaClass1, projectBuilder);
}
}
String appendComment = "";
if (displayActualType) {
if (globGicName.length > 0) {
@ -307,84 +320,112 @@ public class ParamsBuildHelper {
appendComment = " (ActualType: " + JavaClassUtil.getClassSimpleName(docField.getActualJavaType()) + ")";
}
}
//如果已经设置返回类型 不需要再次设置
if (param.getType() == null) {
String processedType;
if (typeSimpleName.length() == 1) {
processedType = DocClassUtil.processTypeNameForParams(typeSimpleName.toLowerCase());
} else {
processedType = isShowJavaType ? typeSimpleName : DocClassUtil.processTypeNameForParams(typeSimpleName.toLowerCase());
}
param.setType(processedType);
}
if (!isResp && javaClass.isEnum()) {
List<JavaMethod> methods = javaClass.getMethods();
int index = 0;
enumOut:
for (JavaMethod method : methods) {
List<JavaAnnotation> javaAnnotationList = method.getAnnotations();
for (JavaAnnotation annotation : javaAnnotationList) {
if (annotation.getType().getValue().contains("JsonValue")) {
break enumOut;
}
}
if (CollectionUtil.isEmpty(javaAnnotations) && index < 1) {
break enumOut;
}
index++;
}
Object value = JavaClassUtil.getEnumValue(javaClass, !jsonRequest);
param.setValue(String.valueOf(value));
param.setEnumValues(JavaClassUtil.getEnumValues(javaClass));
param.setType(DocGlobalConstants.ENUM);
}
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
StringBuilder preBuilder = new StringBuilder();
for (int j = 0; j < level; j++) {
preBuilder.append(DocGlobalConstants.FIELD_SPACE);
}
preBuilder.append("└─");
int fieldPid = paramList.size() + pid;
if (JavaClassValidateUtil.isMap(subTypeName)) {
String gNameTemp = fieldGicName;
String valType = DocClassUtil.getMapKeyValueType(gNameTemp).length == 0 ? gNameTemp : DocClassUtil.getMapKeyValueType(gNameTemp)[1];
if (JavaClassValidateUtil.isMap(gNameTemp) || JAVA_OBJECT_FULLY.equals(valType)) {
ApiParam param1 = ApiParam.of().setField(preBuilder.toString() + "any object")
.setId(fieldPid + 1).setPid(fieldPid)
.setMaxLength(maxLength)
.setType("object").setDesc(DocGlobalConstants.ANY_OBJECT_MSG).setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(param1);
continue;
}
if (!JavaClassValidateUtil.isPrimitive(valType)) {
if (valType.length() == 1) {
String gicName = genericMap.get(valType);
if (!JavaClassValidateUtil.isPrimitive(gicName) && !simpleName.equals(gicName)) {
paramList.addAll(buildParams(gicName, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
} else {
paramList.addAll(buildParams(valType, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
int fieldPid;
ApiParam param = ApiParam.of().setField(pre + fieldName).setPid(pid).setMaxLength(maxLength);
String processedType;
if (typeSimpleName.length() == 1) {
String gicName = JAVA_OBJECT_FULLY;
if (Objects.nonNull(genericMap.get(typeSimpleName))) {
gicName = genericMap.get(subTypeName);
} else {
if (globGicName.length > 0) {
gicName = globGicName[0];
}
}
} else if (JavaClassValidateUtil.isCollection(subTypeName)) {
String gNameTemp = fieldGicName;
if (globGicName.length > 0 && JAVA_LIST_FULLY.equals(gNameTemp)) {
gNameTemp = gNameTemp + "<T>";
if (JavaClassValidateUtil.isPrimitive(gicName)) {
processedType = DocClassUtil.processTypeNameForParams(gicName);
} else {
processedType = DocClassUtil.processTypeNameForParams(typeSimpleName.toLowerCase());
}
String[] gNameArr = DocClassUtil.getSimpleGicName(gNameTemp);
} else {
processedType = isShowJavaType ? typeSimpleName : DocClassUtil.processTypeNameForParams(typeSimpleName.toLowerCase());
}
param.setType(processedType);
JavaClass javaClass = field.getType();
if (javaClass.isEnum()) {
comment = comment + handleEnumComment(javaClass, projectBuilder);
param.setType(DocGlobalConstants.ENUM);
if (!isResp) {
List<JavaMethod> methods = javaClass.getMethods();
int index = 0;
enumOut:
for (JavaMethod method : methods) {
List<JavaAnnotation> javaAnnotationList = method.getAnnotations();
for (JavaAnnotation annotation : javaAnnotationList) {
if (annotation.getType().getValue().contains("JsonValue")) {
break enumOut;
}
}
if (CollectionUtil.isEmpty(javaAnnotations) && index < 1) {
break enumOut;
}
index++;
}
Object value = JavaClassUtil.getEnumValue(javaClass, !jsonRequest);
param.setValue(String.valueOf(value));
param.setEnumValues(JavaClassUtil.getEnumValues(javaClass));
}
// Override old value
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
param.setValue(tagsMap.get(DocTags.MOCK));
}
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
} else if (JavaClassValidateUtil.isCollection(subTypeName) || JavaClassValidateUtil.isArray(subTypeName)) {
param.setType("array");
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
param.setValue(fieldValue);
}
if (globGicName.length > 0 && "java.util.List".equals(fieldGicName)) {
fieldGicName = fieldGicName + "<T>";
}
if (JavaClassValidateUtil.isArray(subTypeName)) {
fieldGicName = fieldGicName.substring(0, fieldGicName.lastIndexOf("["));
fieldGicName = "java.util.List<" + fieldGicName + ">";
}
String[] gNameArr = DocClassUtil.getSimpleGicName(fieldGicName);
if (gNameArr.length == 0) {
continue out;
}
String gName = DocClassUtil.getSimpleGicName(gNameTemp)[0];
if (!JavaClassValidateUtil.isPrimitive(gName)) {
if (gNameArr.length > 0) {
String gName = DocClassUtil.getSimpleGicName(fieldGicName)[0];
JavaClass javaClass1 = projectBuilder.getJavaProjectBuilder().getClassByName(gName);
comment = comment + handleEnumComment(javaClass1, projectBuilder);
}
String gName = gNameArr[0];
if (JavaClassValidateUtil.isPrimitive(gName)) {
String builder = DocUtil.jsonValueByType(gName) +
"," +
DocUtil.jsonValueByType(gName);
if (StringUtil.isEmpty(fieldValue)) {
param.setValue(DocUtil.handleJsonStr(builder));
} else {
param.setValue(fieldValue);
}
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
} else {
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
fieldPid = paramList.size() + pid;
if (!simpleName.equals(gName) && !gName.equals(simpleName)) {
JavaClass arraySubClass = projectBuilder.getJavaProjectBuilder().getClassByName(gName);
if (arraySubClass.isEnum()) {
@ -408,19 +449,49 @@ public class ParamsBuildHelper {
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
}
}
} else if (JavaClassValidateUtil.isMap(subTypeName)) {
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
param.setType("map");
param.setValue(fieldValue);
}
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
String builder = "[" +
DocUtil.jsonValueByType(gName) +
"," +
DocUtil.jsonValueByType(gName) +
"]";
if (StringUtil.isEmpty(fieldValue)) {
param.setValue(DocUtil.handleJsonStr(builder));
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
fieldPid = paramList.size() + pid;
String gNameTemp = fieldGicName;
String valType = DocClassUtil.getMapKeyValueType(gNameTemp).length == 0 ? gNameTemp : DocClassUtil.getMapKeyValueType(gNameTemp)[1];
if (JavaClassValidateUtil.isMap(gNameTemp) || JAVA_OBJECT_FULLY.equals(valType)) {
ApiParam param1 = ApiParam.of().setField(preBuilder.toString() + "any object")
.setId(fieldPid + 1).setPid(fieldPid)
.setMaxLength(maxLength)
.setType("object").setDesc(DocGlobalConstants.ANY_OBJECT_MSG).setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(param1);
continue;
}
if (!JavaClassValidateUtil.isPrimitive(valType)) {
if (valType.length() == 1) {
String gicName = genericMap.get(valType);
if (!JavaClassValidateUtil.isPrimitive(gicName) && !simpleName.equals(gicName)) {
paramList.addAll(buildParams(gicName, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
} else {
param.setValue(fieldValue);
paramList.addAll(buildParams(valType, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
}
} else if (subTypeName.length() == 1 || DocGlobalConstants.JAVA_OBJECT_FULLY.equals(subTypeName)) {
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
fieldPid = paramList.size() + pid;
// handle java generic or object
if (DocGlobalConstants.JAVA_OBJECT_FULLY.equals(subTypeName) && StringUtil.isNotEmpty(field.getComment())) {
ApiParam param1 = ApiParam.of().setField(preBuilder.toString() + "any object")
@ -461,21 +532,19 @@ public class ParamsBuildHelper {
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
}
} else if (JavaClassValidateUtil.isArray(subTypeName)) {
fieldGicName = fieldGicName.substring(0, fieldGicName.indexOf("["));
if (className.equals(fieldGicName)) {
//do nothing
} else if (!JavaClassValidateUtil.isPrimitive(fieldGicName)) {
paramList.addAll(buildParams(fieldGicName, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
} else if (simpleName.equals(subTypeName)) {
//do nothing
} else {
if (!javaClass.isEnum()) {
paramList.addAll(buildParams(fieldGicName, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
if (StringUtil.isNotEmpty(comment)) {
commonHandleParam(paramList, param, isRequired, comment + appendComment, since, strRequired);
} else {
commonHandleParam(paramList, param, isRequired, NO_COMMENTS_FOUND + appendComment, since, strRequired);
}
fieldGicName = DocUtil.formatFieldTypeGicName(genericMap, globGicName, fieldGicName);
fieldPid = paramList.size() + pid;
paramList.addAll(buildParams(fieldGicName, preBuilder.toString(), nextLevel, isRequired,
isResp, registryClasses, projectBuilder, groupClasses, fieldPid, jsonRequest));
}
}
}//end field
@ -527,7 +596,6 @@ public class ParamsBuildHelper {
}
} else {
enumComments = DocUtil.replaceNewLineToHtmlBr(enumComments);
comment = comment + "<br/>" + JavaClassUtil.getEnumParams(javaClass) + "<br/>";
if (StringUtil.isNotEmpty(enumComments)) {
comment = comment + "(See: " + enumComments + ")";
}

View File

@ -26,6 +26,7 @@ import com.power.common.util.CollectionUtil;
import com.power.doc.constants.DocLanguage;
import com.power.doc.model.rpc.RpcApiDependency;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@ -43,6 +44,11 @@ public class ApiConfig {
*/
private String serverUrl;
/**
* Path Prefix, eg: Servlet ContextPath
*/
private String pathPrefix = "";
/**
* Set comments check mode
*/
@ -67,7 +73,13 @@ public class ApiConfig {
/**
* list of Request headers
*/
private List<ApiReqHeader> requestHeaders;
private List<ApiReqParam> requestHeaders;
/**
* @since 2.2.2
* list of Request params
*/
private List<ApiReqParam> requestParams;
/**
* @since 1.7.5
@ -151,6 +163,7 @@ public class ApiConfig {
* project group
*/
private String group;
/**
* @since 1.7.5
* project name
@ -277,6 +290,8 @@ public class ApiConfig {
private String style;
private String highlightStyleLink;
/**
* create debug page
*/
@ -300,38 +315,75 @@ public class ApiConfig {
* Torna appKey
*/
private String appKey;
/**
* Torna Secret
*/
private String secret;
/**
* Torna appToken
*/
private String appToken;
/**
* Torna openUrl
*/
private String openUrl;
/**
* 调试环境名称
* Debugging environment name
*/
private String debugEnvName;
/**
* 调试环境请求路径
* Url of the debugging environment
*/
private String debugEnvUrl;
/**
* torna调试开关
* Show log when pushing document to torna
*/
private boolean tornaDebug = true;
/**
* 推送人
* The operator who pushes the document to Torna
*/
private String author;
/**
* smart-doc supported framework, if not set default is spring,
*/
private String framework;
private List<ApiGroup> groups;
/**
* replace old document while push to torna
* @since 2.2.4
*/
private Boolean replace;
/**
* @since 2.2.5
*/
private boolean requestParamsTable = Boolean.TRUE;
/**
* @since 2.2.5
*/
private boolean responseParamsTable = Boolean.TRUE;
public String getPathPrefix() {
return pathPrefix;
}
public void setPathPrefix(String pathPrefix) {
this.pathPrefix = pathPrefix;
}
public String getAuthor() {
return author;
}
@ -404,18 +456,46 @@ public class ApiConfig {
this.outPath = outPath;
}
public List<ApiReqHeader> getRequestHeaders() {
public List<ApiReqParam> getRequestHeaders() {
return requestHeaders;
}
public void setRequestHeaders(List<ApiReqHeader> requestHeaders) {
public void setRequestHeaders(List<ApiReqParam> requestHeaders) {
this.requestHeaders = requestHeaders;
}
public void setRequestHeaders(ApiReqHeader... requestHeaders) {
public void setRequestHeaders(ApiReqParam... requestHeaders) {
this.requestHeaders = CollectionUtil.asList(requestHeaders);
this.requestHeaders.stream().map(header -> header.setDesc(header.getDesc() + "(Global)"))
.collect(Collectors.toList());
this.requestHeaders.forEach(header -> header.setDesc(header.getDesc() + "(Global)"));
}
public List<ApiGroup> getGroups() {
return groups;
}
public ApiConfig setGroups(List<ApiGroup> groups) {
this.groups = groups;
return this;
}
public ApiConfig setGroups(ApiGroup... groups) {
this.groups = CollectionUtil.asList(groups);
return this;
}
public List<ApiReqParam> getRequestParams() {
return requestParams;
}
public ApiConfig setRequestParams(List<ApiReqParam> requestParams) {
this.requestParams = requestParams;
return this;
}
public void setRequestParams(ApiReqParam... requestParams) {
this.requestParams = CollectionUtil.asList(requestParams);
this.requestParams.forEach(param -> param.setDesc(param.getDesc() + "(Global)"));
}
public List<CustomField> getCustomResponseFields() {
@ -520,7 +600,7 @@ public class ApiConfig {
}
return this.dataDictionaries.stream().filter((apiDataDictionary ->
enumClassName.equalsIgnoreCase(apiDataDictionary.getEnumClassName())))
.findFirst().orElse(new ApiDataDictionary());
.findFirst().orElse(null);
}
public List<ApiErrorCodeDictionary> getErrorCodeDictionaries() {
@ -792,106 +872,43 @@ public class ApiConfig {
this.customRequestFields = CollectionUtil.asList(customRequestFields);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"serverUrl\":\"")
.append(serverUrl).append('\"');
sb.append(",\"isStrict\":")
.append(isStrict);
sb.append(",\"allInOne\":")
.append(allInOne);
sb.append(",\"outPath\":\"")
.append(outPath).append('\"');
sb.append(",\"sourceCodePaths\":")
.append(sourceCodePaths);
sb.append(",\"requestHeaders\":")
.append(requestHeaders);
sb.append(",\"coverOld\":")
.append(coverOld);
sb.append(",\"customResponseFields\":")
.append(customResponseFields);
sb.append(",\"errorCodes\":")
.append(errorCodes);
sb.append(",\"packageFilters\":\"")
.append(packageFilters).append('\"');
sb.append(",\"revisionLogs\":")
.append(revisionLogs);
sb.append(",\"md5EncryptedHtmlName\":")
.append(md5EncryptedHtmlName);
sb.append(",\"language\":")
.append(language);
sb.append(",\"adoc\":")
.append(adoc);
sb.append(",\"dataDictionaries\":")
.append(dataDictionaries);
sb.append(",\"errorCodeDictionaries\":")
.append(errorCodeDictionaries);
sb.append(",\"apiObjectReplacements\":")
.append(apiObjectReplacements);
sb.append(",\"rpcApiDependencies\":")
.append(rpcApiDependencies);
sb.append(",\"apiConstants\":")
.append(apiConstants);
sb.append(",\"group\":\"")
.append(group).append('\"');
sb.append(",\"projectName\":\"")
.append(projectName).append('\"');
sb.append(",\"projectCName\":\"")
.append(projectCName).append('\"');
sb.append(",\"skipTransientField\":")
.append(skipTransientField);
sb.append(",\"showAuthor\":")
.append(showAuthor);
sb.append(",\"requestFieldToUnderline\":")
.append(requestFieldToUnderline);
sb.append(",\"responseFieldToUnderline\":")
.append(responseFieldToUnderline);
sb.append(",\"sortByTitle\":")
.append(sortByTitle);
sb.append(",\"showJavaType\":")
.append(showJavaType);
sb.append(",\"inlineEnum\":")
.append(inlineEnum);
sb.append(",\"rpcConsumerConfig\":\"")
.append(rpcConsumerConfig).append('\"');
sb.append(",\"recursionLimit\":")
.append(recursionLimit);
sb.append(",\"requestExample\":")
.append(requestExample);
sb.append(",\"responseExample\":")
.append(responseExample);
sb.append(",\"allInOneDocFileName\":\"")
.append(allInOneDocFileName).append('\"');
sb.append(",\"paramsDataToTree\":")
.append(paramsDataToTree);
sb.append(",\"ignoreRequestParams\":")
.append(ignoreRequestParams);
sb.append(",\"displayActualType\":")
.append(displayActualType);
sb.append(",\"responseBodyAdvice\":")
.append(responseBodyAdvice);
sb.append(",\"style\":\"")
.append(style).append('\"');
sb.append(",\"createDebugPage\":")
.append(createDebugPage);
sb.append(",\"urlSuffix\":\"")
.append(urlSuffix).append('\"');
sb.append(",\"appKey\":\"")
.append(appKey).append('\"');
sb.append(",\"secret\":\"")
.append(secret).append('\"');
sb.append(",\"appToken\":\"")
.append(appToken).append('\"');
sb.append(",\"openUrl\":\"")
.append(openUrl).append('\"');
sb.append(",\"debugEnvName\":\"")
.append(debugEnvName).append('\"');
sb.append(",\"debugEnvUrl\":\"")
.append(debugEnvUrl).append('\"');
sb.append(",\"tornaDebug\":")
.append(tornaDebug);
sb.append('}');
return sb.toString();
public String getFramework() {
return framework;
}
}
public void setFramework(String framework) {
this.framework = framework;
}
public Boolean getReplace() {
return replace;
}
public void setReplace(Boolean replace) {
this.replace = replace;
}
public boolean isRequestParamsTable() {
return requestParamsTable;
}
public void setRequestParamsTable(boolean requestParamsTable) {
this.requestParamsTable = requestParamsTable;
}
public boolean isResponseParamsTable() {
return responseParamsTable;
}
public void setResponseParamsTable(boolean responseParamsTable) {
this.responseParamsTable = responseParamsTable;
}
public String getHighlightStyleLink() {
return highlightStyleLink;
}
public void setHighlightStyleLink(String highlightStyleLink) {
this.highlightStyleLink = highlightStyleLink;
}
}

View File

@ -25,6 +25,7 @@ package com.power.doc.model;
import com.power.common.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -35,7 +36,7 @@ public class ApiDoc implements Comparable<ApiDoc> {
*
* @since 1.7+
*/
public int order;
public Integer order;
/**
* controller name
@ -49,6 +50,26 @@ public class ApiDoc implements Comparable<ApiDoc> {
*/
private String alias;
/**
* tags
*
* @author cqmike
*/
private String[] tags;
/**
* group
*
* @author cqmike
*/
private String group;
/**
* class in package name
*
*/
private String packageName;
/**
* List of method doc
*/
@ -66,6 +87,16 @@ public class ApiDoc implements Comparable<ApiDoc> {
private String author;
/**
* if this is group, then is true
*/
private boolean isFolder;
/**
* children
*/
private List<ApiDoc> childrenApiDocs = new ArrayList<>();
public String getAuthor() {
return author;
}
@ -98,11 +129,11 @@ public class ApiDoc implements Comparable<ApiDoc> {
this.desc = desc;
}
public int getOrder() {
public Integer getOrder() {
return order;
}
public void setOrder(int order) {
public void setOrder(Integer order) {
this.order = order;
}
@ -114,6 +145,14 @@ public class ApiDoc implements Comparable<ApiDoc> {
this.alias = alias;
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public String getLink() {
if (StringUtil.isNotEmpty(link)) {
return link;
@ -125,6 +164,38 @@ public class ApiDoc implements Comparable<ApiDoc> {
this.link = link;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public boolean isFolder() {
return isFolder;
}
public void setFolder(boolean folder) {
isFolder = folder;
}
public List<ApiDoc> getChildrenApiDocs() {
return childrenApiDocs;
}
public void setChildrenApiDocs(List<ApiDoc> childrenApiDocs) {
this.childrenApiDocs = childrenApiDocs;
}
@Override
public int compareTo(ApiDoc o) {
if (Objects.nonNull(o.getDesc())) {
@ -133,6 +204,31 @@ public class ApiDoc implements Comparable<ApiDoc> {
return name.compareTo(o.getName());
}
public static ApiDoc buildTagApiDoc(ApiDoc source, String tag, ApiMethodDoc methodDoc) {
ApiDoc apiDoc = new ApiDoc();
apiDoc.setAlias(source.getAlias());
apiDoc.setLink(source.getLink());
apiDoc.setDesc(tag);
apiDoc.setAuthor(source.getAuthor());
apiDoc.setPackageName(source.getPackageName());
apiDoc.setName(tag);
apiDoc.setList(new ArrayList<>());
ApiMethodDoc clone = methodDoc.clone();
clone.setOrder(apiDoc.getList().size() + 1);
apiDoc.getList().add(clone);
return apiDoc;
}
public static ApiDoc buildGroupApiDoc(String group) {
ApiDoc apiDoc = new ApiDoc();
apiDoc.setFolder(true);
apiDoc.setGroup(group);
apiDoc.setName(group);
apiDoc.setDesc(group);
apiDoc.setChildrenApiDocs(new ArrayList<>());
return apiDoc;
}
@Override
public String toString() {

View File

@ -0,0 +1,55 @@
package com.power.doc.model;
/**
* api group
*
* @author cqmike
* @version 1.0.0
* @since 2021年07月31日 16:39:00
*/
public class ApiGroup {
/**
* group name
*/
private String name;
/**
* package name
* support patten
*/
private String apis;
/**
* url path
* support patten
*/
private String paths;
public String getName() {
return name;
}
public ApiGroup setName(String name) {
this.name = name;
return this;
}
public String getApis() {
return apis;
}
public ApiGroup setApis(String apis) {
this.apis = apis;
return this;
}
public String getPaths() {
return paths;
}
public ApiGroup setPaths(String paths) {
this.paths = paths;
return this;
}
}

View File

@ -33,7 +33,7 @@ import java.util.Map;
/**
* java api method info model.
*/
public class ApiMethodDoc implements Serializable {
public class ApiMethodDoc implements Serializable, Cloneable {
private static final long serialVersionUID = 7211922919532562867L;
@ -109,7 +109,7 @@ public class ApiMethodDoc implements Serializable {
/**
* http request headers
*/
private List<ApiReqHeader> requestHeaders;
private List<ApiReqParam> requestHeaders;
/**
* path params
@ -181,12 +181,64 @@ public class ApiMethodDoc implements Serializable {
* mark page
*/
private String page = "";
/**
* torna request is array
*/
private Integer isRequestArray;
/**
* torna request is array-type
*/
private String requestArrayType;
/**
* torna response is array
*/
private Integer isResponseArray;
/**
* torna request is array
*/
private String responseArrayType;
/**
* 是否为List数据 openApi
*/
private boolean listParam = false;
/**
* tags
*/
private String[] tags;
public Integer getIsRequestArray() {
return isRequestArray;
}
public void setIsRequestArray(Integer isRequestArray) {
this.isRequestArray = isRequestArray;
}
public String getRequestArrayType() {
return requestArrayType;
}
public void setRequestArrayType(String requestArrayType) {
this.requestArrayType = requestArrayType;
}
public Integer getIsResponseArray() {
return isResponseArray;
}
public void setIsResponseArray(Integer isResponseArray) {
this.isResponseArray = isResponseArray;
}
public String getResponseArrayType() {
return responseArrayType;
}
public void setResponseArrayType(String responseArrayType) {
this.responseArrayType = responseArrayType;
}
public boolean isListParam() {
return listParam;
}
@ -291,11 +343,11 @@ public class ApiMethodDoc implements Serializable {
this.responseParams = responseParams;
}
public List<ApiReqHeader> getRequestHeaders() {
public List<ApiReqParam> getRequestHeaders() {
return requestHeaders;
}
public void setRequestHeaders(List<ApiReqHeader> requestHeaders) {
public void setRequestHeaders(List<ApiReqParam> requestHeaders) {
this.requestHeaders = requestHeaders;
}
@ -415,6 +467,15 @@ public class ApiMethodDoc implements Serializable {
this.page = page;
}
public String[] getTags() {
return tags;
}
public ApiMethodDoc setTags(String[] tags) {
this.tags = tags;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
@ -463,4 +524,13 @@ public class ApiMethodDoc implements Serializable {
sb.append('}');
return sb.toString();
}
@Override
public ApiMethodDoc clone() {
try {
return (ApiMethodDoc) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("clone apiMethodDoc is error", e);
}
}
}

View File

@ -104,6 +104,12 @@ public class ApiParam {
*/
private String maxLength;
/**
* is config.json config param
* default false
*/
private boolean configParam;
public static ApiParam of() {
return new ApiParam();
}
@ -243,6 +249,15 @@ public class ApiParam {
return this;
}
public boolean isConfigParam() {
return configParam;
}
public ApiParam setConfigParam(boolean configParam) {
this.configParam = configParam;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");

View File

@ -22,36 +22,48 @@
*/
package com.power.doc.model;
import com.power.doc.constants.ApiReqParamInTypeEnum;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;
/**
* Description:
* http request header info model
* http request param info model
*
* @author yu 2018/06/18.
* @author chenqi 2021/07/15
*/
public class ApiReqHeader {
public class ApiReqParam {
/**
* Request header name
* Request param name
*/
private String name;
/**
* Request header type
* Request param type
*/
private String type;
/**
* request header defaultValue
* request param defaultValue
*/
private String value;
/**
* Request header description
* Request param description
*/
private String desc;
/**
* where is param location
* default header
*
* @see ApiReqParamInTypeEnum value
*/
private String paramIn;
/**
* required flag
*
@ -66,20 +78,27 @@ public class ApiReqHeader {
*/
private String since = "-";
@Deprecated
public static ApiReqHeader header() {
return new ApiReqHeader();
}
/**
* @since 2.2.2
* Regular expression match request param
*/
private String pathPatterns;
public static ApiReqHeader builder() {
return new ApiReqHeader();
/**
* @since 2.2.2
* Regular expression ignore request param
*/
private String excludePathPatterns;
public static ApiReqParam builder() {
return new ApiReqParam();
}
public String getName() {
return name;
}
public ApiReqHeader setName(String name) {
public ApiReqParam setName(String name) {
this.name = name;
return this;
}
@ -88,7 +107,7 @@ public class ApiReqHeader {
return type;
}
public ApiReqHeader setType(String type) {
public ApiReqParam setType(String type) {
this.type = type;
return this;
}
@ -97,7 +116,7 @@ public class ApiReqHeader {
return desc;
}
public ApiReqHeader setDesc(String desc) {
public ApiReqParam setDesc(String desc) {
this.desc = desc;
return this;
}
@ -106,7 +125,7 @@ public class ApiReqHeader {
return required;
}
public ApiReqHeader setRequired(boolean required) {
public ApiReqParam setRequired(boolean required) {
this.required = required;
return this;
}
@ -115,7 +134,7 @@ public class ApiReqHeader {
return since;
}
public ApiReqHeader setSince(String since) {
public ApiReqParam setSince(String since) {
this.since = since;
return this;
}
@ -124,16 +143,46 @@ public class ApiReqHeader {
return value;
}
public ApiReqHeader setValue(String value) {
public ApiReqParam setValue(String value) {
this.value = value;
return this;
}
public String getPathPatterns() {
return pathPatterns;
}
public ApiReqParam setPathPatterns(String pathPatterns) {
this.pathPatterns = pathPatterns;
return this;
}
public String getExcludePathPatterns() {
return excludePathPatterns;
}
public ApiReqParam setExcludePathPatterns(String excludePathPatterns) {
this.excludePathPatterns = excludePathPatterns;
return this;
}
public String getParamIn() {
if (StringUtils.isEmpty(paramIn)) {
return ApiReqParamInTypeEnum.HEADER.getValue();
}
return paramIn;
}
public ApiReqParam setParamIn(String paramIn) {
this.paramIn = paramIn;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApiReqHeader that = (ApiReqHeader) o;
ApiReqParam that = (ApiReqParam) o;
return Objects.equals(name, that.name);
}
@ -153,11 +202,23 @@ public class ApiReqHeader {
.append(value).append('\"');
sb.append(",\"desc\":\"")
.append(desc).append('\"');
sb.append(",\"paramIn\":")
.append(paramIn);
sb.append(",\"required\":")
.append(required);
sb.append(",\"since\":\"")
.append(since).append('\"');
sb.append(",\"pathPatterns\":\"")
.append(pathPatterns).append('\"');
sb.append(",\"excludePathPatterns\":\"")
.append(excludePathPatterns).append('\"');
sb.append('}');
return sb.toString();
}
public static ApiParam convertToApiParam(ApiReqParam param) {
return ApiParam.of().setField(param.getName()).setValue(param.getValue())
.setRequired(param.isRequired()).setDesc(param.getDesc()).setConfigParam(true)
.setVersion("-").setType(param.getType());
}
}

View File

@ -69,6 +69,11 @@ public class DocJavaField {
*/
private String actualJavaType;
/**
* field name
*/
private String fieldName;
private boolean array;
private boolean primitive;
@ -199,4 +204,13 @@ public class DocJavaField {
public void setEnum(boolean anEnum) {
isEnum = anEnum;
}
public String getFieldName() {
return fieldName;
}
public DocJavaField setFieldName(String fieldName) {
this.fieldName = fieldName;
return this;
}
}

View File

@ -41,6 +41,8 @@ public class DocJavaMethod {
private Map<String, JavaType> actualTypesMap;
private boolean download;
public static DocJavaMethod builder() {
return new DocJavaMethod();
}
@ -80,4 +82,13 @@ public class DocJavaMethod {
this.requestSchema = requestSchema;
return this;
}
public boolean isDownload() {
return download;
}
public DocJavaMethod setDownload(boolean download) {
this.download = download;
return this;
}
}

View File

@ -0,0 +1,66 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.model.framework;
public class PageableAsQueryParam {
/**
* Page you want to retrieve (0..N)
*/
private Integer page;
/**
* Number of records per page
*/
private Integer size;
/**
* Sorting criteria in the format: property(,asc|desc),Default sort order is ascending.
* Multiple sort criteria are supported.
*/
private String sort;
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public String getSort() {
return sort;
}
public void setSort(String sort) {
this.sort = sort;
}
}

View File

@ -22,11 +22,17 @@
*/
package com.power.doc.model.postman;
import com.power.common.util.StringUtil;
import com.power.doc.model.postman.request.ParamBean;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* post
*
* @author yu 2020/11/28.
*/
public class UrlBean {
@ -45,6 +51,42 @@ public class UrlBean {
private List<ParamBean> variable;
public UrlBean() {
}
public UrlBean(String serverUrl) {
if (StringUtil.isEmpty(serverUrl)) {
return;
}
Pattern pattern = Pattern.compile("(https?://)([^:^/]*)(:[a-zA-Z-z0-9|^{{\\}}$]*)?(.*)?");
Matcher matcher = pattern.matcher(serverUrl);
if (matcher.find()) {
String protocol = matcher.group(1);
protocol = protocol.substring(0, protocol.indexOf(":"));
String domain = matcher.group(2);
List<String> hosts = new ArrayList<>();
hosts.add(domain);
// port
String port = matcher.group(3);
if (StringUtil.isNotEmpty(port)) {
port = port.replace(":", "");
}
String uri = matcher.group(4);
List<String> paths = new ArrayList<>();
paths.add(uri);
this.protocol = protocol;
this.host = hosts;
this.port = port;
this.path = paths;
} else {
throw new RuntimeException("ServerUrl must contain http or https protocol.");
}
}
public String getRaw() {
return raw;
}

View File

@ -22,7 +22,7 @@
*/
package com.power.doc.model.request;
import com.power.doc.model.ApiReqHeader;
import com.power.doc.model.ApiReqParam;
import java.util.List;
@ -33,7 +33,7 @@ public class CurlRequest {
private String type;
private List<ApiReqHeader> reqHeaders;
private List<ApiReqParam> reqHeaders;
private String url;
@ -54,11 +54,11 @@ public class CurlRequest {
return this;
}
public List<ApiReqHeader> getReqHeaders() {
public List<ApiReqParam> getReqHeaders() {
return reqHeaders;
}
public CurlRequest setReqHeaders(List<ApiReqHeader> reqHeaders) {
public CurlRequest setReqHeaders(List<ApiReqParam> reqHeaders) {
this.reqHeaders = reqHeaders;
return this;
}

View File

@ -0,0 +1,79 @@
package com.power.doc.model.request;
/**
* @author yu 2021/8/28.
*/
public class JaxrsPathMapping extends RequestMapping {
/**
* url
*/
private String url;
/**
* path
*/
private String shortUrl;
/**
* methodType
*/
private String methodType;
/**
* media type
*/
private String mediaType;
/**
* method deprecated
*/
private boolean deprecated;
public static JaxrsPathMapping builder() {
return new JaxrsPathMapping();
}
public String getUrl() {
return url;
}
public JaxrsPathMapping setUrl(String url) {
this.url = url;
return this;
}
public String getShortUrl() {
return shortUrl;
}
public JaxrsPathMapping setShortUrl(String shortUrl) {
this.shortUrl = shortUrl;
return this;
}
public String getMethodType() {
return methodType;
}
public JaxrsPathMapping setMethodType(String methodType) {
this.methodType = methodType;
return this;
}
public String getMediaType() {
return mediaType;
}
public JaxrsPathMapping setMediaType(String mediaType) {
this.mediaType = mediaType;
return this;
}
public boolean isDeprecated() {
return deprecated;
}
public JaxrsPathMapping setDeprecated(boolean deprecated) {
this.deprecated = deprecated;
return this;
}
}

View File

@ -35,7 +35,14 @@ public class RequestMapping {
* path
*/
private String shortUrl;
/**
* method type
*/
private String methodType;
/**
* media type
*/
private String mediaType;
/**

View File

@ -37,6 +37,7 @@ public class Apis {
private String parentId;
private String isShow;
private String author;
private Integer orderIndex;
private DubboInfo dubboInfo;
private List<HttpParam> headerParams;
private List<HttpParam> pathParams;
@ -45,6 +46,50 @@ public class Apis {
private List<HttpParam> responseParams;
private String errorCodeParams;
private List<Apis> items;
private Integer isRequestArray;
private String requestArrayType;
private Integer isResponseArray;
private String responseArrayType;
public Integer getIsRequestArray() {
return isRequestArray;
}
public void setIsRequestArray(Integer isRequestArray) {
this.isRequestArray = isRequestArray;
}
public String getRequestArrayType() {
return requestArrayType;
}
public void setRequestArrayType(String requestArrayType) {
this.requestArrayType = requestArrayType;
}
public Integer getIsResponseArray() {
return isResponseArray;
}
public void setIsResponseArray(Integer isResponseArray) {
this.isResponseArray = isResponseArray;
}
public String getResponseArrayType() {
return responseArrayType;
}
public void setResponseArrayType(String responseArrayType) {
this.responseArrayType = responseArrayType;
}
public Integer getOrderIndex() {
return orderIndex;
}
public void setOrderIndex(Integer orderIndex) {
this.orderIndex = orderIndex;
}
public String getAuthor() {
return author;

View File

@ -25,25 +25,14 @@ package com.power.doc.model.torna;
import java.util.List;
/**
* @author: xingzi 2021/2/25 12:13
* @author xingzi 2021/2/25 12:13
**/
public class EnumInfo {
/**
* "enumInfo": {
* "name": "支付枚举",
* "description": "支付状态",
* "items": [
* {
* "name": "WAIT_PAY",
* "type": "string",
* "value": "0",
* "description": "未支付"
* }
* ]
* }
*/
private String name;
private String description;
private List<Item> items;
public String getName() {

View File

@ -42,6 +42,18 @@ public class TornaApi {
List<Apis> apis;
String author;
List<CommonErrorCode> commonErrorCodes;
/**
* 是否替换文档1替换0不替换追加缺省1
*/
Integer isReplace;
public Integer getIsReplace() {
return isReplace;
}
public void setIsReplace(Integer isReplace) {
this.isReplace = isReplace;
}
public String getAuthor() {
return author;

View File

@ -29,9 +29,9 @@ import java.net.URLDecoder;
import java.util.HashMap;
/**
* torna请求日志信息
* Print the log of pushing documents to Torna
*
* @author: xingzi 2021/3/20 22:11
* @author xingzi 2021/3/20 22:11
**/
public class TornaRequestInfo {
private String code;
@ -92,18 +92,16 @@ public class TornaRequestInfo {
public String buildInfo() {
StringBuilder sb = new StringBuilder();
sb.append("---------------------------START---------------------------\n")
.append("接口名: ")
.append("API: ")
.append(category)
.append("\n")
.append("请求数据: \n")
.append("Request: \n")
.append(TornaConstants.GSON.toJson(requestInfo))
.append("\n")
.append("返回结果: \n")
.append("Response: \n")
.append(TornaConstants.GSON.fromJson(responseInfo, HashMap.class))
.append("\n")
.append("---------------------------END---------------------------\n");
try {
return URLDecoder.decode(sb.toString(), "utf-8");
} catch (UnsupportedEncodingException e) {

View File

@ -35,6 +35,8 @@ import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaType;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static com.power.doc.constants.DocGlobalConstants.NO_COMMENTS_FOUND;
import static com.power.doc.constants.DocTags.IGNORE_RESPONSE_BODY_ADVICE;
@ -44,12 +46,12 @@ import static com.power.doc.constants.DocTags.IGNORE_RESPONSE_BODY_ADVICE;
*/
public interface IDocBuildTemplate<T> {
default String createDocRenderHeaders(List<ApiReqHeader> headers, boolean isAdoc) {
default String createDocRenderHeaders(List<ApiReqParam> headers, boolean isAdoc) {
StringBuilder builder = new StringBuilder();
if (CollectionUtil.isEmpty(headers)) {
headers = new ArrayList<>(0);
}
for (ApiReqHeader header : headers) {
for (ApiReqParam header : headers) {
if (isAdoc) {
builder.append("|");
}
@ -82,7 +84,14 @@ public interface IDocBuildTemplate<T> {
apiDoc.setName(controllerName);
apiDoc.setAuthor(classAuthor);
apiDoc.setAlias(controllerName);
apiDoc.setFolder(true);
apiDoc.setPackageName(cls.getPackage().getName());
//apiDoc.setAuthor();
// handle class tags
List<DocletTag> classTags = cls.getTagsByName(DocTags.TAG);
apiDoc.setTags(classTags.stream().map(DocletTag::getValue).toArray(String[]::new));
if (isUseMD5) {
String name = DocUtil.generateId(apiDoc.getName());
apiDoc.setAlias(name);
@ -94,19 +103,85 @@ public interface IDocBuildTemplate<T> {
}
/**
* handle group api docs
*
* @param apiDocList list of apiDocList
* @param apiConfig ApiConfig apiConfig
* @author cqmike
* @return List of ApiDoc
*/
default List<ApiDoc> handleApiGroup(List<ApiDoc> apiDocList, ApiConfig apiConfig) {
if (CollectionUtil.isEmpty(apiDocList) || apiConfig == null) {
return apiDocList;
}
List<ApiGroup> groups = apiConfig.getGroups();
ApiDoc defaultGroup = ApiDoc.buildGroupApiDoc("default");
List<ApiDoc> finalApiDocs = new ArrayList<>();
finalApiDocs.add(defaultGroup);
AtomicInteger order = new AtomicInteger(1);
defaultGroup.setOrder(order.getAndIncrement());
if (CollectionUtil.isEmpty(groups)) {
defaultGroup.getChildrenApiDocs().addAll(apiDocList);
return finalApiDocs;
}
Map<String, String> hasInsert = new HashMap<>();
for (ApiGroup group : groups) {
ApiDoc groupApiDoc = ApiDoc.buildGroupApiDoc(group.getName());
groupApiDoc.setOrder(order.getAndIncrement());
finalApiDocs.add(groupApiDoc);
for (ApiDoc doc : apiDocList) {
if (hasInsert.containsKey(doc.getAlias())) {
continue;
}
if (!DocUtil.isMatch(group.getApis(), doc.getPackageName())) {
continue;
}
hasInsert.put(doc.getAlias(), null);
groupApiDoc.getChildrenApiDocs().add(doc);
doc.setOrder(groupApiDoc.getChildrenApiDocs().size());
doc.setGroup(group.getName());
if (StringUtil.isEmpty(group.getPaths())) {
continue;
}
List<ApiMethodDoc> methodDocs = doc.getList().stream()
.filter(l -> DocPathUtil.matches(l.getPath(), group.getPaths(), null))
.collect(Collectors.toList());
doc.setList(methodDocs);
}
}
// Ungrouped join the default group
for (ApiDoc doc : apiDocList) {
String key = doc.getAlias();
if (!hasInsert.containsKey(key)) {
defaultGroup.getChildrenApiDocs().add(doc);
doc.setOrder(defaultGroup.getChildrenApiDocs().size());
hasInsert.put(doc.getAlias(), null);
}
}
return finalApiDocs;
}
default List<ApiParam> buildReturnApiParams(DocJavaMethod docJavaMethod, ProjectDocConfigBuilder projectBuilder) {
JavaMethod method = docJavaMethod.getJavaMethod();
if (method.getReturns().isVoid() && Objects.isNull(projectBuilder.getApiConfig().getResponseBodyAdvice())) {
return new ArrayList<>(0);
}
DocletTag downloadTag = method.getTagByName(DocTags.DOWNLOAD);
if (Objects.nonNull(downloadTag)) {
return new ArrayList<>(0);
}
String returnTypeGenericCanonicalName = method.getReturnType().getGenericCanonicalName();
if (Objects.nonNull(projectBuilder.getApiConfig().getResponseBodyAdvice())
&& Objects.isNull(method.getTagByName(IGNORE_RESPONSE_BODY_ADVICE))) {
String responseBodyAdvice = projectBuilder.getApiConfig().getResponseBodyAdvice().getClassName();
returnTypeGenericCanonicalName = new StringBuffer()
.append(responseBodyAdvice)
.append("<")
.append(returnTypeGenericCanonicalName).append(">").toString();
if (!returnTypeGenericCanonicalName.startsWith(responseBodyAdvice)) {
returnTypeGenericCanonicalName = new StringBuffer()
.append(responseBodyAdvice)
.append("<")
.append(returnTypeGenericCanonicalName).append(">").toString();
}
}
Map<String, JavaType> actualTypesMap = docJavaMethod.getActualTypesMap();
ApiReturn apiReturn = DocClassUtil.processReturnType(returnTypeGenericCanonicalName);

View File

@ -0,0 +1,898 @@
package com.power.doc.template;
import com.power.common.util.*;
import com.power.doc.builder.ProjectDocConfigBuilder;
import com.power.doc.constants.*;
import com.power.doc.handler.JaxrsHeaderHandler;
import com.power.doc.handler.JaxrsPathHandler;
import com.power.doc.helper.FormDataBuildHelper;
import com.power.doc.helper.JsonBuildHelper;
import com.power.doc.helper.ParamsBuildHelper;
import com.power.doc.model.*;
import com.power.doc.model.request.ApiRequestExample;
import com.power.doc.model.request.CurlRequest;
import com.power.doc.model.request.JaxrsPathMapping;
import com.power.doc.utils.*;
import com.thoughtworks.qdox.model.*;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.power.doc.constants.DocGlobalConstants.*;
import static com.power.doc.constants.DocTags.IGNORE;
import static com.power.doc.constants.DocTags.IGNORE_REQUEST_BODY_ADVICE;
/**
* Build documents for JAX RS
*
* @author Zxq
* @since 2021/7/15
*/
public class JaxrsDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
private static Logger log = Logger.getLogger(SpringBootDocBuildTemplate.class.getName());
/**
* api index
*/
private final AtomicInteger atomicInteger = new AtomicInteger(1);
/**
* headers
*/
private List<ApiReqParam> headers;
@Override
public List<ApiDoc> getApiData(ProjectDocConfigBuilder projectBuilder) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
this.headers = apiConfig.getRequestHeaders();
List<ApiDoc> apiDocList = new ArrayList<>();
int order = 0;
Collection<JavaClass> classes = projectBuilder.getJavaProjectBuilder().getClasses();
boolean setCustomOrder = false;
// exclude class is ignore
for (JavaClass cls : classes) {
if (StringUtil.isNotEmpty(apiConfig.getPackageFilters())) {
// from smart config
if (!DocUtil.isMatch(apiConfig.getPackageFilters(), cls.getCanonicalName())) {
continue;
}
}
// from tag
DocletTag ignoreTag = cls.getTagByName(DocTags.IGNORE);
if (!checkController(cls) || Objects.nonNull(ignoreTag)) {
continue;
}
String strOrder = JavaClassUtil.getClassTagsValue(cls, DocTags.ORDER, Boolean.TRUE);
order++;
if (ValidateUtil.isNonnegativeInteger(strOrder)) {
setCustomOrder = true;
order = Integer.parseInt(strOrder);
}
List<ApiMethodDoc> apiMethodDocs = buildControllerMethod(cls, apiConfig, projectBuilder);
this.handleApiDoc(cls, apiDocList, apiMethodDocs, order, apiConfig.isMd5EncryptedHtmlName());
}
// sort
if (apiConfig.isSortByTitle()) {
Collections.sort(apiDocList);
} else if (setCustomOrder) {
// while set custom oder
return apiDocList.stream()
.sorted(Comparator.comparing(ApiDoc::getOrder))
.peek(p -> p.setOrder(atomicInteger.getAndAdd(1))).collect(Collectors.toList());
}
return apiDocList;
}
@Override
public ApiDoc getSingleApiData(ProjectDocConfigBuilder projectBuilder, String apiClassName) {
return null;
}
@Override
public boolean ignoreReturnObject(String typeName, List<String> ignoreParams) {
return false;
}
/**
* Analyze resource method
*
* @param cls cls
* @param apiConfig apiConfig
* @param projectBuilder projectBuilder
* @return List<ApiMethodDoc>
*/
private List<ApiMethodDoc> buildControllerMethod(final JavaClass cls, ApiConfig apiConfig,
ProjectDocConfigBuilder projectBuilder) {
String clazName = cls.getCanonicalName();
boolean paramsDataToTree = projectBuilder.getApiConfig().isParamsDataToTree();
String group = JavaClassUtil.getClassTagsValue(cls, DocTags.GROUP, Boolean.TRUE);
String classAuthor = JavaClassUtil.getClassTagsValue(cls, DocTags.AUTHOR, Boolean.TRUE);
List<JavaAnnotation> classAnnotations = this.getAnnotations(cls);
String baseUrl = "";
for (JavaAnnotation annotation : classAnnotations) {
String annotationName = annotation.getType().getValue();
if (JAXRSAnnotations.JAX_PATH.equals(annotationName) ||
DocGlobalConstants.JAX_PATH_FULLY.equals(annotationName)) {
baseUrl = StringUtil.removeQuotes(DocUtil.getRequestHeaderValue(annotation));
}
}
List<JavaMethod> methods = cls.getMethods();
List<DocJavaMethod> docJavaMethods = new ArrayList<>(methods.size());
// filter private method
for (JavaMethod method : methods) {
if (method.isPrivate()) {
continue;
}
docJavaMethods.add(DocJavaMethod.builder().setJavaMethod(method));
}
JavaClass parentClass = cls.getSuperJavaClass();
if (Objects.nonNull(parentClass) && !"Object".equals(parentClass.getSimpleName())) {
Map<String, JavaType> actualTypesMap = JavaClassUtil.getActualTypesMap(parentClass);
List<JavaMethod> parentMethodList = parentClass.getMethods();
for (JavaMethod method : parentMethodList) {
docJavaMethods.add(DocJavaMethod.builder().setJavaMethod(method).setActualTypesMap(actualTypesMap));
}
}
List<ApiMethodDoc> methodDocList = new ArrayList<>(methods.size());
int methodOrder = 0;
for (DocJavaMethod docJavaMethod : docJavaMethods) {
JavaMethod method = docJavaMethod.getJavaMethod();
if (checkCondition(method)) continue;
// new api doc
//handle request mapping
JaxrsPathMapping jaxPathMapping = new JaxrsPathHandler()
.handle(projectBuilder, baseUrl, method);
if (Objects.isNull(jaxPathMapping)) {
continue;
}
if (StringUtil.isEmpty(method.getComment()) && apiConfig.isStrict()) {
throw new RuntimeException("Unable to find comment for method " + method.getName() + " in " + cls.getCanonicalName());
}
ApiMethodDoc apiMethodDoc = new ApiMethodDoc();
DocletTag downloadTag = method.getTagByName(DocTags.DOWNLOAD);
if (Objects.nonNull(downloadTag)) {
apiMethodDoc.setDownload(true);
}
DocletTag pageTag = method.getTagByName(DocTags.PAGE);
if (Objects.nonNull(pageTag)) {
String pageUrl = projectBuilder.getServerUrl() + "/" + pageTag.getValue();
apiMethodDoc.setPage(UrlUtil.simplifyUrl(pageUrl));
}
DocletTag docletTag = method.getTagByName(DocTags.GROUP);
apiMethodDoc.setGroup(group);
if (Objects.nonNull(docletTag)) {
apiMethodDoc.setGroup(docletTag.getValue());
}
methodOrder++;
apiMethodDoc.setName(method.getName());
apiMethodDoc.setOrder(methodOrder);
String comment = DocUtil.getEscapeAndCleanComment(method.getComment());
apiMethodDoc.setDesc(comment);
String methodUid = DocUtil.generateId(clazName + method.getName());
apiMethodDoc.setMethodId(methodUid);
String apiNoteValue = DocUtil.getNormalTagComments(method, DocTags.API_NOTE, cls.getName());
if (StringUtil.isEmpty(apiNoteValue)) {
apiNoteValue = method.getComment();
}
Map<String, String> authorMap = DocUtil.getParamsComments(method, DocTags.AUTHOR, cls.getName());
String authorValue = String.join(", ", new ArrayList<>(authorMap.keySet()));
if (apiConfig.isShowAuthor() && StringUtil.isNotEmpty(authorValue)) {
apiMethodDoc.setAuthor(JsonUtil.toPrettyFormat(authorValue));
}
if (apiConfig.isShowAuthor() && StringUtil.isEmpty(authorValue)) {
apiMethodDoc.setAuthor(classAuthor);
}
apiMethodDoc.setDetail(apiNoteValue);
List<ApiReqParam> ApiReqParams = new JaxrsHeaderHandler().handle(method, projectBuilder);
apiMethodDoc.setType(jaxPathMapping.getMethodType());
apiMethodDoc.setUrl(jaxPathMapping.getUrl());
apiMethodDoc.setServerUrl(projectBuilder.getServerUrl());
apiMethodDoc.setPath(jaxPathMapping.getShortUrl());
apiMethodDoc.setDeprecated(jaxPathMapping.isDeprecated());
List<JavaParameter> javaParameters = method.getParameters();
setTornaArrayTags(javaParameters, apiMethodDoc, docJavaMethod.getJavaMethod().getReturns());
// apiMethodDoc.setIsRequestArray();
ApiMethodReqParam apiMethodReqParam = requestParams(docJavaMethod, projectBuilder);
// build request params
if (paramsDataToTree) {
apiMethodDoc.setPathParams(ApiParamTreeUtil.apiParamToTree(apiMethodReqParam.getPathParams()));
apiMethodDoc.setQueryParams(ApiParamTreeUtil.apiParamToTree(apiMethodReqParam.getQueryParams()));
apiMethodDoc.setRequestParams(ApiParamTreeUtil.apiParamToTree(apiMethodReqParam.getRequestParams()));
} else {
apiMethodDoc.setPathParams(apiMethodReqParam.getPathParams());
apiMethodDoc.setQueryParams(apiMethodReqParam.getQueryParams());
apiMethodDoc.setRequestParams(apiMethodReqParam.getRequestParams());
}
List<ApiReqParam> allApiReqParams;
allApiReqParams = ApiReqParams;
if (this.headers != null) {
allApiReqParams = Stream.of(this.headers, ApiReqParams)
.flatMap(Collection::stream).distinct().collect(Collectors.toList());
}
allApiReqParams.removeIf(ApiReqParam -> {
if (StringUtil.isEmpty(ApiReqParam.getPathPatterns())
&& StringUtil.isEmpty(ApiReqParam.getExcludePathPatterns())) {
return false;
} else {
boolean flag = DocPathUtil.matches(jaxPathMapping.getShortUrl(), ApiReqParam.getPathPatterns()
, ApiReqParam.getExcludePathPatterns());
return !flag;
}
});
//reduce create in template
apiMethodDoc.setHeaders(this.createDocRenderHeaders(allApiReqParams, apiConfig.isAdoc()));
apiMethodDoc.setRequestHeaders(allApiReqParams);
// build request json
ApiRequestExample requestExample = buildReqJson(docJavaMethod, apiMethodDoc, jaxPathMapping.getMethodType(),
projectBuilder);
String requestJson = requestExample.getExampleBody();
// set request example detail
apiMethodDoc.setRequestExample(requestExample);
apiMethodDoc.setRequestUsage(requestJson == null ? requestExample.getUrl() : requestJson);
// build response usage
String responseValue = DocUtil.getNormalTagComments(method, DocTags.API_RESPONSE, cls.getName());
if (StringUtil.isNotEmpty(responseValue)) {
apiMethodDoc.setResponseUsage(responseValue);
} else {
apiMethodDoc.setResponseUsage(JsonBuildHelper.buildReturnJson(docJavaMethod, projectBuilder));
}
// auto mark file download
if (Objects.isNull(docletTag)) {
apiMethodDoc.setDownload(docJavaMethod.isDownload());
}
// build response params
List<ApiParam> responseParams = buildReturnApiParams(docJavaMethod, projectBuilder);
if (paramsDataToTree) {
responseParams = ApiParamTreeUtil.apiParamToTree(responseParams);
}
apiMethodDoc.setReturnSchema(docJavaMethod.getReturnSchema());
apiMethodDoc.setRequestSchema(docJavaMethod.getRequestSchema());
apiMethodDoc.setResponseParams(responseParams);
methodDocList.add(apiMethodDoc);
}
return methodDocList;
}
/**
* @param method method
* @return boolean
*/
private boolean checkCondition(JavaMethod method) {
return method.isPrivate() || Objects.nonNull(method.getTagByName(IGNORE));
}
/**
* getAnnotations
*
* @param cls java-class
* @return All javaAnnotation
*/
private List<JavaAnnotation> getAnnotations(JavaClass cls) {
List<JavaAnnotation> annotationsList = new ArrayList<>(cls.getAnnotations());
boolean flag = annotationsList.stream().anyMatch(item -> {
String annotationName = item.getType().getValue();
return JAXRSAnnotations.JAX_PATH.equals(annotationName) ||
JAX_PATH_FULLY.equals(annotationName);
});
// child override parent set
if (flag) {
return annotationsList;
}
JavaClass superJavaClass = cls.getSuperJavaClass();
if (Objects.nonNull(superJavaClass) && !"Object".equals(superJavaClass.getSimpleName())) {
annotationsList.addAll(getAnnotations(superJavaClass));
}
return annotationsList;
}
/**
* build request params
*
* @param docJavaMethod docJavaMethod
* @param builder builder
* @return ApiMethodReqParam
*/
private ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder) {
JavaMethod javaMethod = docJavaMethod.getJavaMethod();
boolean isStrict = builder.getApiConfig().isStrict();
String className = javaMethod.getDeclaringClass().getCanonicalName();
Map<String, String> replacementMap = builder.getReplaceClassMap();
Map<String, String> paramTagMap = DocUtil.getParamsComments(javaMethod, DocTags.PARAM, className);
Map<String, String> paramsComments = DocUtil.getParamsComments(javaMethod, DocTags.PARAM, null);
List<ApiParam> paramList = new ArrayList<>();
List<JavaParameter> parameterList = javaMethod.getParameters();
if (parameterList.size() < 1) {
return ApiMethodReqParam.builder()
.setPathParams(new ArrayList<>(0))
.setQueryParams(paramList)
.setRequestParams(new ArrayList<>(0));
}
Map<String, String> constantsMap = builder.getConstantsMap();
boolean requestFieldToUnderline = builder.getApiConfig().isRequestFieldToUnderline();
Set<String> ignoreSets = ignoreParamsSets(javaMethod);
Map<String, JavaType> actualTypesMap = docJavaMethod.getActualTypesMap();
out:
for (JavaParameter parameter : parameterList) {
String paramName = parameter.getName();
if (ignoreSets.contains(paramName)) {
continue;
}
JavaType javaType = parameter.getType();
if (Objects.nonNull(actualTypesMap) && Objects.nonNull(actualTypesMap.get(javaType.getCanonicalName()))) {
javaType = actualTypesMap.get(javaType.getCanonicalName());
}
String typeName = javaType.getGenericCanonicalName();
String simpleName = javaType.getValue().toLowerCase();
String fullTypeName = javaType.getFullyQualifiedName();
String simpleTypeName = javaType.getValue();
String commentClass = paramTagMap.get(paramName);
String rewriteClassName = getRewriteClassName(replacementMap, fullTypeName, commentClass);
// rewrite class
if (DocUtil.isClassName(rewriteClassName)) {
typeName = rewriteClassName;
fullTypeName = DocClassUtil.getSimpleName(rewriteClassName);
}
if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, builder.getApiConfig().getIgnoreRequestParams())) {
continue;
}
fullTypeName = DocClassUtil.rewriteRequestParam(fullTypeName);
typeName = DocClassUtil.rewriteRequestParam(typeName);
if (!paramTagMap.containsKey(paramName) && JavaClassValidateUtil.isPrimitive(fullTypeName) && isStrict) {
throw new RuntimeException("ERROR: Unable to find javadoc @QueryParam for actual param \""
+ paramName + "\" in method " + javaMethod.getName() + " from " + className);
}
String comment = this.paramCommentResolve(paramTagMap.get(paramName));
if (requestFieldToUnderline) {
paramName = StringUtil.camelToUnderline(paramName);
}
//file upload
if (JavaClassValidateUtil.isFile(typeName)) {
ApiParam param = ApiParam.of().setField(paramName).setType("file")
.setId(paramList.size() + 1).setQueryParam(true)
.setRequired(true).setVersion(DocGlobalConstants.DEFAULT_VERSION)
.setDesc(comment);
if (typeName.contains("[]") || typeName.endsWith(">")) {
comment = comment + "(array of file)";
param.setDesc(comment);
param.setHasItems(true);
}
paramList.add(param);
continue;
}
String mockValue = createMockValue(paramsComments, paramName, typeName, simpleTypeName);
JavaClass javaClass = builder.getJavaProjectBuilder().getClassByName(fullTypeName);
List<JavaAnnotation> annotations = parameter.getAnnotations();
List<String> groupClasses = JavaClassUtil.getParamGroupJavaClass(annotations);
String strRequired = "false";
boolean isPathVariable = false;
boolean isRequestBody = false;
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getValue();
if (JAXRSAnnotations.JAX_HEADER_PARAM.equals(annotationName)) {
continue out;
}
// path param
if (JAXRSAnnotations.JAX_PATH_PARAM.equals(annotationName) ) {
isPathVariable = true;
paramName = getParamName(paramName, annotation);
for (Map.Entry<String, String> entry : constantsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (paramName.contains(key)) {
paramName = paramName.replace(key, value);
}
// replace mockValue
if (mockValue.contains(key)) {
mockValue = mockValue.replace(key, value);
}
}
}
}
boolean required = false;
boolean queryParam = !isPathVariable;
if (JavaClassValidateUtil.isCollection(fullTypeName) || JavaClassValidateUtil.isArray(fullTypeName)) {
if (JavaClassValidateUtil.isCollection(typeName)) {
typeName = typeName + "<T>";
}
String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName);
String gicName = gicNameArr[0];
if (JavaClassValidateUtil.isArray(gicName)) {
gicName = gicName.substring(0, gicName.indexOf("["));
}
JavaClass gicJavaClass = builder.getJavaProjectBuilder().getClassByName(gicName);
if (gicJavaClass.isEnum()) {
Object value = JavaClassUtil.getEnumValue(gicJavaClass, Boolean.TRUE);
ApiParam param = ApiParam.of().setField(paramName).setDesc(comment + ",[array of enum]")
.setRequired(required)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setId(paramList.size() + 1)
.setType("array").setValue(String.valueOf(value));
paramList.add(param);
} else if (JavaClassValidateUtil.isPrimitive(gicName)) {
String shortSimple = DocClassUtil.processTypeNameForParams(gicName);
ApiParam param = ApiParam.of()
.setField(paramName)
.setDesc(comment + ",[array of " + shortSimple + "]")
.setRequired(required)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setId(paramList.size() + 1)
.setType("array")
.setValue(DocUtil.getValByTypeAndFieldName(gicName, paramName));
paramList.add(param);
} else {
throw new RuntimeException("Dubbo Rest can't support binding Collection on method "
+ javaMethod.getName() + ",Check it in " + javaMethod.getDeclaringClass()
.getCanonicalName());
}
} else if (JavaClassValidateUtil.isPrimitive(fullTypeName)) {
ApiParam param = ApiParam.of()
.setField(paramName)
.setType(DocClassUtil.processTypeNameForParams(simpleName))
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setValue(mockValue)
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(param);
} else if (JavaClassValidateUtil.isMap(fullTypeName)) {
log.warning("When using smart-doc, it is not recommended to use Map to receive parameters, Check it in "
+ javaMethod.getDeclaringClass().getCanonicalName() + "#" + javaMethod.getName());
if (JavaClassValidateUtil.isMap(typeName)) {
ApiParam apiParam = ApiParam.of()
.setField(paramName)
.setType("map")
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(apiParam);
continue;
}
String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName);
if (JavaClassValidateUtil.isPrimitive(gicNameArr[1])) {
ApiParam apiParam = ApiParam.of()
.setField(paramName)
.setType("map")
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(apiParam);
} else {
paramList.addAll(ParamsBuildHelper.buildParams(gicNameArr[1], DocGlobalConstants.EMPTY, 0,
"true", Boolean.FALSE, new HashMap<>(),
builder, groupClasses, 0, Boolean.FALSE));
}
}
// param is enum
else if (javaClass.isEnum()) {
String o = JavaClassUtil.getEnumParams(javaClass);
Object value = JavaClassUtil.getEnumValue(javaClass, true);
ApiParam param = ApiParam.of().setField(paramName)
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setValue(String.valueOf(value))
.setType("enum").setDesc(StringUtil.removeQuotes(o))
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION)
.setEnumValues(JavaClassUtil.getEnumValues(javaClass));
paramList.add(param);
} else {
paramList.addAll(ParamsBuildHelper.buildParams(typeName, DocGlobalConstants.EMPTY, 0,
"true", Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0, Boolean.FALSE));
}
}
List<ApiParam> pathParams = new ArrayList<>();
List<ApiParam> queryParams = new ArrayList<>();
List<ApiParam> bodyParams = new ArrayList<>();
for (ApiParam param : paramList) {
param.setValue(StringUtil.removeDoubleQuotes(param.getValue()));
if (param.isPathParam()) {
param.setId(pathParams.size() + 1);
pathParams.add(param);
} else {
param.setId(queryParams.size() + 1);
queryParams.add(param);
}
}
return ApiMethodReqParam.builder()
.setRequestParams(bodyParams)
.setPathParams(pathParams)
.setQueryParams(queryParams);
}
private String getParamName(String paramName, JavaAnnotation annotation) {
AnnotationValue annotationValue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP);
if (Objects.nonNull(annotationValue)) {
paramName = StringUtil.removeQuotes(annotationValue.toString());
}
AnnotationValue annotationOfName = annotation.getProperty(DocAnnotationConstants.NAME_PROP);
if (Objects.nonNull(annotationOfName)) {
paramName = StringUtil.removeQuotes(annotationOfName.toString());
}
return paramName;
}
private String createMockValue(Map<String, String> paramsComments, String paramName, String typeName, String simpleTypeName) {
String mockValue = "";
if (JavaClassValidateUtil.isPrimitive(typeName)) {
mockValue = paramsComments.get(paramName);
if (Objects.nonNull(mockValue) && mockValue.contains("|")) {
mockValue = mockValue.substring(mockValue.lastIndexOf("|") + 1);
} else {
mockValue = "";
}
if (StringUtil.isEmpty(mockValue)) {
mockValue = DocUtil.getValByTypeAndFieldName(simpleTypeName, paramName, Boolean.TRUE);
}
}
return mockValue;
}
private static void setTornaArrayTags(List<JavaParameter> javaParameters, ApiMethodDoc apiMethodDoc, JavaClass returnClass) {
apiMethodDoc.setIsResponseArray(0);
apiMethodDoc.setIsRequestArray(0);
//response tags
if (JavaClassValidateUtil.isCollection(returnClass.getFullyQualifiedName()) ||
JavaClassValidateUtil.isArray(returnClass.getFullyQualifiedName())) {
apiMethodDoc.setIsResponseArray(1);
String gicType;
String simpleGicType;
String typeName = returnClass.getGenericFullyQualifiedName();
gicType = getType(typeName);
simpleGicType = gicType.substring(gicType.lastIndexOf(".") + 1).toLowerCase();
apiMethodDoc.setResponseArrayType(JavaClassValidateUtil.isPrimitive(gicType) ? simpleGicType : OBJECT);
}
//request tags
if (CollectionUtil.isNotEmpty(javaParameters)) {
for (JavaParameter parameter : javaParameters) {
String gicType;
String simpleGicType;
String typeName = parameter.getType().getGenericFullyQualifiedName();
String name = parameter.getType().getFullyQualifiedName();
gicType = getType(typeName);
simpleGicType = gicType.substring(gicType.lastIndexOf(".") + 1).toLowerCase();
// is array
if (JavaClassValidateUtil.isCollection(name) || JavaClassValidateUtil.isArray(name)) {
boolean hasRequestBody = false;
//param has @RequestBody ?
List<JavaAnnotation> annotations = parameter.getAnnotations();
for (JavaAnnotation annotation : annotations) {
if (REQUEST_BODY_FULLY.equals(annotation.getType().getName())) {
hasRequestBody = true;
break;
}
}
//formData - multiple data
if (!hasRequestBody && javaParameters.size() > 1) {
return;
} else {
apiMethodDoc.setIsRequestArray(1);
if (JavaClassValidateUtil.isPrimitive(gicType)) {
apiMethodDoc.setRequestArrayType(simpleGicType);
} else {
apiMethodDoc.setRequestArrayType(OBJECT);
}
}
}
}
}
}
private ApiRequestExample buildReqJson(DocJavaMethod javaMethod, ApiMethodDoc apiMethodDoc, String methodType,
ProjectDocConfigBuilder configBuilder) {
JavaMethod method = javaMethod.getJavaMethod();
Map<String, String> pathParamsMap = new LinkedHashMap<>();
List<JavaAnnotation> methodAnnotations = method.getAnnotations();
List<JavaParameter> parameterList = method.getParameters();
List<ApiReqParam> reqHeaderList = apiMethodDoc.getRequestHeaders();
if (parameterList.size() < 1) {
CurlRequest curlRequest = CurlRequest.builder()
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(apiMethodDoc.getUrl());
String format = CurlUtil.toCurl(curlRequest);
return ApiRequestExample.builder().setUrl(apiMethodDoc.getUrl()).setExampleBody(format);
}
Set<String> ignoreSets = ignoreParamsSets(method);
Map<String, JavaType> actualTypesMap = javaMethod.getActualTypesMap();
Map<String, String> constantsMap = configBuilder.getConstantsMap();
boolean requestFieldToUnderline = configBuilder.getApiConfig().isRequestFieldToUnderline();
Map<String, String> replacementMap = configBuilder.getReplaceClassMap();
Map<String, String> paramsComments = DocUtil.getParamsComments(method, DocTags.PARAM, null);
List<FormData> formDataList = new ArrayList<>();
ApiRequestExample requestExample = ApiRequestExample.builder();
for (JavaParameter parameter : parameterList) {
JavaType javaType = parameter.getType();
if (Objects.nonNull(actualTypesMap) && Objects.nonNull(actualTypesMap.get(javaType.getCanonicalName()))) {
javaType = actualTypesMap.get(javaType.getCanonicalName());
}
String paramName = parameter.getName();
if (ignoreSets.contains(paramName)) {
continue;
}
String typeName = javaType.getFullyQualifiedName();
String gicTypeName = javaType.getGenericCanonicalName();
String commentClass = paramsComments.get(paramName);
//ignore request params
if (Objects.nonNull(commentClass) && commentClass.contains(IGNORE)) {
continue;
}
String rewriteClassName = this.getRewriteClassName(replacementMap, typeName, commentClass);
// rewrite class
if (DocUtil.isClassName(rewriteClassName)) {
gicTypeName = rewriteClassName;
typeName = DocClassUtil.getSimpleName(rewriteClassName);
}
if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, configBuilder.getApiConfig()
.getIgnoreRequestParams())) {
continue;
}
String simpleTypeName = javaType.getValue();
typeName = DocClassUtil.rewriteRequestParam(typeName);
gicTypeName = DocClassUtil.rewriteRequestParam(gicTypeName);
//if params is collection
if (JavaClassValidateUtil.isCollection(typeName)) {
apiMethodDoc.setListParam(true);
}
JavaClass javaClass = configBuilder.getJavaProjectBuilder().getClassByName(typeName);
String[] globGicName = DocClassUtil.getSimpleGicName(gicTypeName);
String comment = this.paramCommentResolve(paramsComments.get(paramName));
String mockValue = createMockValue(paramsComments, paramName, typeName, simpleTypeName);
if (requestFieldToUnderline) {
paramName = StringUtil.camelToUnderline(paramName);
}
List<JavaAnnotation> annotations = parameter.getAnnotations();
List<String> groupClasses = JavaClassUtil.getParamGroupJavaClass(annotations);
boolean paramAdded = false;
for (JavaAnnotation annotation : methodAnnotations) {
String annotationName = annotation.getType().getValue();
if (JAXRSAnnotations.JAX_CONSUMES.equals(annotationName) || paramAdded) {
AnnotationValue avalue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP);
if (avalue != null && avalue.toString().contains("json") || avalue.toString().contains("JSON")) {
paramName = getParamName(paramName, annotation);
for (Map.Entry<String, String> entry : constantsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// replace param
if (paramName.contains(key)) {
paramName = paramName.replace(key, value);
}
// replace mockValue
if (mockValue.contains(key)) {
mockValue = mockValue.replace(key, value);
}
}
apiMethodDoc.setContentType(JSON_CONTENT_TYPE);
if (Objects.nonNull(configBuilder.getApiConfig().getRequestBodyAdvice())
&& Objects.isNull(method.getTagByName(IGNORE_REQUEST_BODY_ADVICE))) {
String requestBodyAdvice = configBuilder.getApiConfig()
.getRequestBodyAdvice()
.getClassName();
typeName = configBuilder.getApiConfig().getRequestBodyAdvice().getClassName();
gicTypeName = requestBodyAdvice + "<" + gicTypeName + ">";
}
if (JavaClassValidateUtil.isPrimitive(simpleTypeName)) {
StringBuilder builder = new StringBuilder();
builder.append("{\"")
.append(paramName)
.append("\":")
.append(DocUtil.handleJsonStr(mockValue))
.append("}");
requestExample.setJsonBody(JsonUtil.toPrettyFormat(builder.toString())).setJson(true);
} else {
String json = JsonBuildHelper.buildJson(typeName, gicTypeName, Boolean.FALSE, 0, new HashMap<>(),groupClasses, configBuilder);
requestExample.setJsonBody(JsonUtil.toPrettyFormat(json)).setJson(true);
}
paramAdded = true;
}
}
if (JAXRSAnnotations.JAX_PATH_PARAM.equals(annotationName)) {
if (javaClass.isEnum()) {
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.TRUE);
mockValue = StringUtil.removeQuotes(String.valueOf(value));
}
pathParamsMap.put(paramName, mockValue);
paramAdded = true;
}
if (paramAdded) {
continue;
}
//file upload
if (JavaClassValidateUtil.isFile(gicTypeName)) {
apiMethodDoc.setContentType(FILE_CONTENT_TYPE);
FormData formData = new FormData();
formData.setKey(paramName);
formData.setType("file");
formData.setDescription(comment);
formData.setValue(mockValue);
formDataList.add(formData);
} else if (JavaClassValidateUtil.isPrimitive(typeName)) {
FormData formData = new FormData();
formData.setKey(paramName);
formData.setDescription(comment);
formData.setType("text");
formData.setValue(mockValue);
formDataList.add(formData);
} else if (JavaClassValidateUtil.isArray(typeName) || JavaClassValidateUtil.isCollection(typeName)) {
String gicName = globGicName[0];
if (JavaClassValidateUtil.isArray(gicName)) {
gicName = gicName.substring(0, gicName.indexOf("["));
}
if (!JavaClassValidateUtil.isPrimitive(gicName)
&& !configBuilder.getJavaProjectBuilder().getClassByName(gicName).isEnum()) {
throw new RuntimeException("Dubbo rest can't support binding Collection on method "
+ method.getName() + "Check it in " + method.getDeclaringClass().getCanonicalName());
}
FormData formData = new FormData();
formData.setKey(paramName);
if (!paramName.contains("[]")) {
formData.setKey(paramName + "[]");
}
formData.setDescription(comment);
formData.setType("text");
formData.setValue(RandomUtil.randomValueByType(gicName));
formDataList.add(formData);
} else if (javaClass.isEnum()) {
// do nothing
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.TRUE);
String strVal = StringUtil.removeQuotes(String.valueOf(value));
FormData formData = new FormData();
formData.setKey(paramName);
formData.setType("text");
formData.setDescription(comment);
formData.setValue(strVal);
formDataList.add(formData);
} else {
formDataList.addAll(FormDataBuildHelper.getFormData(gicTypeName, new HashMap<>(), 0, configBuilder, DocGlobalConstants.EMPTY));
}
}
}
requestExample.setFormDataList(formDataList);
String[] paths = apiMethodDoc.getPath().split(";");
String path = paths[0];
String body;
String exampleBody;
String url;
if (Methods.POST.getValue()
.equals(methodType) || Methods.PUT.getValue()
.equals(methodType)) {
//for post put
path = DocUtil.formatAndRemove(path, pathParamsMap);
body = UrlUtil.urlJoin(DocGlobalConstants.EMPTY, DocUtil.formDataToMap(formDataList))
.replace("?", DocGlobalConstants.EMPTY);
body = StringUtil.removeQuotes(body);
url = apiMethodDoc.getServerUrl() + "/" + path;
url = UrlUtil.simplifyUrl(url);
if (requestExample.isJson()) {
if (StringUtil.isNotEmpty(body)) {
url = url + "?" + body;
}
CurlRequest curlRequest = CurlRequest.builder()
.setBody(requestExample.getJsonBody())
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(url);
exampleBody = CurlUtil.toCurl(curlRequest);
} else {
CurlRequest curlRequest;
if (StringUtil.isNotEmpty(body)) {
curlRequest = CurlRequest.builder()
.setBody(body)
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(url);
} else {
curlRequest = CurlRequest.builder()
.setBody(requestExample.getJsonBody())
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(url);
}
exampleBody = CurlUtil.toCurl(curlRequest);
}
requestExample.setExampleBody(exampleBody).setUrl(url);
} else {
// for get delete
pathParamsMap.putAll(DocUtil.formDataToMap(formDataList));
path = DocUtil.formatAndRemove(path, pathParamsMap);
url = UrlUtil.urlJoin(path, pathParamsMap);
url = StringUtil.removeQuotes(url);
url = apiMethodDoc.getServerUrl() + "/" + url;
url = UrlUtil.simplifyUrl(url);
CurlRequest curlRequest = CurlRequest.builder()
.setBody(requestExample.getJsonBody())
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(url);
exampleBody = CurlUtil.toCurl(curlRequest);
requestExample.setExampleBody(exampleBody)
.setJsonBody(DocGlobalConstants.EMPTY)
.setUrl(url);
}
return requestExample;
}
private String getRewriteClassName(Map<String, String> replacementMap, String fullTypeName, String commentClass) {
String rewriteClassName;
if (Objects.nonNull(commentClass) && !DocGlobalConstants.NO_COMMENTS_FOUND.equals(commentClass)) {
String[] comments = commentClass.split("\\|");
rewriteClassName = comments[comments.length - 1];
if (DocUtil.isClassName(rewriteClassName)) {
return rewriteClassName;
}
}
return replacementMap.get(fullTypeName);
}
private static String getType(String typeName) {
String gicType;
//get generic type
if (typeName.contains("<")) {
gicType = typeName.substring(typeName.indexOf("<") + 1, typeName.lastIndexOf(">"));
} else {
gicType = typeName;
}
if (gicType.contains("[")) {
gicType = gicType.substring(0, gicType.indexOf("["));
}
return gicType;
}
private boolean checkController(JavaClass cls) {
if (cls.isAnnotation() || cls.isEnum()) {
return false;
}
JavaClass superClass = cls.getSuperJavaClass();
List<JavaAnnotation> classAnnotations = new ArrayList<>();
if (Objects.nonNull(superClass)) {
classAnnotations.addAll(superClass.getAnnotations());
}
classAnnotations.addAll(cls.getAnnotations());
for (JavaAnnotation annotation : classAnnotations) {
String name = annotation.getType().getValue();
if (JAXRSAnnotations.JAX_PATH.equals(name)) {
return true;
}
}
// use custom doc tag to support Feign.
List<DocletTag> docletTags = cls.getTags();
for (DocletTag docletTag : docletTags) {
String value = docletTag.getName();
if (DocTags.DUBBO_REST.equals(value)) {
return true;
}
}
return false;
}
}

View File

@ -35,10 +35,7 @@ import com.power.doc.model.ApiParam;
import com.power.doc.model.DocJavaMethod;
import com.power.doc.model.JavaMethodDoc;
import com.power.doc.model.rpc.RpcApiDoc;
import com.power.doc.utils.DocClassUtil;
import com.power.doc.utils.DocUtil;
import com.power.doc.utils.JavaClassUtil;
import com.power.doc.utils.JavaClassValidateUtil;
import com.power.doc.utils.*;
import com.thoughtworks.qdox.model.*;
import java.util.*;
@ -70,7 +67,8 @@ public class RpcDocBuildTemplate implements IDocBuildTemplate<RpcApiDoc> {
continue;
}
}
if (!checkDubboInterface(cls)) {
DocletTag ignoreTag = cls.getTagByName(DocTags.IGNORE);
if (!checkDubboInterface(cls)|| Objects.nonNull(ignoreTag)) {
continue;
}
String strOrder = JavaClassUtil.getClassTagsValue(cls, DocTags.ORDER, Boolean.TRUE);
@ -152,12 +150,20 @@ public class RpcDocBuildTemplate implements IDocBuildTemplate<RpcApiDoc> {
continue;
}
apiMethodDoc.setDeprecated(deprecated);
// build request params
List<ApiParam> requestParams = requestParams(method, projectBuilder);
apiMethodDoc.setRequestParams(requestParams);
// build response params
List<ApiParam> responseParams = buildReturnApiParams(DocJavaMethod.builder().setJavaMethod(method), projectBuilder);
apiMethodDoc.setResponseParams(responseParams);
if (apiConfig.isParamsDataToTree()) {
apiMethodDoc.setRequestParams(ApiParamTreeUtil.apiParamToTree(requestParams));
apiMethodDoc.setResponseParams(ApiParamTreeUtil.apiParamToTree(responseParams));
} else {
apiMethodDoc.setRequestParams(requestParams);
apiMethodDoc.setResponseParams(responseParams);
}
methodDocList.add(apiMethodDoc);
}

View File

@ -37,6 +37,7 @@ import com.power.doc.model.request.RequestMapping;
import com.power.doc.utils.*;
import com.thoughtworks.qdox.model.*;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import org.apache.commons.lang3.ArrayUtils;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@ -44,8 +45,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.power.doc.constants.DocGlobalConstants.FILE_CONTENT_TYPE;
import static com.power.doc.constants.DocGlobalConstants.JSON_CONTENT_TYPE;
import static com.power.doc.constants.DocGlobalConstants.*;
import static com.power.doc.constants.DocTags.IGNORE;
import static com.power.doc.constants.DocTags.IGNORE_REQUEST_BODY_ADVICE;
@ -59,12 +59,13 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
* api index
*/
private final AtomicInteger atomicInteger = new AtomicInteger(1);
private List<ApiReqHeader> headers;
private List<ApiReqParam> configApiReqParams;
@Override
public List<ApiDoc> getApiData(ProjectDocConfigBuilder projectBuilder) {
ApiConfig apiConfig = projectBuilder.getApiConfig();
this.headers = apiConfig.getRequestHeaders();
this.configApiReqParams = Stream.of(apiConfig.getRequestHeaders(), apiConfig.getRequestParams()).filter(Objects::nonNull)
.flatMap(Collection::stream).collect(Collectors.toList());
List<ApiDoc> apiDocList = new ArrayList<>();
int order = 0;
Collection<JavaClass> classes = projectBuilder.getJavaProjectBuilder().getClasses();
@ -75,8 +76,8 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
continue;
}
}
String ignoreTag = JavaClassUtil.getClassTagsValue(cls, DocTags.IGNORE, Boolean.FALSE);
if (!checkController(cls) || StringUtil.isNotEmpty(ignoreTag)) {
DocletTag ignoreTag = cls.getTagByName(DocTags.IGNORE);
if (!checkController(cls) || Objects.nonNull(ignoreTag)) {
continue;
}
String strOrder = JavaClassUtil.getClassTagsValue(cls, DocTags.ORDER, Boolean.TRUE);
@ -88,6 +89,9 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
List<ApiMethodDoc> apiMethodDocs = buildControllerMethod(cls, apiConfig, projectBuilder);
this.handleApiDoc(cls, apiDocList, apiMethodDocs, order, apiConfig.isMd5EncryptedHtmlName());
}
// handle TagsApiDoc
apiDocList = handleTagsApiDoc(apiDocList);
// sort
if (apiConfig.isSortByTitle()) {
Collections.sort(apiDocList);
@ -100,6 +104,70 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
return apiDocList;
}
/**
* handle tags to api doc
* copy the same tag
*
* @author cqmike
*/
private List<ApiDoc> handleTagsApiDoc(List<ApiDoc> apiDocList) {
if (CollectionUtil.isEmpty(apiDocList)) {
return Collections.emptyList();
}
// all class tag copy
Map<String, ApiDoc> copyMap = new HashMap<>();
apiDocList.forEach(doc -> {
String[] tags = doc.getTags();
if (ArrayUtils.isEmpty(tags)) {
tags = new String[]{doc.getName()};
}
for (String tag : tags) {
tag = StringUtil.trim(tag);
copyMap.computeIfPresent(tag, (k, v) -> {
List<ApiMethodDoc> list = CollectionUtil.isEmpty(v.getList()) ? new ArrayList<>() : v.getList();
list.addAll(doc.getList());
v.setList(list);
return v;
});
copyMap.putIfAbsent(tag, doc);
}
});
// handle method tag
Map<String, ApiDoc> allMap = new HashMap<>(copyMap);
allMap.forEach((k, v) -> {
List<ApiMethodDoc> methodDocList = v.getList();
methodDocList.forEach(method -> {
String[] tags = method.getTags();
if (ArrayUtils.isEmpty(tags)) {
return;
}
for (String tag : tags) {
tag = StringUtil.trim(tag);
copyMap.computeIfPresent(tag, (k1, v2) -> {
method.setOrder(v2.getList().size() + 1);
v2.getList().add(method);
return v2;
});
copyMap.putIfAbsent(tag, ApiDoc.buildTagApiDoc(v, tag, method));
}
});
});
List<ApiDoc> apiDocs = new ArrayList<>(copyMap.values());
int index = apiDocs.size() - 1;
for (ApiDoc apiDoc : apiDocs) {
if (apiDoc.getOrder() == null) {
apiDoc.setOrder(index++);
}
}
apiDocs.sort(Comparator.comparing(ApiDoc::getOrder));
return apiDocs;
}
@Override
public ApiDoc getSingleApiData(ProjectDocConfigBuilder projectBuilder, String apiClassName) {
return null;
@ -107,10 +175,7 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
@Override
public boolean ignoreReturnObject(String typeName, List<String> ignoreParams) {
if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, ignoreParams)) {
return DocGlobalConstants.MODE_AND_VIEW_FULLY.equals(typeName);
}
return false;
return JavaClassValidateUtil.isMvcIgnoreParams(typeName, ignoreParams);
}
private List<ApiMethodDoc> buildControllerMethod(final JavaClass cls, ApiConfig apiConfig,
@ -128,7 +193,12 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
DocGlobalConstants.REQUEST_MAPPING_FULLY.equals(annotationName)) {
baseUrl = StringUtil.removeQuotes(DocUtil.getRequestMappingUrl(annotation));
}
//FeignClient path handle
if (DocGlobalConstants.FEIGN_CLIENT.equals(annotationName) || DocGlobalConstants.FEIGN_CLIENT_FULLY.equals(annotationName)) {
baseUrl = StringUtil.removeQuotes(DocUtil.getPathUrl(annotation, DocAnnotationConstants.PATH_PROP));
}
}
List<JavaMethod> methods = cls.getMethods();
List<DocJavaMethod> docJavaMethods = new ArrayList<>(methods.size());
for (JavaMethod method : methods) {
@ -145,6 +215,16 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
docJavaMethods.add(DocJavaMethod.builder().setJavaMethod(method).setActualTypesMap(actualTypesMap));
}
}
List<JavaType> implClasses = cls.getImplements();
for (JavaType type : implClasses) {
JavaClass javaClass = (JavaClass) type;
Map<String, JavaType> actualTypesMap = JavaClassUtil.getActualTypesMap(javaClass);
for (JavaMethod method : javaClass.getMethods()) {
if (method.isDefault()) {
docJavaMethods.add(DocJavaMethod.builder().setJavaMethod(method).setActualTypesMap(actualTypesMap));
}
}
}
List<ApiMethodDoc> methodDocList = new ArrayList<>(methods.size());
int methodOrder = 0;
for (DocJavaMethod docJavaMethod : docJavaMethods) {
@ -177,6 +257,11 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
} else {
apiMethodDoc.setGroup(group);
}
// handle tags
List<DocletTag> tags = method.getTagsByName(DocTags.TAG);
apiMethodDoc.setTags(tags.stream().map(DocletTag::getValue).toArray(String[]::new));
methodOrder++;
apiMethodDoc.setName(method.getName());
apiMethodDoc.setOrder(methodOrder);
@ -191,21 +276,28 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
Map<String, String> authorMap = DocUtil.getParamsComments(method, DocTags.AUTHOR, cls.getName());
String authorValue = String.join(", ", new ArrayList<>(authorMap.keySet()));
if (apiConfig.isShowAuthor() && StringUtil.isNotEmpty(authorValue)) {
apiMethodDoc.setAuthor(authorValue);
apiMethodDoc.setAuthor(JsonUtil.toPrettyFormat(authorValue));
}
if (apiConfig.isShowAuthor() && StringUtil.isEmpty(authorValue)) {
apiMethodDoc.setAuthor(classAuthor);
}
apiMethodDoc.setDetail(apiNoteValue);
//handle headers
List<ApiReqHeader> apiReqHeaders = new SpringMVCRequestHeaderHandler().handle(method);
List<ApiReqParam> apiReqHeaders = new SpringMVCRequestHeaderHandler().handle(method, projectBuilder);
apiMethodDoc.setType(requestMapping.getMethodType());
apiMethodDoc.setUrl(requestMapping.getUrl());
apiMethodDoc.setServerUrl(projectBuilder.getServerUrl());
apiMethodDoc.setPath(requestMapping.getShortUrl());
apiMethodDoc.setDeprecated(requestMapping.isDeprecated());
ApiMethodReqParam apiMethodReqParam = requestParams(docJavaMethod, projectBuilder);
List<JavaParameter> javaParameters = method.getParameters();
setTornaArrayTags(javaParameters, apiMethodDoc, docJavaMethod.getJavaMethod().getReturns());
// apiMethodDoc.setIsRequestArray();
final List<ApiReqParam> apiReqParamList = this.configApiReqParams.stream()
.filter(param -> filterPath(requestMapping, param)).collect(Collectors.toList());
ApiMethodReqParam apiMethodReqParam = requestParams(docJavaMethod, projectBuilder, apiReqParamList);
// build request params
if (paramsDataToTree) {
apiMethodDoc.setPathParams(ApiParamTreeUtil.apiParamToTree(apiMethodReqParam.getPathParams()));
@ -217,17 +309,32 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
apiMethodDoc.setRequestParams(apiMethodReqParam.getRequestParams());
}
List<ApiReqHeader> allApiReqHeaders;
if (this.headers != null) {
allApiReqHeaders = Stream.of(this.headers, apiReqHeaders)
List<ApiReqParam> allApiReqHeaders;
if (this.configApiReqParams != null) {
final Map<String, List<ApiReqParam>> reqParamMap = apiReqParamList.stream().collect(Collectors.groupingBy(ApiReqParam::getParamIn));
final List<ApiReqParam> headerParamList = reqParamMap.getOrDefault(ApiReqParamInTypeEnum.HEADER.getValue(), Collections.emptyList());
allApiReqHeaders = Stream.of(apiReqHeaders, headerParamList).filter(Objects::nonNull)
.flatMap(Collection::stream).distinct().collect(Collectors.toList());
} else {
allApiReqHeaders = apiReqHeaders;
allApiReqHeaders = apiReqHeaders.stream().filter(param -> filterPath(requestMapping, param)).collect(Collectors.toList());
}
//reduce create in template
apiMethodDoc.setHeaders(this.createDocRenderHeaders(allApiReqHeaders, apiConfig.isAdoc()));
apiMethodDoc.setRequestHeaders(allApiReqHeaders);
String path = apiMethodDoc.getPath().split(";")[0];
String pathUrl = DocUtil.formatPathUrl(path);
List<ApiParam> pathParams = apiMethodDoc.getPathParams();
Iterator<ApiParam> pathIterator = pathParams.iterator();
while (pathIterator.hasNext()) {
ApiParam next = pathIterator.next();
String pathKey = "{" + next.getField() + "}";
if (!pathUrl.contains(pathKey)) {
pathIterator.remove();
}
}
// build request json
ApiRequestExample requestExample = buildReqJson(docJavaMethod, apiMethodDoc, requestMapping.getMethodType(),
projectBuilder);
@ -236,7 +343,13 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
apiMethodDoc.setRequestExample(requestExample);
apiMethodDoc.setRequestUsage(requestJson == null ? requestExample.getUrl() : requestJson);
// build response usage
apiMethodDoc.setResponseUsage(JsonBuildHelper.buildReturnJson(docJavaMethod, projectBuilder));
String responseValue = DocUtil.getNormalTagComments(method, DocTags.API_RESPONSE, cls.getName());
if (StringUtil.isNotEmpty(responseValue)) {
responseValue = responseValue.replaceAll("<br>", "");
apiMethodDoc.setResponseUsage(JsonUtil.toPrettyFormat(responseValue));
} else {
apiMethodDoc.setResponseUsage(JsonBuildHelper.buildReturnJson(docJavaMethod, projectBuilder));
}
// build response params
List<ApiParam> responseParams = buildReturnApiParams(docJavaMethod, projectBuilder);
if (paramsDataToTree) {
@ -250,10 +363,28 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
return methodDocList;
}
private boolean filterPath(RequestMapping requestMapping, ApiReqParam apiReqHeader) {
if (StringUtil.isEmpty(apiReqHeader.getPathPatterns())
&& StringUtil.isEmpty(apiReqHeader.getExcludePathPatterns())) {
return true;
}
return DocPathUtil.matches(requestMapping.getShortUrl(), apiReqHeader.getPathPatterns()
, apiReqHeader.getExcludePathPatterns());
}
private ApiRequestExample buildReqJson(DocJavaMethod javaMethod, ApiMethodDoc apiMethodDoc, String methodType,
ProjectDocConfigBuilder configBuilder) {
JavaMethod method = javaMethod.getJavaMethod();
Map<String, String> pathParamsMap = new LinkedHashMap<>();
Map<String, String> queryParamsMap = new LinkedHashMap<>();
apiMethodDoc.getPathParams().stream().filter(Objects::nonNull).filter(ApiParam::isConfigParam)
.forEach(param -> pathParamsMap.put(param.getField(), param.getValue()));
apiMethodDoc.getQueryParams().stream().filter(Objects::nonNull).filter(ApiParam::isConfigParam)
.forEach(param -> queryParamsMap.put(param.getField(), param.getValue()));
List<JavaAnnotation> methodAnnotations = method.getAnnotations();
for (JavaAnnotation annotation : methodAnnotations) {
String annotationName = annotation.getType().getName();
@ -264,23 +395,29 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
String params = StringUtil.removeQuotes(paramsObjects.toString());
if (!params.startsWith("[")) {
mappingParamProcess(paramsObjects.toString(), pathParamsMap);
mappingParamProcess(paramsObjects.toString(), queryParamsMap);
continue;
}
List<String> headers = (LinkedList) paramsObjects;
for (String str : headers) {
mappingParamProcess(str, pathParamsMap);
mappingParamProcess(str, queryParamsMap);
}
}
}
List<JavaParameter> parameterList = method.getParameters();
List<ApiReqHeader> reqHeaderList = apiMethodDoc.getRequestHeaders();
List<ApiReqParam> reqHeaderList = apiMethodDoc.getRequestHeaders();
if (parameterList.size() < 1) {
String path = apiMethodDoc.getPath().split(";")[0];
path = DocUtil.formatAndRemove(path, pathParamsMap);
String url = UrlUtil.urlJoin(path, queryParamsMap);
url = StringUtil.removeQuotes(url);
url = apiMethodDoc.getServerUrl() + "/" + url;
url = UrlUtil.simplifyUrl(url);
CurlRequest curlRequest = CurlRequest.builder()
.setContentType(apiMethodDoc.getContentType())
.setType(methodType)
.setReqHeaders(reqHeaderList)
.setUrl(apiMethodDoc.getUrl());
.setUrl(url);
String format = CurlUtil.toCurl(curlRequest);
return ApiRequestExample.builder().setUrl(apiMethodDoc.getUrl()).setExampleBody(format);
}
@ -317,7 +454,8 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
gicTypeName = rewriteClassName;
typeName = DocClassUtil.getSimpleName(rewriteClassName);
}
if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, configBuilder.getApiConfig().getIgnoreRequestParams())) {
if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, configBuilder.getApiConfig()
.getIgnoreRequestParams())) {
continue;
}
String simpleTypeName = javaType.getValue();
@ -331,10 +469,14 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
String[] globGicName = DocClassUtil.getSimpleGicName(gicTypeName);
String comment = this.paramCommentResolve(paramsComments.get(paramName));
String mockValue = createMockValue(paramsComments, paramName, typeName, simpleTypeName);
if (queryParamsMap.containsKey(paramName)) {
mockValue = queryParamsMap.get(paramName);
}
if (requestFieldToUnderline) {
paramName = StringUtil.camelToUnderline(paramName);
}
List<JavaAnnotation> annotations = parameter.getAnnotations();
List<String> groupClasses = JavaClassUtil.getParamGroupJavaClass(annotations);
boolean paramAdded = false;
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getValue();
@ -342,9 +484,17 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
if (!springMvcRequestAnnotations.contains(fullName) || paramAdded) {
continue;
}
if (SpringMvcAnnotations.SESSION_ATTRIBUTE.equals(annotationName)) {
continue out;
}
if(SpringMvcAnnotations.REQUEST_ATTRIBUTE.equals(annotationName))
{
continue out;
}
if (SpringMvcAnnotations.REQUEST_HERDER.equals(annotationName)) {
continue out;
}
AnnotationValue annotationDefaultVal = annotation.getProperty(DocAnnotationConstants.DEFAULT_VALUE_PROP);
if (null != annotationDefaultVal) {
mockValue = StringUtil.removeQuotes(annotationDefaultVal.toString());
@ -371,18 +521,11 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
gicTypeName = requestBodyAdvice + "<" + gicTypeName + ">";
}
if (JavaClassValidateUtil.isPrimitive(simpleTypeName)) {
StringBuilder builder = new StringBuilder();
builder.append("{\"")
.append(paramName)
.append("\":")
.append(DocUtil.handleJsonStr(mockValue))
.append("}");
requestExample.setJsonBody(JsonFormatUtil.formatJson(builder.toString())).setJson(true);
requestExample.setJsonBody(mockValue).setJson(true);
} else {
String json = JsonBuildHelper.buildJson(typeName, gicTypeName, Boolean.FALSE, 0, new HashMap<>(), configBuilder);
requestExample.setJsonBody(JsonFormatUtil.formatJson(json)).setJson(true);
String json = JsonBuildHelper.buildJson(typeName, gicTypeName, Boolean.FALSE, 0, new HashMap<>(), groupClasses, configBuilder);
requestExample.setJsonBody(JsonUtil.toPrettyFormat(json)).setJson(true);
}
paramAdded = true;
} else if (SpringMvcAnnotations.PATH_VARIABLE.contains(annotationName)) {
@ -390,6 +533,9 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
Object value = JavaClassUtil.getEnumValue(javaClass, Boolean.TRUE);
mockValue = StringUtil.removeQuotes(String.valueOf(value));
}
if (pathParamsMap.containsKey(paramName)) {
mockValue = pathParamsMap.get(paramName);
}
pathParamsMap.put(paramName, mockValue);
paramAdded = true;
}
@ -452,12 +598,14 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
String body;
String exampleBody;
String url;
final Map<String, String> formDataToMap = DocUtil.formDataToMap(formDataList);
if (Methods.POST.getValue()
.equals(methodType) || Methods.PUT.getValue()
.equals(methodType)) {
//for post put
path = DocUtil.formatAndRemove(path, pathParamsMap);
body = UrlUtil.urlJoin(DocGlobalConstants.EMPTY, DocUtil.formDataToMap(formDataList))
formDataToMap.putAll(queryParamsMap);
body = UrlUtil.urlJoin(DocGlobalConstants.EMPTY, formDataToMap)
.replace("?", DocGlobalConstants.EMPTY);
body = StringUtil.removeQuotes(body);
url = apiMethodDoc.getServerUrl() + "/" + path;
@ -496,9 +644,9 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
requestExample.setExampleBody(exampleBody).setUrl(url);
} else {
// for get delete
pathParamsMap.putAll(DocUtil.formDataToMap(formDataList));
queryParamsMap.putAll(formDataToMap);
path = DocUtil.formatAndRemove(path, pathParamsMap);
url = UrlUtil.urlJoin(path, pathParamsMap);
url = UrlUtil.urlJoin(path, queryParamsMap);
url = StringUtil.removeQuotes(url);
url = apiMethodDoc.getServerUrl() + "/" + url;
url = UrlUtil.simplifyUrl(url);
@ -517,10 +665,9 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
private ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder) {
private ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder, List<ApiReqParam> configApiReqParams) {
JavaMethod javaMethod = docJavaMethod.getJavaMethod();
boolean isStrict = builder.getApiConfig().isStrict();
Map<String, CustomField> responseFieldMap = new HashMap<>();
String className = javaMethod.getDeclaringClass().getCanonicalName();
Map<String, String> replacementMap = builder.getReplaceClassMap();
Map<String, String> paramTagMap = DocUtil.getParamsComments(javaMethod, DocTags.PARAM, className);
@ -546,10 +693,21 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
}
}
final Map<String, Map<String, ApiReqParam>> collect = configApiReqParams.stream().collect(Collectors.groupingBy(ApiReqParam::getParamIn,
Collectors.toMap(ApiReqParam::getName, m -> m, (k1, k2) -> k1)));
final Map<String, ApiReqParam> pathReqParamMap = collect.getOrDefault(ApiReqParamInTypeEnum.PATH.getValue(), Collections.emptyMap());
final Map<String, ApiReqParam> queryReqParamMap = collect.getOrDefault(ApiReqParamInTypeEnum.QUERY.getValue(), Collections.emptyMap());
List<JavaParameter> parameterList = javaMethod.getParameters();
if (parameterList.size() < 1) {
AtomicInteger querySize = new AtomicInteger(paramList.size() + 1);
paramList.addAll(queryReqParamMap.values().stream()
.map(p -> ApiReqParam.convertToApiParam(p).setQueryParam(true).setId(querySize.getAndIncrement()))
.collect(Collectors.toList()));
AtomicInteger pathSize = new AtomicInteger(1);
return ApiMethodReqParam.builder()
.setPathParams(new ArrayList<>(0))
.setPathParams(new ArrayList<>(pathReqParamMap.values().stream()
.map(p -> ApiReqParam.convertToApiParam(p).setPathParam(true).setId(pathSize.getAndIncrement()))
.collect(Collectors.toList())))
.setQueryParams(paramList)
.setRequestParams(new ArrayList<>(0));
}
@ -618,6 +776,13 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
boolean isRequestBody = false;
for (JavaAnnotation annotation : annotations) {
String annotationName = annotation.getType().getValue();
if (SpringMvcAnnotations.SESSION_ATTRIBUTE.equals(annotationName)) {
continue out;
}
if(SpringMvcAnnotations.REQUEST_ATTRIBUTE.equals(annotationName))
{
continue out;
}
if (SpringMvcAnnotations.REQUEST_HERDER.equals(annotationName)) {
continue out;
}
@ -697,7 +862,9 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
} else if (JavaClassValidateUtil.isPrimitive(gicName)) {
String shortSimple = DocClassUtil.processTypeNameForParams(gicName);
ApiParam param = ApiParam.of().setField(paramName).setDesc(comment + ",[array of " + shortSimple + "]")
ApiParam param = ApiParam.of()
.setField(paramName)
.setDesc(comment + ",[array of " + shortSimple + "]")
.setRequired(required)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
@ -717,16 +884,21 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
groupClasses, 0, Boolean.TRUE));
} else {
throw new RuntimeException("Spring MVC can't support binding Collection on method "
+ javaMethod.getName() + ",Check it in " + javaMethod.getDeclaringClass().getCanonicalName());
+ javaMethod.getName() + ",Check it in " + javaMethod.getDeclaringClass()
.getCanonicalName());
}
}
} else if (JavaClassValidateUtil.isPrimitive(fullTypeName)) {
ApiParam param = ApiParam.of().setField(paramName)
ApiParam param = ApiParam.of()
.setField(paramName)
.setType(DocClassUtil.processTypeNameForParams(simpleName))
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam).setValue(mockValue)
.setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION);
.setQueryParam(queryParam)
.setValue(mockValue)
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(param);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.primaryTypeSchema(simpleName);
@ -737,11 +909,15 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
+ javaMethod.getDeclaringClass().getCanonicalName() + "#" + javaMethod.getName());
//如果typeName map 但没加泛型 java.util.HashMap
if (JavaClassValidateUtil.isMap(typeName)) {
ApiParam apiParam = ApiParam.of().setField(paramName).setType("map")
ApiParam apiParam = ApiParam.of()
.setField(paramName)
.setType("map")
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION);
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(apiParam);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.mapTypeSchema("object");
@ -751,11 +927,15 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName);
if (JavaClassValidateUtil.isPrimitive(gicNameArr[1])) {
ApiParam apiParam = ApiParam.of().setField(paramName).setType("map")
ApiParam apiParam = ApiParam.of()
.setField(paramName)
.setType("map")
.setId(paramList.size() + 1)
.setPathParam(isPathVariable)
.setQueryParam(queryParam)
.setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION);
.setDesc(comment)
.setRequired(required)
.setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(apiParam);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.mapTypeSchema(gicNameArr[1]);
@ -791,11 +971,17 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
List<ApiParam> queryParams = new ArrayList<>();
List<ApiParam> bodyParams = new ArrayList<>();
for (ApiParam param : paramList) {
param.setValue(StringUtil.removeQuotes(param.getValue()));
param.setValue(StringUtil.removeDoubleQuotes(param.getValue()));
if (param.isPathParam()) {
if (pathReqParamMap.containsKey(param.getField())) {
param.setConfigParam(true).setValue(pathReqParamMap.get(param.getField()).getValue());
}
param.setId(pathParams.size() + 1);
pathParams.add(param);
} else if (param.isQueryParam() || requestBodyCounter < 1) {
if (queryReqParamMap.containsKey(param.getField())) {
param.setConfigParam(true).setValue(queryReqParamMap.get(param.getField()).getValue());
}
param.setId(queryParams.size() + 1);
queryParams.add(param);
} else {
@ -803,6 +989,27 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
bodyParams.add(param);
}
}
final Set<String> queryParamSet = queryParams.stream().map(ApiParam::getField).collect(Collectors.toSet());
for (ApiReqParam value : queryReqParamMap.values()) {
if (queryParamSet.contains(value.getName())) {
continue;
}
final ApiParam apiParam = ApiReqParam.convertToApiParam(value)
.setQueryParam(true).setId(queryParams.size());
queryParams.add(apiParam);
}
final Set<String> pathParamSet = pathParams.stream().map(ApiParam::getField).collect(Collectors.toSet());
for (ApiReqParam value : pathReqParamMap.values()) {
if (pathParamSet.contains(value.getName())) {
continue;
}
final ApiParam apiParam = ApiReqParam.convertToApiParam(value)
.setPathParam(true).setId(pathParams.size());
pathParams.add(apiParam);
}
ApiMethodReqParam apiMethodReqParam = ApiMethodReqParam.builder()
.setRequestParams(bodyParams)
.setPathParams(pathParams)
@ -823,9 +1030,14 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
private boolean checkController(JavaClass cls) {
if (cls.isAnnotation() || cls.isEnum()) {
return false;
}
JavaClass superClass = cls.getSuperJavaClass();
List<JavaAnnotation> classAnnotations = new ArrayList<>();
classAnnotations.addAll(superClass.getAnnotations());
if (Objects.nonNull(superClass)) {
classAnnotations.addAll(superClass.getAnnotations());
}
classAnnotations.addAll(cls.getAnnotations());
for (JavaAnnotation annotation : classAnnotations) {
String name = annotation.getType().getValue();
@ -928,9 +1140,61 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
return annotationsList;
}
JavaClass superJavaClass = cls.getSuperJavaClass();
if (!"Object".equals(superJavaClass.getSimpleName())) {
if (Objects.nonNull(superJavaClass) && !"Object".equals(superJavaClass.getSimpleName())) {
annotationsList.addAll(getAnnotations(superJavaClass));
}
return annotationsList;
}
private static void setTornaArrayTags(List<JavaParameter> javaParameters, ApiMethodDoc apiMethodDoc, JavaClass returnClass) {
apiMethodDoc.setIsResponseArray(0);
apiMethodDoc.setIsRequestArray(0);
//response tags
if (JavaClassValidateUtil.isCollection(returnClass.getFullyQualifiedName()) ||
JavaClassValidateUtil.isArray(returnClass.getFullyQualifiedName())) {
apiMethodDoc.setIsResponseArray(1);
String gicType;
String simpleGicType;
String typeName = returnClass.getGenericFullyQualifiedName();
gicType = getType(typeName);
simpleGicType = gicType.substring(gicType.lastIndexOf(".") + 1).toLowerCase();
apiMethodDoc.setResponseArrayType(JavaClassValidateUtil.isPrimitive(gicType) ? simpleGicType : OBJECT);
}
//request tags
if (CollectionUtil.isNotEmpty(javaParameters)) {
for (JavaParameter parameter : javaParameters) {
String gicType;
String simpleGicType;
String typeName = parameter.getType().getGenericFullyQualifiedName();
String name = parameter.getType().getFullyQualifiedName();
gicType = getType(typeName);
simpleGicType = gicType.substring(gicType.lastIndexOf(".") + 1).toLowerCase();
// is array
if (JavaClassValidateUtil.isCollection(name) || JavaClassValidateUtil.isArray(name)) {
apiMethodDoc.setIsRequestArray(1);
if (JavaClassValidateUtil.isPrimitive(gicType)) {
apiMethodDoc.setRequestArrayType(simpleGicType);
} else {
apiMethodDoc.setRequestArrayType(OBJECT);
}
}
}
}
}
private static String getType(String typeName) {
String gicType;
//get generic type
if (typeName.contains("<")) {
gicType = typeName.substring(typeName.indexOf("<") + 1, typeName.lastIndexOf(">"));
} else {
gicType = typeName;
}
if (gicType.contains("[")) {
gicType = gicType.substring(0, gicType.indexOf("["));
}
return gicType;
}
}

View File

@ -49,7 +49,7 @@ public class ApiParamTreeUtil {
}
for (ApiParam apiParam : params) {
// remove pre of field
apiParam.setChildren(getChild(apiParam.getId(), apiParamList));
apiParam.setChildren(getChild(apiParam.getId(), apiParamList,0));
}
return params;
}
@ -59,17 +59,22 @@ public class ApiParamTreeUtil {
*
* @param id param id
* @param apiParamList List of ApiParam
* @param counter invoked counter
* @return List of ApiParam
*/
private static List<ApiParam> getChild(int id, List<ApiParam> apiParamList) {
private static List<ApiParam> getChild(int id, List<ApiParam> apiParamList, int counter) {
List<ApiParam> childList = new ArrayList<>();
if (counter > 7) {
return childList;
}
for (ApiParam param : apiParamList) {
if (param.getPid() == id) {
childList.add(param);
}
}
counter++;
for (ApiParam param : childList) {
param.setChildren(getChild(param.getId(), apiParamList));
param.setChildren(getChild(param.getId(), apiParamList, counter));
}
if (childList.size() == 0) {
return new ArrayList<>(0);

View File

@ -25,7 +25,7 @@ package com.power.doc.utils;
import com.power.common.util.CollectionUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.model.ApiReqHeader;
import com.power.doc.model.ApiReqParam;
import com.power.doc.model.request.CurlRequest;
/**
@ -47,7 +47,7 @@ public class CurlUtil {
sb.append(" 'Content-Type: ").append(request.getContentType()).append("'");
}
if (CollectionUtil.isNotEmpty(request.getReqHeaders())) {
for (ApiReqHeader reqHeader : request.getReqHeaders()) {
for (ApiReqParam reqHeader : request.getReqHeaders()) {
sb.append(" -H");
if (StringUtil.isEmpty(reqHeader.getValue())) {
sb.append(" '" + reqHeader.getName() + "'");

View File

@ -188,6 +188,8 @@ public class DocClassUtil {
case "zoneddatetime":
case "java.time.zoneddatetime":
case "java.time.ZonedDateTime":
case "java.lang.Character":
case "character":
return "string";
case "java.util.List":
case "list":
@ -255,7 +257,7 @@ public class DocClassUtil {
public static String rewriteRequestParam(String typeName) {
switch (typeName) {
case "org.springframework.data.domain.Pageable":
return "org.springframework.data.domain.PageRequest";
return "com.power.doc.model.framework.PageableAsQueryParam";
default:
return typeName;
}

View File

@ -22,12 +22,15 @@
*/
package com.power.doc.utils;
import com.power.common.util.PathUtil;
import com.power.common.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class PathUtil {
public class DocPathUtil {
/**
* Get the java class name
@ -61,4 +64,24 @@ public class PathUtil {
}
return null;
}
/**
* Determine a match for the given lookup path.
*
* @param lookupPath the request path
* @param includePatterns the path patterns to map (empty for matching to all paths)
* @param excludePatterns the path patterns to exclude (empty for no specific excludes)
* @return {@code true} if matched the request path
*/
public static boolean matches(String lookupPath, String includePatterns, String excludePatterns) {
List<String> includePatternList = null;
if (StringUtil.isNotEmpty(includePatterns)) {
includePatternList = Arrays.asList(includePatterns.split(",", 0));
}
List<String> excludePatternList = null;
if (StringUtil.isNotEmpty(excludePatterns)) {
excludePatternList = Arrays.asList(excludePatterns.split(",", 0));
}
return PathUtil.matches(lookupPath,includePatternList,excludePatternList);
}
}

View File

@ -34,6 +34,7 @@ import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@ -44,7 +45,7 @@ import java.util.stream.Collectors;
*/
public class DocUtil {
private static Faker faker = new Faker(new Locale(System.getProperty(DocGlobalConstants.DOC_LANGUAGE)));
private static Faker faker = new Faker(new Locale("en-US"));
//private static Faker faker = new Faker(new Locale("smart-doc_language"));
private static Faker enFaker = new Faker(new Locale("en-US"));
@ -54,7 +55,7 @@ public class DocUtil {
static {
fieldValue.put("uuid-string", UUID.randomUUID().toString());
fieldValue.put("uid", UUID.randomUUID().toString());
fieldValue.put("traceid-string",UUID.randomUUID().toString());
fieldValue.put("id-string", String.valueOf(RandomUtil.randomInt(1, 200)));
fieldValue.put("nickname-string", enFaker.name().username());
fieldValue.put("hostname-string", faker.internet().ipV4Address());
@ -62,6 +63,8 @@ public class DocUtil {
fieldValue.put("author-string", faker.book().author());
fieldValue.put("url-string", faker.internet().url());
fieldValue.put("username-string", faker.name().username());
fieldValue.put("index-int","1");
fieldValue.put("index-integer","1");
fieldValue.put("page-int", "1");
fieldValue.put("page-integer", "1");
fieldValue.put("age-int", String.valueOf(RandomUtil.randomInt(0, 70)));
@ -186,7 +189,7 @@ public class DocUtil {
}
/**
* 是否是合法的java类名称
* valid java class name
*
* @param className class nem
* @return boolean
@ -227,11 +230,12 @@ public class DocUtil {
}
String[] patterns = packageFilters.split(",");
for (String str : patterns) {
String name = str;
if (str.endsWith("*")) {
name = str.substring(0, str.length() - 2);
}
if (controllerName.contains(name)) {
if (str.contains("*")) {
Pattern pattern = Pattern.compile(str);
if (pattern.matcher(controllerName).matches()) {
return true;
}
} else if (controllerName.startsWith(str)) {
return true;
}
}
@ -290,6 +294,28 @@ public class DocUtil {
return builder.toString();
}
/**
* // /detail/{id:[a-zA-Z0-9]{3}}/{name:[a-zA-Z0-9]{3}}
* remove pattern
*
* @param str path
* @return String
*/
public static String formatPathUrl(String str) {
if (!str.contains(":")) {
return str;
}
String[] strArr = str.split("/");
for (int i = 0; i < strArr.length; i++) {
String pathParam = strArr[i];
if (pathParam.contains(":")) {
strArr[i] = pathParam.substring(0, pathParam.indexOf(":")) + "}";
}
}
str = StringUtils.join(Arrays.asList(strArr), '/');
return str;
}
/**
* handle spring mvc method
*
@ -326,6 +352,37 @@ public class DocUtil {
}
}
/**
* Split url
*
* @param url URL to be divided
* @return list of url
*/
public static List<String> split(String url) {
char[] chars = url.toCharArray();
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder();
Stack<Character> stack = new Stack<>();
for (int i = 0; i < chars.length; i++) {
char s = chars[i];
if ('{' == s) {
stack.push(s);
}
if ('}' == s) {
stack.pop();
}
if (',' == s && stack.isEmpty()) {
result.add(sb.toString());
sb.delete(0, sb.length());
continue;
}
sb.append(s);
}
result.add(sb.toString());
return result;
}
/**
* obtain params comments
*
@ -341,18 +398,16 @@ public class DocUtil {
String value = docletTag.getValue();
if (StringUtil.isEmpty(value) && StringUtil.isNotEmpty(className)) {
throw new RuntimeException("ERROR: #" + javaMethod.getName()
+ "() - bad @" + tagName + " javadoc from " + javaMethod.getDeclaringClass().getCanonicalName() + ", must be add comment if you use it.");
+ "() - bad @" + tagName + " javadoc from " + javaMethod.getDeclaringClass()
.getCanonicalName() + ", must be add comment if you use it.");
}
String pName;
String pValue;
int idx = value.indexOf("\n");
String pName = value;
String pValue = DocGlobalConstants.NO_COMMENTS_FOUND;
int idx = value.indexOf(" ");
//existed \n
if (idx > -1) {
pName = value.substring(0, idx);
pValue = value.substring(idx + 1);
} else {
pName = (value.contains(" ")) ? value.substring(0, value.indexOf(" ")) : value;
pValue = value.contains(" ") ? value.substring(value.indexOf(' ') + 1) : DocGlobalConstants.NO_COMMENTS_FOUND;
}
paramTagMap.put(pName, pValue);
}
@ -476,6 +531,8 @@ public class DocUtil {
case "BigDecimal":
case "BigInteger":
case "Byte":
case "Character":
case "character":
return true;
default:
return false;
@ -525,14 +582,53 @@ public class DocUtil {
* @return the url
*/
public static String getRequestMappingUrl(JavaAnnotation annotation) {
Object url = annotation.getNamedParameter(DocAnnotationConstants.VALUE_PROP);
return getPathUrl(annotation, DocAnnotationConstants.VALUE_PROP, DocAnnotationConstants.PATH_PROP);
}
if (null != url) {
return url.toString();
} else {
url = annotation.getNamedParameter(DocAnnotationConstants.PATH_PROP);
return null == url ? StringUtil.EMPTY : url.toString();
/**
* Get mapping url from Annotation
*
* @param annotation JavaAnnotation
* @param props annotation properties
* @return the path
*/
public static String getPathUrl(JavaAnnotation annotation, String... props) {
for (String prop : props) {
Object url = annotation.getNamedParameter(prop);
if (Objects.nonNull(url)) {
return url.toString();
}
}
return StringUtil.EMPTY;
}
/**
* handle spring mvc RequestHeader value
*
* @param annotation JavaAnnotation
* @return String
*/
public static String handleRequestHeaderValue(JavaAnnotation annotation) {
String header = getRequestHeaderValue(annotation);
if (StringUtil.isEmpty(header)) {
return header;
}
return StringUtil.trimBlank(header);
}
/**
* Obtain constant from @RequestHeader annotation
*
* @param annotation RequestMapping GetMapping PostMapping etc.
* @return The constant value
*/
public static String getRequestHeaderValue(JavaAnnotation annotation) {
Object constantValue = annotation.getNamedParameter(DocAnnotationConstants.VALUE_PROP);
if (null != constantValue) {
return constantValue.toString();
}
return "";
}
public static List<ApiErrorCode> errorCodeDictToList(ApiConfig config) {
@ -609,4 +705,32 @@ public class DocUtil {
return apiDocDictList;
}
/**
* Format field Type
*
* @param genericMap genericMap
* @param globGicName globGicName array
* @param fieldGicName fieldGicName
* @return string
*/
public static String formatFieldTypeGicName(Map<String, String> genericMap, String[] globGicName, String fieldGicName) {
String[] gNameArr = DocClassUtil.getSimpleGicName(fieldGicName);
if (gNameArr.length > 0) {
String gName = gNameArr[0];
if (gName.length() == 1) {
String gicName = "";
if (Objects.nonNull(genericMap.get(gName))) {
gicName = genericMap.get(gName);
}
if (globGicName.length > 0) {
gicName = globGicName[0];
}
if (StringUtil.isNotEmpty(gicName)) {
fieldGicName = fieldGicName.replace(gName, gicName);
}
}
}
return fieldGicName;
}
}

View File

@ -25,6 +25,7 @@ package com.power.doc.utils;
import com.power.common.util.CollectionUtil;
import com.power.common.util.StringUtil;
import com.power.doc.constants.DocAnnotationConstants;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.DocValidatorAnnotationEnum;
import com.power.doc.constants.ValidatorAnnotations;
import com.power.doc.model.DocJavaField;
@ -89,12 +90,15 @@ public class JavaClassUtil {
String comment = javaMethod.getComment();
JavaField javaField = new DefaultJavaField(javaMethod.getReturns(), methodName);
DocJavaField docJavaField = DocJavaField.builder()
.setFieldName(methodName)
.setJavaField(javaField)
.setComment(comment)
.setDocletTags(javaMethod.getTags())
.setAnnotations(javaMethod.getAnnotations())
.setFullyQualifiedName(javaField.getType().getFullyQualifiedName())
.setGenericCanonicalName(javaField.getType().getGenericCanonicalName());
.setFullyQualifiedName(javaField.getType()
.getFullyQualifiedName())
.setGenericCanonicalName(javaField.getType()
.getGenericCanonicalName());
addedFields.put(methodName, docJavaField);
}
}
@ -129,15 +133,26 @@ public class JavaClassUtil {
DocJavaField docJavaField = addedFields.get(methodName);
docJavaField.setAnnotations(method.getAnnotations());
docJavaField.setComment(comment);
docJavaField.setFieldName(methodName);
addedFields.put(methodName, docJavaField);
}
}
for (JavaField javaField : cls1.getFields()) {
String fieldName = javaField.getName();
String subTypeName = javaField.getType().getFullyQualifiedName();
if (javaField.isStatic() || "this$0".equals(fieldName) ||
JavaClassValidateUtil.isIgnoreFieldTypes(subTypeName)) {
continue;
}
if (fieldName.startsWith("is") && ("boolean".equals(subTypeName))) {
fieldName = StringUtil.firstToLowerCase(fieldName.substring(2));
}
DocJavaField docJavaField = DocJavaField.builder();
boolean typeChecked = false;
String gicName = javaField.getType().getGenericCanonicalName();
String subTypeName = javaField.getType().getFullyQualifiedName();
String actualType = null;
if (JavaClassValidateUtil.isCollection(subTypeName) &&
!JavaClassValidateUtil.isCollection(gicName)) {
@ -160,7 +175,7 @@ public class JavaClassUtil {
for (Map.Entry<String, JavaType> entry : actualJavaTypes.entrySet()) {
String key = entry.getKey();
JavaType value = entry.getValue();
if (gicName.contains(key)) {
if (gicName.equals(key)) {
subTypeName = subTypeName.replaceAll(key, value.getFullyQualifiedName());
gicName = gicName.replaceAll(key, value.getGenericCanonicalName());
actualType = value.getFullyQualifiedName();
@ -169,15 +184,18 @@ public class JavaClassUtil {
docJavaField.setComment(javaField.getComment())
.setJavaField(javaField).setFullyQualifiedName(subTypeName)
.setGenericCanonicalName(gicName).setActualJavaType(actualType)
.setAnnotations(javaField.getAnnotations());
.setAnnotations(javaField.getAnnotations())
.setFieldName(fieldName);
if (addedFields.containsKey(fieldName)) {
addedFields.put(fieldName, docJavaField);
continue;
}
addedFields.put(fieldName, docJavaField);
}
List<DocJavaField> parentFieldList = addedFields.values().stream()
.filter(v -> Objects.nonNull(v)).collect(Collectors.toList());
List<DocJavaField> parentFieldList = addedFields.values()
.stream()
.filter(v -> Objects.nonNull(v))
.collect(Collectors.toList());
fieldList.addAll(parentFieldList);
}
return fieldList;
@ -442,15 +460,15 @@ public class JavaClassUtil {
if (CollectionUtil.isEmpty(annotationValueList)) {
return;
}
for (int i = 0; i < annotationValueList.size(); i++) {
TypeRef annotationValue = (TypeRef) annotationValueList.get(i);
DefaultJavaParameterizedType annotationValueType = (DefaultJavaParameterizedType) annotationValue.getType();
for (AnnotationValue annotationValue : annotationValueList) {
TypeRef typeRef = (TypeRef) annotationValue;
DefaultJavaParameterizedType annotationValueType = (DefaultJavaParameterizedType) typeRef.getType();
javaClassList.add(annotationValueType.getGenericCanonicalName());
}
}
private static List<AnnotationValue> getAnnotationValues(List<String> validates, JavaAnnotation javaAnnotation) {
List<AnnotationValue> annotationValueList = null;
List<AnnotationValue> annotationValueList = new ArrayList<>();
String simpleName = javaAnnotation.getType().getValue();
if (simpleName.equalsIgnoreCase(ValidatorAnnotations.VALIDATED)) {
if (Objects.nonNull(javaAnnotation.getProperty(DocAnnotationConstants.VALUE_PROP))) {
@ -459,7 +477,6 @@ public class JavaClassUtil {
annotationValueList = ((AnnotationValueList) v).getValueList();
}
if (v instanceof TypeRef) {
annotationValueList = new ArrayList<>();
annotationValueList.add(v);
}
}
@ -470,7 +487,6 @@ public class JavaClassUtil {
annotationValueList = ((AnnotationValueList) v).getValueList();
}
if (v instanceof TypeRef) {
annotationValueList = new ArrayList<>();
annotationValueList.add(v);
}
}
@ -513,4 +529,40 @@ public class JavaClassUtil {
}
return false;
}
public static Map<String, String> getClassJsonIgnoreFields(JavaClass cls) {
if (cls == null) return Collections.EMPTY_MAP;
List<JavaAnnotation> classAnnotation = cls.getAnnotations();
Map<String, String> ignoreFields = new HashMap<>();
for (JavaAnnotation annotation : classAnnotation) {
String simpleAnnotationName = annotation.getType().getValue();
if (DocAnnotationConstants.SHORT_JSON_IGNORE_PROPERTIES.equalsIgnoreCase(simpleAnnotationName)) {
return JavaClassUtil.getJsonIgnoresProp(annotation, DocAnnotationConstants.VALUE_PROP);
}
if (DocAnnotationConstants.SHORT_JSON_TYPE.equals(simpleAnnotationName)) {
return JavaClassUtil.getJsonIgnoresProp(annotation, DocAnnotationConstants.IGNORE_PROP);
}
}
return ignoreFields;
}
public static Map<String, String> getJsonIgnoresProp(JavaAnnotation annotation, String propName) {
Map<String, String> ignoreFields = new HashMap<>();
Object ignoresObject = annotation.getNamedParameter(propName);
if (Objects.isNull(ignoresObject)) {
return ignoreFields;
}
if (ignoresObject instanceof String) {
String prop = StringUtil.removeQuotes(ignoresObject.toString());
ignoreFields.put(prop, null);
return ignoreFields;
}
List<String> ignorePropList = (LinkedList) ignoresObject;
for (String str : ignorePropList) {
String prop = StringUtil.removeQuotes(str);
ignoreFields.put(prop, null);
}
return ignoreFields;
}
}

View File

@ -87,6 +87,7 @@ public class JavaClassValidateUtil {
case "boolean":
case "byte":
case "uuid":
case "character":
case "java.sql.timestamp":
case "java.util.date":
case "java.time.localdatetime":
@ -101,6 +102,7 @@ public class JavaClassValidateUtil {
case "java.util.uuid":
case "java.util.UUID":
case "java.io.serializable":
case "java.lang.character":
return true;
default:
return false;
@ -213,6 +215,22 @@ public class JavaClassValidateUtil {
}
}
/**
* Download
* @param typeName return type name
* @return boolean
*/
public static boolean isFileDownloadResource(String typeName){
switch (typeName) {
case "org.springframework.core.io.Resource":
case "org.springframework.core.io.InputStreamSource":
case "org.springframework.core.io.ByteArrayResource":
return true;
default:
return false;
}
}
/**
* ignore param of spring mvc
*
@ -228,14 +246,19 @@ public class JavaClassValidateUtil {
case "org.springframework.ui.Model":
case "org.springframework.ui.ModelMap":
case "org.springframework.web.servlet.ModelAndView":
case "org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap":
case "org.springframework.validation.BindingResult":
case "javax.servlet.http.HttpServletRequest":
case "org.springframework.web.context.request.WebRequest":
case "javax.servlet.http.HttpServlet":
case "javax.servlet.http.HttpSession":
case "javax.servlet.http.HttpServletResponse":
case "org.springframework.web.context.request.WebRequest":
case "org.springframework.web.reactive.function.server.ServerRequest":
case "org.springframework.web.multipart.MultipartHttpServletRequest":
case "org.springframework.http.HttpHeaders":
case "org.springframework.core.io.Resource":
case "org.springframework.core.io.InputStreamSource":
case "org.springframework.core.io.ByteArrayResource":
return true;
default:
return false;

View File

@ -0,0 +1,63 @@
/*
* smart-doc https://github.com/shalousun/smart-doc
*
* Copyright (C) 2018-2021 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.power.doc.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/**
* @author yu 2021/6/26.
*/
public class JsonUtil {
/**
* Convert a JSON string to pretty print
*
* @param jsonString json string
* @return Format json string
*/
public static String toPrettyFormat(String jsonString) {
try {
JsonElement jsonElement = JsonParser.parseString(jsonString);
Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
String prettyJson = gson.toJson(jsonElement);
return prettyJson;
} catch (Exception e) {
return jsonString;
}
}
/**
* Convert a JSON to String and pretty print
*
* @param src Json
* @return Format json string
*/
public static String toPrettyJson(Object src) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String prettyJson = gson.toJson(src);
return prettyJson;
}
}

View File

@ -2,35 +2,30 @@ package com.power.doc.utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.internal.$Gson$Types;
import com.power.common.model.EnumDictionary;
import com.power.common.util.CollectionUtil;
import com.power.common.util.StringUtil;
import com.power.doc.builder.BaseDocBuilderTemplate;
import com.power.doc.constants.DocGlobalConstants;
import com.power.doc.constants.TornaConstants;
import com.power.doc.model.*;
import com.power.doc.model.rpc.RpcApiDependency;
import com.power.doc.model.torna.*;
import okhttp3.internal.http2.ErrorCode;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static com.power.doc.constants.TornaConstants.PUSH;
/**
* @author xingzi 2021/4/28 16:15
**/
public class TornaUtil {
public static boolean setDebugEnv(ApiConfig apiConfig, TornaApi tornaApi) {
//是否设置测试环境
boolean hasDebugEnv = StringUtils.isNotBlank(apiConfig.getDebugEnvName())
&&
StringUtils.isNotBlank(apiConfig.getDebugEnvUrl());
//设置测试环境
//Set up the test environment
List<DebugEnv> debugEnvs = new ArrayList<>();
if (hasDebugEnv) {
DebugEnv debugEnv = new DebugEnv();
@ -42,9 +37,9 @@ public class TornaUtil {
return hasDebugEnv;
}
public static void printDebugInfo(ApiConfig apiConfig, String responseMsg, Map<String, String> requestJson,String category) {
public static void printDebugInfo(ApiConfig apiConfig, String responseMsg, Map<String, String> requestJson, String category) {
if (apiConfig.isTornaDebug()) {
String sb = "配置信息列表: \n" +
String sb = "Configuration information : \n" +
"OpenUrl: " +
apiConfig.getOpenUrl() +
"\n" +
@ -78,10 +73,10 @@ public class TornaUtil {
* @return List of Api
*/
public static List<Apis> buildApis(List<ApiMethodDoc> apiMethodDocs, boolean hasDebugEnv) {
//参数列表
//Parameter list
List<Apis> apis = new ArrayList<>();
Apis methodApi;
//遍历分类接口
//Iterative classification interface
for (ApiMethodDoc apiMethodDoc : apiMethodDocs) {
methodApi = new Apis();
methodApi.setIsFolder(TornaConstants.NO);
@ -92,15 +87,24 @@ public class TornaUtil {
methodApi.setDescription(apiMethodDoc.getDetail());
methodApi.setIsShow(TornaConstants.YES);
methodApi.setAuthor(apiMethodDoc.getAuthor());
methodApi.setOrderIndex(apiMethodDoc.getOrder());
methodApi.setHeaderParams(buildHerder(apiMethodDoc.getRequestHeaders()));
methodApi.setResponseParams(buildParams(apiMethodDoc.getResponseParams()));
methodApi.setIsRequestArray(apiMethodDoc.getIsRequestArray());
methodApi.setIsResponseArray(apiMethodDoc.getIsResponseArray());
methodApi.setRequestArrayType(apiMethodDoc.getRequestArrayType());
methodApi.setResponseArrayType(apiMethodDoc.getResponseArrayType());
//Path
if (CollectionUtil.isNotEmpty(apiMethodDoc.getPathParams())) {
methodApi.setPathParams(buildParams(apiMethodDoc.getPathParams()));
}
//formData
if (CollectionUtil.isNotEmpty(apiMethodDoc.getQueryParams())) {
if (CollectionUtil.isNotEmpty(apiMethodDoc.getQueryParams())
&& DocGlobalConstants.MULTIPART_TYPE.equals(apiMethodDoc.getContentType())) {
// file upload
methodApi.setRequestParams(buildParams(apiMethodDoc.getQueryParams()));
} else if (CollectionUtil.isNotEmpty(apiMethodDoc.getQueryParams())) {
methodApi.setQueryParams(buildParams(apiMethodDoc.getQueryParams()));
}
//Json
@ -119,10 +123,10 @@ public class TornaUtil {
* @return List of Api
*/
public static List<Apis> buildDubboApis(List<JavaMethodDoc> apiMethodDocs) {
//参数列表
//Parameter list
List<Apis> apis = new ArrayList<>();
Apis methodApi;
//遍历分类接口
//Iterative classification interface
for (JavaMethodDoc apiMethodDoc : apiMethodDocs) {
methodApi = new Apis();
methodApi.setIsFolder(TornaConstants.NO);
@ -132,6 +136,7 @@ public class TornaUtil {
methodApi.setAuthor(apiMethodDoc.getAuthor());
methodApi.setUrl(apiMethodDoc.getMethodDefinition());
methodApi.setResponseParams(buildParams(apiMethodDoc.getResponseParams()));
methodApi.setOrderIndex(apiMethodDoc.getOrder());
//Json
if (CollectionUtil.isNotEmpty(apiMethodDoc.getRequestParams())) {
methodApi.setRequestParams(buildParams(apiMethodDoc.getRequestParams()));
@ -144,19 +149,13 @@ public class TornaUtil {
/**
* build request header
*
* @param apiReqHeaders 请求头参数列表
* @param apiReqParams Request header parameter list
* @return List of HttpParam
*/
public static List<HttpParam> buildHerder(List<ApiReqHeader> apiReqHeaders) {
/**
* name": "token",
* "required": "1",
* "example": "iphone12",
* "description": "商品名称描述"
*/
public static List<HttpParam> buildHerder(List<ApiReqParam> apiReqParams) {
HttpParam httpParam;
List<HttpParam> headers = new ArrayList<>();
for (ApiReqHeader header : apiReqHeaders) {
for (ApiReqParam header : apiReqParams) {
httpParam = new HttpParam();
httpParam.setName(header.getName());
httpParam.setRequired(header.isRequired() ? TornaConstants.YES : TornaConstants.NO);
@ -170,30 +169,12 @@ public class TornaUtil {
/**
* build request response params
*
* @param apiParams 参数列表
* @param apiParams Param list
* @return List of HttpParam
*/
public static List<HttpParam> buildParams(List<ApiParam> apiParams) {
HttpParam httpParam;
List<HttpParam> bodies = new ArrayList<>();
/**
* "name": "goodsName",
* "type": "string",
* "required": "1",
* "maxLength": "128",
* "example": "iphone12",
* "description": "商品名称描述",
* "parentId": "",
* "enumInfo": {
* "name": "支付枚举",
* "description": "支付状态",
* "items": [
* {
* "name": "WAIT_PAY",
* "type": "string",
* "value": "0",
* "description": "未支付"
*/
for (ApiParam apiParam : apiParams) {
httpParam = new HttpParam();
httpParam.setName(apiParam.getField());
@ -239,11 +220,12 @@ public class TornaUtil {
public static List<TornaDic> buildTornaDic(List<ApiDocDict> apiDocDicts) {
List<TornaDic> dics = new ArrayList<>();
TornaDic tornaDic = new TornaDic();
TornaDic tornaDic;
if (CollectionUtil.isNotEmpty(apiDocDicts)) {
for (ApiDocDict doc : apiDocDicts) {
tornaDic = new TornaDic();
tornaDic.setName(doc.getTitle())
// .setDescription(doc.getTitle())
// .setDescription(doc.getTitle())
.setItems(buildTornaDicItems(doc.getDataDictList()));
dics.add(tornaDic);
}

View File

@ -1,2 +1,5 @@
DELIMITER_STATEMENT_START=<%
DELIMITER_STATEMENT_END=%>
DELIMITER_STATEMENT_END=%>
FN.htmlEscape = com.power.doc.function.HtmlEscape
FN.removeLineBreaks = com.power.doc.function.RemoveLineBreaks
FNP.strUtil = org.beetl.ext.fn.StringUtil

View File

@ -17,26 +17,33 @@ for(revisionLog in revisionLogList){
<%
for(api in apiDocList){
for(apiGroup in apiDocList){
%>
== ${api.desc}
<%
if(apiGroupLP.size > 1) {%>
==${apiGroup.group}
<%} %>
<%
for(api in apiGroup.childrenApiDocs){
%>
=== ${api.desc}
<%
for(doc in api.list){
%>
<%if(doc.deprecated){%>
=== [line-through]#${doc.desc}#
==== [line-through]#${doc.desc}#
<%}else{%>
=== ${doc.desc}
==== ${doc.desc}
<%}%>
*URL:* ${doc.url}
*URL:* `${doc.url}`
*Type:* ${doc.type}
*Type:* `${doc.type}`
<%if(isNotEmpty(doc.author)){%>
*Author:* ${doc.author}
<%}%>
*Content-Type:* ${doc.contentType}
*Content-Type:* `${doc.contentType}`
<%if(isNotEmpty(doc.headers)){%>
*Request-headers:*
@ -122,6 +129,7 @@ ${doc.responseUsage}
----
<%}%>
<% } %>
<% } %>
<% } %>
<%if(isNotEmpty(errorCodeList)){%>

View File

@ -14,7 +14,14 @@ ${revisionLog.version}|${revisionLog.revisionTime}|${revisionLog.status}|${revis
<%
for(api in apiDocList){
for(apiGroup in apiDocList){
%>
<%
if(apiGroupLP.size > 1) {%>
# ${apiGroup.group}
<%}%>
<%
for(api in apiGroup.childrenApiDocs){
%>
## ${api.desc}
<%
@ -106,6 +113,7 @@ ${doc.responseUsage}
```
<%}%>
<% } %>
<% } %>
<% } %>
<%if(isNotEmpty(errorCodeList)){%>

File diff suppressed because one or more lines are too long

View File

@ -28,8 +28,9 @@ for(doc in list){
|====================
|Header | Type|Description|Required|Since
${doc.headers}
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.pathParams)){%>
*Path-parameters:*

View File

@ -4,4 +4,10 @@ ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:
.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.checkbox{position:relative;height:30px}.checkbox input[type='checkbox']{position:absolute;left:0;top:0;width:20px;height:20px;opacity:0;border-radius:4px}.checkbox label{position:absolute;left:30px;top:0;height:20px;line-height:20px}.checkbox label:before{content:'';position:absolute;left:-30px;top:2px;width:20px;height:20px;border:1px solid #ddd;border-radius:4px;transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox label:after{content:'';position:absolute;left:-22px;top:3px;width:6px;height:12px;border:0;border-right:1px solid #fff;border-bottom:1px solid #fff;border-radius:2px;transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox input[type='checkbox']:checked+label:before{background:#4cd764;border-color:#4cd764}.checkbox input[type='checkbox']:checked+label:after{background:#4cd764}.send-button{color:#fff;background-color:#5cb85c;display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px;outline-color:transparent}textarea{width:100%;background-color:#f7f7f8;border:1px solid #f7f7f8;border-radius:4px;font-size:1em;padding:1em;font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;outline-color:#dedede}input{border:0;background-color:transparent;outline-color:transparent;outline-style:dotted;max-width:100%}#book-search-input{padding:13px;background:0;transition:top .5s ease;border-bottom:1px solid rgba(0,0,0,.07);border-top:1px solid rgba(0,0,0,.07);margin-top:-1px}#book-search-input input,#book-search-input input:focus,#book-search-input input:hover{width:100%;background:0;border:1px solid transparent;box-shadow:none;outline:0;line-height:22px;padding:7px 7px;color:inherit}[contenteditable="plaintext-only"]:focus{border:0;outline:0}
body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.checkbox{position:relative;height:30px}.checkbox input[type='checkbox']{position:absolute;left:0;top:0;width:20px;height:20px;opacity:0;border-radius:4px}.checkbox label{position:absolute;left:30px;top:0;height:20px;line-height:20px}.checkbox label:before{content:'';position:absolute;left:-30px;top:2px;width:20px;height:20px;border:1px solid #ddd;border-radius:4px;transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox label:after{content:'';position:absolute;left:-22px;top:3px;width:6px;height:12px;border:0;border-right:1px solid #fff;border-bottom:1px solid #fff;border-radius:2px;transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox input[type='checkbox']:checked+label:before{background:#4cd764;border-color:#4cd764}.checkbox input[type='checkbox']:checked+label:after{background:#4cd764}.send-button{color:#fff;background-color:#5cb85c;display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px;outline-color:transparent}textarea{width:100%;background-color:#f7f7f8;border:1px solid #f7f7f8;border-radius:4px;font-size:1em;padding:1em;font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;outline-color:#dedede}input{border:0;background-color:transparent;outline-color:transparent;outline-style:dotted;max-width:100%}#book-search-input{padding:13px;background:0;transition:top .5s ease;border-bottom:1px solid rgba(0,0,0,.07);border-top:1px solid rgba(0,0,0,.07);margin-top:-1px}#book-search-input input,#book-search-input input:focus,#book-search-input input:hover{width:100%;background:0;border:1px solid transparent;box-shadow:none;outline:0;line-height:22px;padding:7px 7px;color:inherit}[contenteditable="plaintext-only"]:focus{border:0;outline:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}.left{float:left!important}.right{float:right!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}.text-justify{text-align:justify!important}.hide{display:none}img,object,svg{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.center{margin-left:auto;margin-right:auto}.spread{width:100%}p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}a{color:#364149;text-decoration:underline;line-height:inherit}a:hover,a:focus{color:#364149}a img{border:0}p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}p aside{font-size:.875em;line-height:1.35;font-style:italic}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}h1{font-size:2.125em}h2{font-size:1.6875em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}h4,h5{font-size:1.125em}h6{font-size:1em}hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}ul,ol{margin-left:1.5em}ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square}ul.circle{list-style-type:circle}ul.disc{list-style-type:disc}ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}dl dt{margin-bottom:.3125em;font-weight:bold}dl dd{margin-bottom:1.25em}abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}h1{font-size:2.75em}h2{font-size:2.3125em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}table thead,table tfoot{background:#f7f8f7;font-weight:bold}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}.clearfix:after,.float-group:after{clear:both}*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}*:not(pre)>code.nobreak{word-wrap:normal}*:not(pre)>code.nowrap{white-space:nowrap}pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}em em{font-style:normal}strong strong{font-weight:400}.keyseq{color:rgba(51,51,51,.8)}kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}.keyseq kbd:first-child{margin-left:0}.keyseq kbd:last-child{margin-right:0}.menuseq,.menuref{color:#000}.menuseq b:not(.caret),.menuref{font-weight:inherit}.menuseq{word-spacing:-.02em}.menuseq b.caret{font-size:1.25em;line-height:.8}.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}b.button:before{content:"[";padding:0 3px 0 2px}b.button:after{content:"]";padding:0 2px 0 3px}p a>code:hover{color:rgba(0,0,0,.9)}#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}#header:after,#content:after,#footnotes:after,#footer:after{clear:both}#content{margin-top:1.25em}#content:before{content:none}#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}#header .details span:first-child{margin-left:-.125em}#header .details span.email a{color:rgba(0,0,0,.85)}#header .details br{display:none}#header .details br+span:before{content:"\00a0\2013\00a0"}#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}#header .details br+span#revremark:before{content:"\00a0|\00a0"}#header #revnumber{text-transform:capitalize}#header #revnumber:after{content:"\00a0"}#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}#toc>ul{margin-left:.125em;padding-left:1.25em}#toc ul.sectlevel0>li>a{font-style:italic}#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}#toc li{line-height:1.3334;margin-top:.3334em;padding-bottom:4px;padding-top:4px}#toc a{text-decoration:none}#toc a:active{text-decoration:underline}#toctitle{color:#7a2518;font-size:1.2em}@media only screen and (min-width:768px){#toctitle{font-size:1.375em}body.toc2{padding-left:15em;padding-right:0}#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;#padding:1.25em 1em;height:100%;overflow:auto}#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}#toc.toc2 ul ul{margin-left:0;padding-left:1em}#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}body.toc2.toc-right{padding-left:0;padding-right:15em}body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}#toc.toc2{width:20em}#toc.toc2 #toctitle{font-size:1.375em;border-bottom:1px solid rgba(0,0,0,.07);padding-top:20px;padding-bottom:15px}#toc.toc2 #toctitle span{padding-left:1.25em;padding-bottom:15px}#toc.toc2>ul{font-size:.95em}#toc.toc2 ul ul{padding-left:1.25em}body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}#content #toc>:first-child{margin-top:0}#content #toc>:last-child{margin-bottom:0}#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}#footer-text{color:rgba(255,255,255,.8);line-height:1.44}.sect1{padding-bottom:.625em}@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}.admonitionblock>table{border-collapse:separate;border:0;background:0;width:100%}.admonitionblock>table td.icon{text-align:center;width:80px}.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}
.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.copyright{text-align:right;padding-top:1.25em}#toTop{display:none;position:fixed;bottom:10px;right:0;width:44px;height:44px;border-radius:50%;background-color:#ced4ce;cursor:pointer;text-align:center}#upArrow{position:absolute;left:24%;right:0;bottom:19%;transition:.3s ease-in-out;display:block}#upText{position:absolute;left:0;right:0;bottom:0;font-size:16px;font-weight: 600;line-height:45px;display:none;transition:.3s ease-in-out;-webkit-box-align:center}

View File

@ -0,0 +1,5 @@
@font-face{font-family:'Droid Sans Mono';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/droidsansmono/v14/6NUO8FuJNQ2MbkrZ5-J8lKFrp7pRef2r.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZzC7TMQ.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImbjC7TMQ.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZjC7TMQ.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImaTC7TMQ.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZTC7TMQ.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZDC7TMQ.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImajC7.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufuVMCoY.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWud-VMCoY.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuf-VMCoY.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWucOVMCoY.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufOVMCoY.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufeVMCoY.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuc-VM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFoWaCi_.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFMWaCi_.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFsWaCi_.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFQWaCi_.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFgWaCi_.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFkWaCi_.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFcWaA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRqecf1I.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfROecf1I.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRuecf1I.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRSecf1I.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRiecf1I.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRmecf1I.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfReecQ.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2) format('woff2');unicode-range:U+0370-03FF}
@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWJ0bbck.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWZ0bbck.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWp0bbck.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFW50bbck.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOUuhp.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

View File

@ -0,0 +1 @@
.hljs{display:block;overflow-x:auto;color:#eaeaea;background:#000;padding:.5em}.hljs-subst{color:#eaeaea}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-builtin-name,.hljs-type{color:#eaeaea}.hljs-params{color:#da0000}.hljs-literal,.hljs-name,.hljs-number{color:red;font-weight:bolder}.hljs-comment{color:#969896}.hljs-quote,.hljs-selector-id{color:#0ff}.hljs-template-variable,.hljs-title,.hljs-variable{color:#0ff;font-weight:700}.hljs-keyword,.hljs-selector-class,.hljs-symbol{color:#fff000}.hljs-bullet,.hljs-string{color:#0f0}.hljs-section,.hljs-tag{color:#000fff}.hljs-selector-tag{color:#000fff;font-weight:700}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-regexp{color:#f0f}.hljs-meta{color:#fff;font-weight:bolder}

File diff suppressed because one or more lines are too long

View File

@ -9,15 +9,13 @@
**Author:** ${author}
**Version:** ${version}
<% for(doc in list){ %>
<%
for(doc in list){
%>
<%if(doc.deprecated){%>
## ~~${doc.desc}~~
<%}else{%>
## ${doc.desc}
<%}%>
**Definition** ${doc.methodDefinition}
@ -33,8 +31,9 @@
Parameter|Type|Description|Required|Since
---|---|---|---|---
<% for(param in doc.requestParams){ %>
<%
for(param in doc.requestParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
@ -44,8 +43,9 @@ ${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
Field | Type|Description|Since
---|---|---|---
<% for(param in doc.responseParams){ %>
<%
for(param in doc.responseParams){
%>
${param.field}|${param.type}|${param.desc}|${param.version}
<%}%>
<%}%>

View File

@ -1,31 +1,271 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><meta name="generator"content="smart-doc"><%if(isNotEmpty(projectName)){%><title>${projectName}</title><%}else{%><title>API Reference</title><%}%><link rel="stylesheet"href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"><link rel="stylesheet"href="AllInOne.css?v=${version}"/><script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script></head><body class="book toc2 toc-left"><div id="header"><%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%><div id="toc"class="toc2"><div id="book-search-input"><input id="search"type="text"placeholder="Type to search"></div><div id="toctitle"><span>API Reference</span></div><ul id="accordion"class="sectlevel1"><li><a href="#_add_dependency">1.&nbsp;Add dependency</a></li><%for(api in apiDocList){%><%if(apiLP.first){%><li class="open"><a class="dd"href="#_${api.link}">${api.order+1}.&nbsp;${api.desc}</a><ul class="sectlevel2"style="display: block"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}else{%><li><a class="dd"href="#_${api.link}">${api.order+1}.&nbsp;${api.desc}</a><ul class="sectlevel2"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}%><%}%><%if(isNotEmpty(errorCodeList)){%><li><a href="#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a></li><%}%><%if(isNotEmpty(dictList)){%><li><a class="dd"href="#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a><ul class="sectlevel2"><%for(dict in dictList){%><li><a href="#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a></li><%}%></ul></li><%}%></ul></div></div><div id="content"><%if(isNotEmpty(revisionLogList)){%><div id="preamble"><div class="sectionbody"><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Version</th><th class="tableblock halign-left valign-top">Update Time</th><th class="tableblock halign-left valign-top">Status</th><th class="tableblock halign-left valign-top">Author</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><%for(revisionLog in revisionLogList){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.version}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.revisionTime}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.status}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.author}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.remarks}</p></td></tr><%}%></tbody></table></div></div><%}%><%if(isNotEmpty(dependencyList)){%><div class="sect1"><h2 id="_add_dependency"><a class="anchor"href="#_add_dependency"></a><a class="link"href="#_add_dependency">1.&nbsp;Add dependency</a></h2><div class="sectionbody"><div class="listingblock"><div class="content">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="smart-doc">
<%if(isNotEmpty(projectName)){%><title>${projectName}</title><%}else{%><title>API Reference</title><%}%>
<link rel="stylesheet" href="font.css?v=${version}">
<link rel="stylesheet" href="AllInOne.css?v=${version}"/>
<script src="jquery.min.js"></script>
</head>
<body class="book toc2 toc-left">
<div id="header"> <%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%>
<div id="toc" class="toc2">
<div id="book-search-input"><input id="search" type="text" placeholder="Type to search"></div>
<div id="toctitle"><span>API Reference</span></div>
<ul id="accordion" class="sectlevel1">
<li><a href="#_add_dependency">1.&nbsp;Add dependency</a></li>
<%for(api in apiDocList){%><%if(apiLP.first){%>
<li class="open"><a class="dd" href="#_${api.link}">${api.order+1}.&nbsp;${api.desc}</a>
<ul class="sectlevel2" style="display: block"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}else{%>
<li><a class="dd" href="#_${api.link}">${api.order+1}.&nbsp;${htmlEscape(api.desc)}</a>
<ul class="sectlevel2"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}%><%}%><%if(isNotEmpty(errorCodeList)){%>
<li><a href="#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a></li>
<%}%><%if(isNotEmpty(dictList)){%>
<li><a class="dd" href="#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a>
<ul class="sectlevel2"><%for(dict in dictList){%>
<li><a href="#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${htmlEscape(dict.title)}</a>
</li>
<%}%>
</ul>
</li>
<%}%>
</ul>
</div>
</div>
<div id="content"> <%if(isNotEmpty(revisionLogList)){%>
<div id="preamble">
<div class="sectionbody">
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Version</th>
<th class="tableblock halign-left valign-top">Update Time</th>
<th class="tableblock halign-left valign-top">Status</th>
<th class="tableblock halign-left valign-top">Author</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody><%for(revisionLog in revisionLogList){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.version}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.revisionTime}</p>
</td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.status}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.author}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${revisionLog.remarks}</p></td>
</tr>
<%}%>
</tbody>
</table>
</div>
</div>
<%}%><%if(isNotEmpty(dependencyList)){%>
<div class="sect1"><h2 id="_add_dependency"><a class="anchor" href="#_add_dependency"></a><a class="link"
href="#_add_dependency">1.&nbsp;Add
dependency</a></h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre><%for(dp in dependencyList){%>&lt;dependency&gt;
&lt;groupId&gt;${dp.groupId}&lt;/groupId&gt;
&lt;artifactId&gt;${dp.artifactId}&lt;/artifactId&gt;
&lt;version&gt;${dp.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;${dp.groupId}&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;${dp.artifactId}&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${dp.version}&lt;/version&gt;
&lt;/dependency&gt;
<%}%>
</pre>
</div></div><%if(isNotEmpty(consumerConfigExample)){%><div class="paragraph"><p>Consumer config</p></div><div class="listingblock"><div class="content"><pre>${consumerConfigExample}</pre></div></div><%}%></div></div><%}%><%for(api in apiDocList){%><div class="sect1"><h2 id="_${api.desc}"><a class="anchor"href="#_${api.desc}"></a><a class="link"href="#_${api.desc}">${api.order+1}.&nbsp;${api.desc}</a></h2><div class="sectionbody"><div class="paragraph"><p><strong>URI:</strong>&nbsp;${api.uri}</p></div><div class="paragraph"><p><strong>Service:</strong>&nbsp;${api.name}</p></div><div class="paragraph"><p><strong>Protocol:</strong>&nbsp;${api.protocol}</p></div><div class="paragraph"><p><strong>Author:</strong>&nbsp;${api.author}</p></div><div class="paragraph"><p><strong>Version:</strong>&nbsp;${api.version}</p></div><%for(doc in api.list){%><div class="sect2"><h3 id="_${api.order+1}_${doc.order}_${doc.desc}"><a class="anchor"href="#_${api.order+1}_${doc.order}_${doc.desc}"></a><%if(doc.deprecated){%><a class="link"href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a class="link"href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${doc.desc}</a><%}%></h3><div class="paragraph"><p><strong>Definition:</strong>&nbsp;${doc.escapeMethodDefinition}</p></div><div class="paragraph"><p><strong>Description:</strong>&nbsp;${doc.detail}</p></div><%if(isNotEmpty(doc.requestParams)){%><div class="paragraph"><p><strong>Invoke-parameters:</strong></p></div><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Parameter</th><th class="tableblock halign-left valign-top">Type</th><th class="tableblock halign-left valign-top">Description</th><th class="tableblock halign-left valign-top">Required</th><th class="tableblock halign-left valign-top">Since</th></tr></thead><tbody><%for(param in doc.requestParams){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${param.field}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.type}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.desc}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.required}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.version}</p></td></tr><%}%></tbody></table><%}%><%if(isNotEmpty(doc.responseParams)){%><div class="paragraph"><p><strong>Response-fields:</strong></p></div><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Field</th><th class="tableblock halign-left valign-top">Type</th><th class="tableblock halign-left valign-top">Description</th><th class="tableblock halign-left valign-top">Since</th></tr></thead><tbody><%for(param in doc.responseParams){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${param.field}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.type}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.desc}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${param.version}</p></td></tr><%}%></tbody></table><%}%></div><%}%></div></div><%}%><%if(isNotEmpty(errorCodeList)){%><div class="sect1"><h2 id="_error_code_list"><a class="anchor"href="#_error_code_list"></a><a class="link"href="#_error_code_list">${apiDocList.~size+2}.&nbsp;${errorListTitle}</a></h2><div class="sectionbody"><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Error code</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><%for(error in errorCodeList){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${error.value}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${error.desc}</p></td></tr><%}%></tbody></table></div></div><%}%><footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span class="footer-modification">Suggestions,contact,support and error reporting on<a href="https://gitee.com/smart-doc-team/smart-doc"target="_blank">&nbsp;Gitee</a>&nbsp;or<a href="https://github.com/shalousun/smart-doc"target="_blank">&nbsp;Github</a></span></footer></div>
</div>
</div>
<%if(isNotEmpty(consumerConfigExample)){%>
<div class="paragraph"><p>Consumer config</p></div>
<div class="listingblock">
<div class="content">
<pre>${consumerConfigExample}</pre>
</div>
</div>
<%}%>
</div>
</div>
<%}%><%for(api in apiDocList){%>
<div class="sect1"><h2 id="_${api.desc}"><a class="anchor" href="#_${api.desc}"></a><a class="link"
href="#_${api.desc}">${api.order+1}.&nbsp;${htmlEscape(api.desc)}</a>
</h2>
<div class="sectionbody">
<div class="paragraph"><p><strong>URI:</strong>&nbsp;${api.uri}</p></div>
<div class="paragraph"><p><strong>Service:</strong>&nbsp;${api.name}</p></div>
<div class="paragraph"><p><strong>Protocol:</strong>&nbsp;${api.protocol}</p></div>
<div class="paragraph"><p><strong>Author:</strong>&nbsp;${api.author}</p></div>
<div class="paragraph"><p><strong>Version:</strong>&nbsp;${api.version}</p></div>
<%for(doc in api.list){%>
<div class="sect2"><h3 id="_${api.order+1}_${doc.order}_${doc.desc}"><a class="anchor"
href="#_${api.order+1}_${doc.order}_${doc.desc}"></a><%if(doc.deprecated){%><a
class="link"
href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a class="link"
href="#_${api.order+1}_${doc.order}_${doc.desc}">${api.order+1}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</h3>
<div class="paragraph"><p><strong>Definition:</strong>&nbsp;${doc.escapeMethodDefinition}</p></div>
<div class="paragraph"><p><strong>Description:</strong>&nbsp;${htmlEscape(doc.detail)}</p></div>
<%if(isNotEmpty(doc.requestParams)){%>
<div class="paragraph"><p><strong>Invoke-parameters:</strong></p></div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Parameter</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Since</th>
</tr>
</thead>
<tbody><%for(param in doc.requestParams){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.field}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.type}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">
${htmlEscape(param.desc)}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.required}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.version}</p></td>
</tr>
<%}%>
</tbody>
</table>
<%}%><%if(isNotEmpty(doc.responseParams)){%>
<div class="paragraph"><p><strong>Response-fields:</strong></p></div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Field</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Since</th>
</tr>
</thead>
<tbody><%for(param in doc.responseParams){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.field}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.type}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">
${htmlEscape(param.desc)}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${param.version}</p></td>
</tr>
<%}%>
</tbody>
</table>
<%}%>
</div>
<%}%>
</div>
</div>
<%}%><%if(isNotEmpty(errorCodeList)){%>
<div class="sect1"><h2 id="_error_code_list"><a class="anchor" href="#_error_code_list"></a><a class="link"
href="#_error_code_list">${apiDocList.~size+2}.&nbsp;${errorListTitle}</a>
</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Error code</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody><%for(error in errorCodeList){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${error.value}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${htmlEscape(error.desc)}</p>
</td>
</tr>
<%}%>
</tbody>
</table>
</div>
</div>
<%}%>
<footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span
class="footer-modification">Suggestions,contact,support and error reporting on<ahref="https://gitee.com/smart-doc-team/smart-doc" target="_blank">&nbsp;Gitee</a>&nbsp;or<ahref="https://github.com/smart-doc-group/smart-doc.git" target="_blank">&nbsp;Github</a></span>
</footer>
<div href="javascript:void(0)" id="toTop"><img id="upArrow"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABlUlEQVRIS+2UvUvDQBiH398Rly4udnARwUXs4qAIOigI4iL30dTZ2T9AcNPVvUsXF7uYttdScNDFRRAnB11cFFwKxcXBJTQnJ6lEbRI/CIiY9e6e5/e+9+ZAGX/ImE9/QKCU2jfGbGTQqq4xZgtSyisiKmQgIAAVCCFWAGxnIOhqrdd/xyUrpRZsP40xSwA6AI57vd5eq9W6T6s8tQIppSKi+gDQNREprfVNkiRRwDlfY4xZ+FAIuSOi8Qjw0nEc5XnebZwkViClXA2T5+xhY8xus9ncEUJMAziITN5FEARuXLsGCoQQywBs8uEovJ+Scz7FGDuMSM4cx3E9z+u8r+SDQEq5SEQ1IhoZBE+QnBKRq7V+iEreCDjn84wxCx9NgidITnK5nFutVh/7e14FSqnZIAhqAMY+A4+TADjyfb/Ubref7J4XQXhxNvnEV+AJlbTy+XypUqn4KBaLBZuciCa/A0+opN5oNFz7FpUBbP4EHicxxsyAcz7HGDvvz3nar5+2Ho5wOQwsU5+KNGDa+r8grUP0DBLjtRtNKEliAAAAAElFTkSuQmCC"><span
id="upText">Top</span></div>
</div>
<script src="search.js?v=${version}"></script>
<script>
$(function () {
const Accordion = function (el, multiple) {
this.el = el || {};
this.multiple = multiple || false;
const links = this.el.find('.dd');
links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
const links = this.el.find(".dd");
links.on("click", {el: this.el, multiple: this.multiple}, this.dropdown)
};
Accordion.prototype.dropdown = function (e) {
const $el = e.data.el;
$this = $(this), $next = $this.next();
$next.slideToggle();
$this.parent().toggleClass('open');
$this.parent().toggleClass("open");
if (!e.data.multiple) {
$el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
$el.find(".submenu").not($next).slideUp("20").parent().removeClass("open")
}
};
new Accordion($('#accordion'), false);
new Accordion($("#accordion"), false);
$(window).scroll(function () {
if ($(window).scrollTop() > 100) {
$("#toTop").fadeIn(1500);
$("#toTop").hover(function () {
$("#upArrow").hide();
$("#upText").show()
}, function () {
$("#upArrow").show();
$("#upText").hide()
})
} else {
$("#toTop").fadeOut(1500)
}
});
$("#toTop").click(function () {
$("body, html").animate({scrollTop: 0}, 1000);
return false
})
});
</script>
</body>

View File

@ -1,18 +1,17 @@
<%if(isNotEmpty(projectName)){%>
# ${projectName}
<%}%>
<%if(isNotEmpty(revisionLogList)){%>
Version | Update Time | Status | Author | Description ---|---|---|---|---
Version|Update Time|Status|Author|Description
---|---|---|---|---
<% for(revisionLog in revisionLogList){ %>
${revisionLog.version}|${revisionLog.revisionTime}|${revisionLog.status}|${revisionLog.author}|${revisionLog.remarks}
<%}%>
<%}%>
<%if(isNotEmpty(dependencyList)){%>
## Add dependency
```
@ -30,16 +29,15 @@ for(dependency in dependencyList){
<%if(isNotEmpty(consumerConfigExample)){%>
Consumer config
```
${consumerConfigExample}
```
<%}%>
<%}%>
<% for(api in apiDocList){ %>
<%
for(api in apiDocList){
%>
## ${api.desc}
**URI:** ${api.uri}
@ -51,15 +49,13 @@ ${consumerConfigExample}
**Author:** ${api.author}
**Version:** ${api.version}
<% for(doc in api.list){ %>
<%
for(doc in api.list){
%>
<%if(doc.deprecated){%>
### ~~${doc.desc}~~
<%}else{%>
### ${doc.desc}
<%}%>
**Definition** ${doc.methodDefinition}
@ -75,8 +71,9 @@ ${consumerConfigExample}
Parameter|Type|Description|Required|Since
---|---|---|---|---
<% for(param in doc.requestParams){ %>
<%
for(param in doc.requestParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
@ -86,23 +83,25 @@ ${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
Field | Type|Description|Since
---|---|---|---
<% for(param in doc.responseParams){ %>
<%
for(param in doc.responseParams){
%>
${param.field}|${param.type}|${param.desc}|${param.version}
<%}%>
<%}%>
<%if(isNotEmpty(errorCodeList)){%>
<%}%>
<%}%>
<%if(isNotEmpty(errorCodeList)){%>
## ${errorListTitle}
Error code |Description
---|---
<% for(error in errorCodeList){ %>
<%
for(error in errorCodeList){
%>
${error.value}|${error.desc}
<%}%>
<%}%>
<%}%>
<%}%>

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,101 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><meta name="generator"content="smart-doc"><title>SpringBoot2-Open-Api</title><link rel="stylesheet"href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"><link rel="stylesheet"href="AllInOne.css?v=1607962934038"/><link rel="stylesheet"href="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/styles/xt256.min.css"><style>.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#000}.hljs{padding:0em}</style><script src="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/highlight.min.js"></script><script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script></head><body class="book toc2 toc-left"><div id="header"><%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%><div id="toc"class="toc2"><div id="book-search-input"><input id="search"type="text"placeholder="Type to search"></div><div id="toctitle"><span>API Reference</span></div><ul id="accordion"class="sectlevel1"><%for(api in apiDocList){%><%if(apiLP.first){%><li><a class="dd"href="${alias}.html#header">${api.order}.&nbsp;${api.desc}</a><ul class="sectlevel2"style="display: none"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}else{%><li><a class="dd"href="${api.alias}.html#header">${api.order}.&nbsp;${api.desc}</a><ul class="sectlevel2"style="display: none"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}%><%}%><%if(isNotEmpty(errorCodeList)){%><li><a href="error.html#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a></li><%}%><%if(isNotEmpty(dictList)){%><li class="open"><a class="dd"href="dict.html#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a><ul class="sectlevel2"style="display: block"><%for(dict in dictList){%><li><a href="dict.html#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a></li><%}%></ul></li><%}%></ul></div></div><div id="content"><%if(isNotEmpty(dictList)){%><div class="sect1"><h2 id="_dict_list"><a class="anchor"href="#_dict_list"></a><a class="link"href="#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a></h2><div class="sectionbody"><%for(dict in dictList){%><div class="sect2"><h3 id="_${dictListOrder}_${dict.order}_${dict.title}"><a class="anchor"href="#_${dictListOrder}_${dict.order}_${dict.title}"></a><a class="link"href="#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a></h3><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 33%;"><col style="width: 33%;"><col style="width: 33%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Code</th><th class="tableblock halign-left valign-top">Type</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><%for(dataDict in dict.dataDictList){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${dataDict.value}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${dataDict.type}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${dataDict.desc}</p></td></tr><%}%></tbody></table></div><%}%></div></div><%}%><footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span class="footer-modification">Suggestions,contact,support and error reporting on<a href="https://gitee.com/smart-doc-team/smart-doc"target="_blank">&nbsp;Gitee</a>&nbsp;or<a href="https://github.com/smart-doc-group/smart-doc.git"target="_blank">&nbsp;Github</a></span></footer></div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="smart-doc">
<title>Dictionary</title>
<link rel="stylesheet" href="font.css">
<link rel="stylesheet" href="AllInOne.css?v=${version}"/>
<%if(isNotEmpty(highlightCssLink)){%><link rel="stylesheet" href="${highlightCssLink}"><%}%>
<style>.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {
background: ${background}
}
.hljs {
padding: 0em
}</style>
<script src="highlight.min.js"></script>
<script src="jquery.min.js"></script>
</head>
<body class="book toc2 toc-left">
<div id="header"><%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%>
<div id="toc" class="toc2">
<div id="book-search-input"><input id="search" type="text" placeholder="Type to search"></div>
<div id="toctitle"><span>API Reference</span></div>
<ul id="accordion" class="sectlevel1"><%for(api in apiDocList){%><%if(apiLP.first){%>
<li><a class="dd" href="${alias}.html#header">${api.order}.&nbsp;${htmlEscape(api.desc)}</a>
<ul class="sectlevel2" style="display: none"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}else{%>
<li><a class="dd" href="${api.alias}.html#header">${api.order}.&nbsp;${htmlEscape(api.desc)}</a>
<ul class="sectlevel2" style="display: none"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}%><%}%><%if(isNotEmpty(errorCodeList)){%>
<li><a href="error.html#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a></li>
<%}%><%if(isNotEmpty(dictList)){%>
<li class="open"><a class="dd" href="dict.html#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a>
<ul class="sectlevel2" style="display: block"><%for(dict in dictList){%>
<li><a href="dict.html#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a>
</li>
<%}%>
</ul>
</li>
<%}%>
</ul>
</div>
</div>
<div id="content"><%if(isNotEmpty(dictList)){%>
<div class="sect1"><h2 id="_dict_list"><a class="anchor" href="#_dict_list"></a><a class="link" href="#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a>
</h2>
<div class="sectionbody"><%for(dict in dictList){%>
<div class="sect2"><h3 id="_${dictListOrder}_${dict.order}_${dict.title}">
<a class="anchor" href="#_${dictListOrder}_${dict.order}_${dict.title}"></a>
<a class="link" href="#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a>
</h3>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 33%;">
<col style="width: 33%;">
<col style="width: 33%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Code</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody><%for(dataDict in dict.dataDictList){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${dataDict.value}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${dataDict.type}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${htmlEscape(dataDict.desc)}</p></td>
</tr>
<%}%>
</tbody>
</table>
</div>
<%}%>
</div>
</div>
<%}%>
<footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span
class="footer-modification">Suggestions,contact,support and error reporting on<a
href="https://gitee.com/smart-doc-team/smart-doc" target="_blank">&nbsp;Gitee</a>&nbsp;or<a
href="https://github.com/smart-doc-group/smart-doc.git" target="_blank">&nbsp;Github</a></span></footer>
</div>
<script src="search.js?v=${version}"></script>
<script>
$(function () {
@ -18,7 +115,7 @@
}
};
new Accordion($('#accordion'), false);
hljs.initHighlightingOnLoad()
hljs.highlightAll()
});
</script>
</body>

View File

@ -1,4 +1,104 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><meta name="generator"content="smart-doc"><title>SpringBoot2-Open-Api</title><link rel="stylesheet"href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"><link rel="stylesheet"href="AllInOne.css?v=1607962934038"/><link rel="stylesheet"href="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/styles/xt256.min.css"><style>.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#000}.hljs{padding:0em}</style><script src="https://cdn.bootcdn.net/ajax/libs/highlight.js/10.3.2/highlight.min.js"></script><script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script></head><body class="book toc2 toc-left"><div id="header"><%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%><div id="toc"class="toc2"><div id="book-search-input"><input id="search"type="text"placeholder="Type to search"></div><div id="toctitle"><span>API Reference</span></div><ul id="accordion"class="sectlevel1"><%for(api in apiDocList){%><%if(apiLP.first){%><li><a class="dd"href="${alias}.html#header">${api.order}.&nbsp;${api.desc}</a><ul class="sectlevel2"style="display: none"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}else{%><li><a class="dd"href="${api.alias}.html#header">${api.order}.&nbsp;${api.desc}</a><ul class="sectlevel2"style="display: none"><%for(doc in api.list){%><li><%if(doc.deprecated){%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span class="line-through">${doc.desc}</span></a><%}else{%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${doc.desc}</a><%}%></li><%}%></ul></li><%}%><%}%><%if(isNotEmpty(errorCodeList)){%><li class="open"><a href="error.html#header">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a><ul class="sectlevel2"style="display: block"><li><a href="error.html#_${apiDocList.~size+1}_1_${errorListTitle}">${apiDocList.~size+1}.1.&nbsp;${errorListTitle}</a></li></ul></li><%}%><%if(isNotEmpty(dictList)){%><li><a class="dd"href="dict.html#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a><ul class="sectlevel2"><%for(dict in dictList){%><li><a href="dict.html#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a></li><%}%></ul></li><%}%></ul></div></div><div id="content"><%if(isNotEmpty(errorCodeList)){%><div class="sect1"><h2 id="_error_code_list"><a class="anchor"href="#_error_code_list"></a><a class="link"href="#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a></h2><div class="sectionbody"><div class="sect2"><h3 id="_${apiDocList.~size+1}_1_${errorListTitle}"><a class="anchor"href="#_${apiDocList.~size+1}_1_${errorListTitle}"></a><a class="link"href="#_${apiDocList.~size+1}_1_${errorListTitle}">${apiDocList.~size+1}.1.&nbsp;${errorListTitle}</a></h3><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Error code</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><%for(error in errorCodeList){%><tr><td class="tableblock halign-left valign-top"><p class="tableblock">${error.value}</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">${error.desc}</p></td></tr><%}%></tbody></table></div></div></div><%}%><footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span class="footer-modification">Suggestions,contact,support and error reporting on<a href="https://gitee.com/smart-doc-team/smart-doc"target="_blank">&nbsp;Gitee</a>&nbsp;or<a href="https://github.com/smart-doc-group/smart-doc.git"target="_blank">&nbsp;Github</a></span></footer></div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="smart-doc">
<title>Error Code</title>
<link rel="stylesheet" href="font.css">
<link rel="stylesheet" href="AllInOne.css?v=${version}"/>
<%if(isNotEmpty(highlightCssLink)){%><link rel="stylesheet" href="${highlightCssLink}"><%}%>
<style>.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {
background: ${background}
}
.hljs {
padding: 0em
}</style>
<script src="highlight.min.js"></script>
<script src="jquery.min.js"></script>
</head>
<body class="book toc2 toc-left">
<div id="header"><%if(isNotEmpty(projectName)){%><h1>${projectName}</h1><%}%>
<div id="toc" class="toc2">
<div id="book-search-input"><input id="search" type="text" placeholder="Type to search"></div>
<div id="toctitle"><span>API Reference</span></div>
<ul id="accordion" class="sectlevel1"><%for(api in apiDocList){%><%if(apiLP.first){%>
<li><a class="dd" href="${alias}.html#header">${api.order}.&nbsp;${htmlEscape(api.desc)}</a>
<ul class="sectlevel2" style="display: none"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="${alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}else{%>
<li><a class="dd" href="${api.alias}.html#header">${api.order}.&nbsp;${htmlEscape(api.desc)}</a>
<ul class="sectlevel2" style="display: none"><%for(doc in api.list){%>
<li><%if(doc.deprecated){%><a href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;<span
class="line-through">${htmlEscape(doc.desc)}</span></a><%}else{%><a
href="${api.alias}.html#_${api.order}_${doc.order}_${doc.desc}">${api.order}.${doc.order}.&nbsp;${htmlEscape(doc.desc)}</a><%}%>
</li>
<%}%>
</ul>
</li>
<%}%><%}%><%if(isNotEmpty(errorCodeList)){%>
<li class="open"><a href="error.html#header">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a>
<ul class="sectlevel2" style="display: block">
<li><a href="error.html#_${apiDocList.~size+1}_1_${errorListTitle}">${apiDocList.~size+1}.1.&nbsp;${errorListTitle}</a>
</li>
</ul>
</li>
<%}%><%if(isNotEmpty(dictList)){%>
<li><a class="dd" href="dict.html#_dict_list">${dictListOrder}.&nbsp;${dictListTitle}</a>
<ul class="sectlevel2"><%for(dict in dictList){%>
<li><a href="dict.html#_${dictListOrder}_${dict.order}_${dict.title}">${dictListOrder}.${dict.order}.&nbsp;${dict.title}</a>
</li>
<%}%>
</ul>
</li>
<%}%>
</ul>
</div>
</div>
<div id="content"><%if(isNotEmpty(errorCodeList)){%>
<div class="sect1"><h2 id="_error_code_list"><a class="anchor" href="#_error_code_list"></a><a class="link"
href="#_error_code_list">${apiDocList.~size+1}.&nbsp;${errorListTitle}</a>
</h2>
<div class="sectionbody">
<div class="sect2"><h3 id="_${apiDocList.~size+1}_1_${errorListTitle}"><a class="anchor"
href="#_${apiDocList.~size+1}_1_${errorListTitle}"></a><a
class="link" href="#_${apiDocList.~size+1}_1_${errorListTitle}">${apiDocList.~size+1}.1.&nbsp;${errorListTitle}</a>
</h3>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Error code</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody><%for(error in errorCodeList){%>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">${error.value}</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">${htmlEscape(error.desc)}</p></td>
</tr>
<%}%>
</tbody>
</table>
</div>
</div>
</div>
<%}%>
<footer class="page-footer"><span class="copyright">Generated by smart-doc at ${createTime}</span><span
class="footer-modification">Suggestions,contact,support and error reporting on<a
href="https://gitee.com/smart-doc-team/smart-doc" target="_blank">&nbsp;Gitee</a>&nbsp;or<a
href="https://github.com/smart-doc-group/smart-doc.git" target="_blank">&nbsp;Github</a></span></footer>
</div>
<script src="search.js?v=${version}"></script>
<script>
$(function () {
@ -18,7 +118,7 @@
}
};
new Accordion($('#accordion'), false);
hljs.initHighlightingOnLoad()
hljs.highlightAll()
});
</script>
</body>

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ $(function () {
}
};
new Accordion($('#accordion'), false);
hljs.initHighlightingOnLoad();
hljs.highlightAll();
});
$("[contenteditable=plaintext-only]").on('blur', function (e) {
@ -182,7 +182,12 @@ function getInputData(element, returnFormDate) {
formData.append(name, $(input)[0].files[0]);
} else {
const val = $(input).val();
formData.append(name, val);
if (isValidUrl(val)) {
formData.append(name, encodeURI(val));
} else {
// support chinese
formData.append(name, encodeURIComponent(val));
}
}
}
});
@ -277,10 +282,12 @@ function toCurl(request) {
if (request.url.indexOf('https') == 0) {
cmd.push("-k");
}
// append Content-Type
if (request.data && request.data.length > 0) {
if (request.contentType) {
cmd.push("-H");
cmd.push("'Content-Type: application/json; charset=utf-8'");
cmd.push("'Content-Type:");
cmd.push(request.contentType+"'");
}
// append request headers
let headerValue;
@ -310,10 +317,39 @@ function toCurl(request) {
}
cmd.push(url);
// append data
if (request.data && request.data.length > 0) {
if (typeof request.data == 'object') {
let index = 0;
const bodyData = [];
bodyData.push("\"")
for (let key in request.data) {
if (Object.prototype.hasOwnProperty.call(request.data, key)) {
if (index===0){
bodyData.push(key);
bodyData.push("=");
bodyData.push(request.data[key]);
} else {
bodyData.push("&")
bodyData.push(key);
bodyData.push("=");
bodyData.push(request.data[key]);
}
}
index++;
}
bodyData.push("\"");
let bodyStr = ""
bodyData.forEach(function (item) {
bodyStr += item;
});
cmd.push("--data");
cmd.push(bodyStr);
} else if (request.data && request.data.length > 0) {
// append json data
cmd.push("--data");
cmd.push("'" + request.data + "'");
}
let curlCmd = "";
cmd.forEach(function (item) {
curlCmd += item + " ";
@ -324,4 +360,14 @@ function toCurl(request) {
function isEmpty(obj) {
return obj === undefined || obj === null || new String(obj).trim() === '';
}
function isValidUrl(_string) {
let urlString;
try {
urlString = new URL(_string);
} catch (_) {
return false;
}
return urlString.protocol === "http:" || urlString.protocol === "https:" ;
}

Some files were not shown because too many files have changed in this diff Show More