Commit 2b76db72 authored by duanledexianxianxian's avatar duanledexianxianxian 😁

生成markdown api文档

parent f20179e3
......@@ -15,12 +15,16 @@ import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParse
import java.util.Optional;
import java.util.stream.Collectors;
/**
* The type Comment helper.
*/
public class CommentHelper {
/**
* 获取完整注释字符串
* @param description
* @return
*
* @param description the description
* @return string
*/
public static String getDescription(JavadocDescription description){
return description.getElements()
......@@ -29,6 +33,12 @@ public class CommentHelper {
.map(JavadocDescriptionElement::toText).collect(Collectors.joining());
}
/**
* Get content string.
*
* @param comment the comment
* @return the string
*/
public static String getContent(Comment comment){
if(!comment.isJavadocComment()){
return comment.getContent();
......@@ -37,6 +47,12 @@ public class CommentHelper {
}
/**
* Get comment string.
*
* @param it the it
* @return the string
*/
public static String getComment(MethodUsage it){
if (it.getDeclaration() instanceof JavaParserMethodDeclaration) {
MethodDeclaration wrappedNode = ((JavaParserMethodDeclaration) it.getDeclaration()).getWrappedNode();
......@@ -47,6 +63,13 @@ public class CommentHelper {
}
return null;
}
/**
* Get comment string.
*
* @param it the it
* @return the string
*/
public static String getComment(ResolvedFieldDeclaration it){
if(it instanceof JavaParserFieldDeclaration){
FieldDeclaration wrappedNode = ((JavaParserFieldDeclaration) it).getWrappedNode();
......@@ -56,8 +79,9 @@ public class CommentHelper {
}
}else if(it instanceof JavaParserClassDeclaration){
JavaParserClassDeclaration classDeclaration = (JavaParserClassDeclaration) it;
}
return null;
}
}
package com.kim.apidoc.core.common.helper;
import com.github.javaparser.ast.body.EnumConstantDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedEnumDeclaration;
import com.kim.apidoc.core.schema.Section;
import com.kim.apidoc.core.schema.Cell;
import java.util.ArrayList;
import java.util.List;
/**
* The type Enum helper.
*/
public class EnumHelper {
public static String getNames(ResolvedEnumDeclaration enumDeclaration) {
/**
* Get names string.
*
* @param enumDeclaration the enum declaration
* @return the string
*/
public static String getNames(ResolvedEnumDeclaration enumDeclaration){
StringBuilder sb = new StringBuilder();
for (ResolvedEnumConstantDeclaration resolvedEnumConstantDeclaration : enumDeclaration.getEnumConstants()) {
if (sb.length() > 0) {
if(sb.length()>0){
sb.append(",");
}
sb.append(resolvedEnumConstantDeclaration.getName());
......@@ -18,23 +32,24 @@ public class EnumHelper {
return sb.toString();
}
public static Section toDetails(EnumDeclaration declaration) {
Section section = new Section();
section.setId(declaration.getNameAsString());
section.setName(declaration.getNameAsString());
section.setNameAndDescription(declaration.getNameAsString());
//Map<String, Row> rows = Maps.newLinkedHashMap();
//for (EnumConstantDeclaration constant : declaration.getEntries()) {
// Row row=new Row();
// row.setKey(constant.getNameAsString());
// for (Expression expression : constant.getArguments()) {
// Object value = Expressions.getValue(expression);
// cell.add(String.valueOf(value));
// }
// cells.add(cell);
//}
return section;
/**
* To details list.
*
* @param declaration the declaration
* @return the list
*/
public static List<Cell<String>> toDetails(EnumDeclaration declaration){
List<Cell<String>> cells = new ArrayList<>();
for (EnumConstantDeclaration constant : declaration.getEntries()) {
Cell<String> cell = new Cell<>();
cell.add(constant.getNameAsString());
for (Expression expression : constant.getArguments()) {
Object value = ExpressionHelper.getValue(expression);
cell.add(String.valueOf(value));
}
cells.add(cell);
}
return cells;
}
}
package com.kim.apidoc.core.common.helper;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserFieldDeclaration;
import com.kim.apidoc.core.schema.Cell;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* The type Field helper.
*/
public class FieldHelper {
/**
* 通过access方法,获取属性名
*
* @param methodName access方法名
* @return 属性名
* @return 属性名 string
*/
public static String getByAccessMethod(String methodName){
if(methodName.startsWith("is") && methodName.length()>2){
public static String getByAccessMethod(String methodName) {
if (methodName.startsWith("is") && methodName.length() > 2) {
String first = methodName.substring(2, 3);
String less = methodName.substring(3);
return first.toLowerCase() + less;
}
if(methodName.startsWith("get") && methodName.length()>3){
if (methodName.startsWith("get") && methodName.length() > 3) {
String first = methodName.substring(3, 4);
String less = methodName.substring(4);
return first.toLowerCase() + less;
......@@ -27,12 +38,45 @@ public class FieldHelper {
return null;
}
public static Optional<Expression> getInitializer(ResolvedFieldDeclaration declaredField){
if(declaredField instanceof JavaParserFieldDeclaration){
/**
* Get initializer optional.
*
* @param declaredField the declared field
* @return the optional
*/
public static Optional<Expression> getInitializer(ResolvedFieldDeclaration declaredField) {
if (declaredField instanceof JavaParserFieldDeclaration) {
JavaParserFieldDeclaration field = (JavaParserFieldDeclaration) declaredField;
return field.getVariableDeclarator().getInitializer();
}
return Optional.empty();
}
/**
* 获取常量
*
* @param declaration the declaration
* @return constants
*/
public static List<Cell<String>> getConstants(ClassOrInterfaceDeclaration declaration) {
List<Cell<String>> cells = new ArrayList<>();
for (FieldDeclaration field : declaration.getFields()) {
if (field.isStatic() && field.isPublic() && field.isFinal()) {
VariableDeclarator variable = field.getVariable(0);
String value = null;
String description = null;
if (variable.getInitializer().isPresent()) {
value = String.valueOf(ExpressionHelper.getValue(variable.getInitializer().get()));
}
if (field.getComment().isPresent()) {
description = CommentHelper.getContent(field.getComment().get());
}
cells.add(new Cell<>(variable.getNameAsString(), value, description));
}
}
return cells;
}
}
package com.kim.apidoc.core.parser;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.EnumConstantDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
......@@ -8,15 +9,14 @@ import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.javadoc.Javadoc;
import com.github.javaparser.javadoc.JavadocBlockTag;
import com.github.javaparser.symbolsolver.core.resolution.Context;
import com.kim.apidoc.core.ApiDoc;
import com.kim.apidoc.core.common.helper.OptionalHelper;
import com.kim.apidoc.core.schema.Chapter;
import com.kim.apidoc.core.schema.Node;
import com.kim.apidoc.core.schema.Project;
import com.kim.apidoc.core.schema.Section;
import com.kim.apidoc.core.schema.*;
import java.util.Objects;
import java.util.Optional;
import static com.kim.apidoc.core.schema.Chapter.NODE_CHAPTER;
/**
* The type Visitor parser.
......@@ -39,9 +39,22 @@ public class VisitorParser extends VoidVisitorAdapter<Node> {
@Override
public void visit(final EnumDeclaration enumDeclaration, final Node arg) {
super.visit(enumDeclaration, arg);
// 访问枚举
if (arg instanceof Project) {
Project project = (Project) arg;
// 章节
Chapter chapter = new Chapter();
enumDeclaration.getFullyQualifiedName().ifPresent(chapter::setId);
chapter.setName(enumDeclaration.getNameAsString());
enumDeclaration.getComment().ifPresent(chapter::accept);
OptionalHelper.any(chapter.getTag("book"), chapter.getTag("group"))
.ifPresent(tag -> chapter.setBookName(tag.getContent()));
project.addChapter(chapter);
super.visit(enumDeclaration, chapter);
}
}
/**
* 类或者接口声明
*
......@@ -62,52 +75,39 @@ public class VisitorParser extends VoidVisitorAdapter<Node> {
.ifPresent(tag -> chapter.setBookName(tag.getContent()));
if (parserStrategy.accept(classOrInterfaceDeclaration)) {
chapter.setType(NODE_CHAPTER);
parserStrategy.visit(classOrInterfaceDeclaration, chapter);
project.addChapter(chapter);
super.visit(classOrInterfaceDeclaration, chapter);
} else if (isExistTag(classOrInterfaceDeclaration, "code")) {
//chapter.setType(NODE_APPENDIX);
//if (null==project.getAppendixChapter(chapter.getBookName())){
// project.addChapter(chapter);
//}
super.visit(classOrInterfaceDeclaration, arg);
} else if (isExistTag(classOrInterfaceDeclaration, "resultData")) {
}
super.visit(classOrInterfaceDeclaration, chapter);
}
}
/**
* 处理特殊的tag
*
* @param classOrInterfaceDeclaration the class or interface declaration
* @param tag the tag
* @return boolean boolean
*/
private Boolean isExistTag(ClassOrInterfaceDeclaration classOrInterfaceDeclaration, String tag) {
if (classOrInterfaceDeclaration.getComment().isPresent()) {
Comment comment = classOrInterfaceDeclaration.getComment().get();
if (!comment.isJavadocComment()) {
return false;
}
Javadoc javadoc = comment.asJavadocComment().parse();
Optional<JavadocBlockTag> blockTagOptional =
javadoc.getBlockTags().stream().filter(blockTag -> {
return tag.equalsIgnoreCase(blockTag.getTagName());
}).findFirst();
if (blockTagOptional.isPresent()) {
return true;
@Override
public void visit(JavadocComment javadocComment, Node arg) {
if (arg instanceof Chapter) {
Chapter chapter = (Chapter) arg;
OptionalHelper.any(chapter.getTag("code"))
.ifPresent(tag -> {
if (javadocComment.getCommentedNode().isPresent()){
com.github.javaparser.ast.Node commentedNode=javadocComment.getCommentedNode().get();
// 常量类||枚举类
if (commentedNode instanceof ClassOrInterfaceDeclaration
|| commentedNode instanceof EnumDeclaration){
Appendix appendix = Appendix.parse(javadocComment);
if (appendix != null) {
ApiDoc.getInstance().getProject().getAppendices().add(appendix);
}
}
return false;
}
});
@Override
public void visit(JavadocComment n, Node arg) {
super.visit(n, arg);
OptionalHelper.any(chapter.getTag("resultData"))
.ifPresent(tag -> {
System.out.println(javadocComment);
});
}
super.visit(javadocComment, arg);
}
/**
......
......@@ -2,6 +2,8 @@ package com.kim.apidoc.core.render;
import com.kim.apidoc.core.ApiDoc;
import com.kim.apidoc.core.common.Constants;
import com.kim.apidoc.core.common.ObjectMappers;
import com.kim.apidoc.core.common.helper.FileHelper;
import com.kim.apidoc.core.schema.Project;
import freemarker.template.Configuration;
import freemarker.template.Template;
......@@ -9,6 +11,7 @@ import lombok.extern.slf4j.Slf4j;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.nio.file.Path;
......@@ -50,10 +53,12 @@ public class MarkdownRender implements ProjectRender {
Template template = configuration.getTemplate(templatePath);
try (FileOutputStream fileOutputStream = new FileOutputStream(markdownFile.toString())) {
template.process(project, new OutputStreamWriter(fileOutputStream, Constants.UTF8));
}
StringWriter writer = new StringWriter();
template.process(project, writer);
writer.flush();
writer.close();
FileHelper.write(markdownFile, writer.getBuffer().toString());
log.info("Build Markdown {}", markdownFile);
}
......
package com.kim.apidoc.core.schema;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
import com.kim.apidoc.core.common.helper.EnumHelper;
import com.kim.apidoc.core.common.helper.FieldHelper;
import com.kim.apidoc.core.common.helper.FileHelper;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* 附录
*/
@Setter
@Getter
public class Appendix extends Node {
/**
* The Cells.
*/
List<Cell<String>> cells = new ArrayList<>();
/**
* Is empty boolean.
*
* @return the boolean
*/
public boolean isEmpty() {
return cells.isEmpty();
}
/**
* Parse appendix.
*
* @param n the n
* @return the appendix
*/
@Nullable
public static Appendix parse(JavadocComment n) {
if (!n.getCommentedNode().isPresent()) {
return null;
}
final com.github.javaparser.ast.Node node = n.getCommentedNode().get();
if (!(node instanceof BodyDeclaration)) {
return null;
}
final BodyDeclaration bodyDeclaration = (BodyDeclaration) node;
// 不是枚举且不是类
if (!bodyDeclaration.isEnumDeclaration() && !bodyDeclaration.isClassOrInterfaceDeclaration()) {
return null;
}
Appendix appendix = new Appendix();
// 是枚举
if (bodyDeclaration.isEnumDeclaration()) {
appendix.getCells().addAll(EnumHelper.toDetails(bodyDeclaration.asEnumDeclaration()));
} else if (bodyDeclaration.isClassOrInterfaceDeclaration()) {
// 常量类
appendix.getCells().addAll(FieldHelper.getConstants(bodyDeclaration.asClassOrInterfaceDeclaration()));
}
if (node instanceof NodeWithSimpleName) {
appendix.setName(((NodeWithSimpleName) node).getNameAsString());
}
if (node.getComment().isPresent()) {
appendix.accept(node.getComment().get());
}
return appendix;
}
}
package com.kim.apidoc.core.schema;
import com.google.common.collect.Lists;
import java.util.List;
/**
* 多个数据的组合
*
* @param <T> the type parameter
*/
public class Cell<T> {
/**
* The Values.
*/
private List<T> values;
/**
* The Enable.
*/
private boolean enable;
/**
* Instantiates a new Cell.
*
* @param values the values
*/
@SafeVarargs
public Cell(T ... values) {
this(true,values);
}
/**
* Instantiates a new Cell.
*
* @param enable the enable
* @param values the values
*/
@SafeVarargs
public Cell(boolean enable, T ... values) {
this(enable, Lists.newArrayList(values));
}
/**
* Instantiates a new Cell.
*
* @param enable the enable
* @param values the values
*/
public Cell(boolean enable, List<T> values) {
this.values = values;
this.enable = enable;
}
/**
* To list list.
*
* @return the list
*/
public List<T> toList(){
return values;
}
/**
* Is enable boolean.
*
* @return the boolean
*/
public boolean isEnable() {
return enable;
}
/**
* Add.
*
* @param value the value
*/
public void add(T value){
values.add(value);
}
/**
* Size int.
*
* @return the int
*/
public int size(){
return values.size();
}
/**
* Set.
*
* @param index the index
* @param t the t
*/
public void set(int index, T t){
values.set(index, t);
}
/**
* Get t.
*
* @param index the index
* @return the t
*/
public T get(int index){
return values.get(index);
}
/**
* Duplicate cell.
*
* @return the cell
*/
public Cell<T> duplicate(){
return new Cell<>(isEnable(), Lists.newArrayList(values));
}
/**
* Sets enable.
*
* @param enable the enable
*/
public void setEnable(boolean enable) {
this.enable = enable;
}
}
......@@ -15,20 +15,13 @@ import java.util.TreeSet;
@Setter
@Getter
public class Chapter extends Node {
/**
* 章节节点.
*/
public static final String NODE_CHAPTER = "NODE_CHAPTER";
/**
* 附录节点.
*/
public static final String NODE_APPENDIX = "NODE_APPENDIX";
/**
* The Book name.
*/
String bookName;
Project project;
/**
* The Sections.
*/
......
......@@ -2,11 +2,9 @@ package com.kim.apidoc.core.schema;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import static com.kim.apidoc.core.schema.Chapter.NODE_APPENDIX;
/**
* 项目
......@@ -28,12 +26,19 @@ public class Project extends Node {
*/
Map<String, Book> books = new TreeMap<>();
/**
* 附录
*/
List<Appendix> appendices = new LinkedList<>();
/**
* Add chapter.
*
* @param chapter the chapter
*/
public void addChapter(Chapter chapter) {
chapter.setProject(this);
if (Objects.isNull(chapter.getBookName())) {
chapter.setBookName(Book.DEFAULT);
}
......@@ -42,17 +47,4 @@ public class Project extends Node {
}
books.get(chapter.getBookName()).getChapters().add(chapter);
}
public Chapter getAppendixChapter(String bookName) {
if (StringUtils.isBlank(bookName)) {
bookName = Book.DEFAULT;
}
if (books.containsKey(bookName)) {
Optional<Chapter> chapterOptional = books.get(bookName).getChapters().stream().filter(x -> NODE_APPENDIX.equals(x.getType())).findFirst();
if (chapterOptional.isPresent()) {
return chapterOptional.get();
}
}
return null;
}
}
......@@ -67,19 +67,6 @@ public class Section extends Node {
*/
Object rawResponse;
/**
* 全局编码列表
*/
Map<String, Row> codeRows = new LinkedHashMap<>();
/**
* Add code rows.
*
* @param row the row
*/
public void addCodeRows(Row row) {
codeRows.put(row.getKey(), row);
}
/**
* Add request row.
......
......@@ -38,7 +38,7 @@ ${section.description}
</#if>
<#-- 请求-->
**Request**
**请求**
<#-- 请求示例-->
```HTTP
......@@ -56,8 +56,7 @@ ${section.getParameterString()}
<#-- 请求参数table列表-->
<#if section.requestRows?? && (section.requestRows?size>0)>
**Request**
| Field | Type | Request Type | Required | Condition | Default | Description |
| 字段 | 类型 | 参数类型 | 是否必填 | 验证 | 默认值 | 描述 |
| :------- | :----- | :----- |:-------- |:-------- | :------ | :---------- |
<#list section.requestRows as rowKey,rowValue>
| ${rowValue.key!''} | ${rowValue.type!''} | **${rowValue.requestParameterType!''}** |${rowValue.required?string('true','false')} | ${rowValue.condition!''} | ${rowValue.def!''} | ${rowValue.remark!''} |
......@@ -65,7 +64,7 @@ ${section.getParameterString()}
</#if>
<#-- 响应-->
<#if section.hasResponseBody()>
**Response**
**响应**
```
${section.getResponseString()}
```
......@@ -73,7 +72,7 @@ ${section.getResponseString()}
<#-- 响应参数table列表-->
<#if section.responseRows?? && (section.responseRows?size>0)>
| Field | Type | Description |
| 字段 | 类型 | 描述 |
| :------- | :----- | :---------- |
<#list section.responseRows as rowKey,rowValue>
| ${rowValue.key!''} | ${rowValue.type!''} | ${rowValue.remark!''} |
......
......@@ -17,7 +17,7 @@ public class SpringTest {
Context context = new Context();
context.setId("test");
context.setName("测试项目");
context.addSource(Paths.get("K:\\@project-dianli@\\tool\\apidoc\\apidoc-springmvc\\src\\test\\java"));
context.addSource(Paths.get("F:\\@project@\\@dianli@\\tool\\apidoc\\apidoc-springmvc\\src\\test\\java"));
// context.setCss("https://darshandsoni.com/asciidoctor-skins/css/monospace.css");
ApiDoc apigcc = new ApiDoc(context);
......
......@@ -14,7 +14,7 @@
添加用户
**request**
**请求**
```HTTP
POST /api/v1/users HTTP/1.1
......@@ -26,31 +26,44 @@ POST /api/v1/users HTTP/1.1
}
```
**header**
| 字段 | 类型 | 参数类型 | 是否必填 | 默认值 | 描述 |
| :------- | :----- | ---------- | :-------- | :------ | :---------- |
| loginId | String | Body | 必填 | | 用户编号 |
| userName | String | Body | 必填 | | 用户名 |
| password | String | Body | 必填 | | 密码 |
| Field | Type | Condition | Default | Description |
| :------- | :----- | :-------- | :------ | :---------- |
| loginId | String | | | 用户编号 |
| userName | String | | | 用户名 |
| password | String | | | 密码 |
**响应**
**query**
```
{
"loginId":"admin",
"userName":"123456",
"passwords":"123456"
}
```
| 字段 | 类型 | 描述 |
| :------- | :----- | :---------- |
| loginId | String | 用户编号 |
| userName | String | 用户名 |
| password | String | 密码 |
| Field | Type | Condition | Default | Description |
| :------- | :----- | :-------- | :------ | :---------- |
| loginId | String | | | 用户编号 |
| userName | String | | | 用户名 |
| password | String | | | 密码 |
### 1.2 查询用户列表
**body**
查询用户列表
**请求**
```HTTP
GET /api/v1/users?userName=XXX&sex=XXX HTTP/1.1E
```
| Field | Type | Condition | Default | Description |
| :------- | :----- | :-------- | :------ | :---------- |
| loginId | String | | | 用户编号 |
| userName | String | | | 用户名 |
| password | String | | | 密码 |
| 字段 | 类型 | 参数类型 | 是否必填 | 默认值 | 描述 |
| :------- | :----- | :-------- | :------ | :---------- | ----------- |
| userName | String | QUERY | | |用户名|
| sex | String | QUERY | | |性别|
**response**
**响应**
```
{
......@@ -60,9 +73,36 @@ POST /api/v1/users HTTP/1.1
}
```
| Field | Type | Description |
| 字段 | 类型 | 描述 |
| :------- | :----- | :---------- |
| loginId | String | 用户编号 |
| userName | String | 用户名 |
| password | String | 密码 |
### 1.3 删除用户
删除用户
**请求**
```HTTP
DELETE /api/v1/users/:userId HTTP/1.1E
```
| 字段 | 类型 | 参数类型 | 是否必填 | 默认值 | 描述 |
| :------- | :----- | :-------- | :------ | :---------- | ----------- |
| userId | String | PATH | | |用户编号|
**响应**
```
{
"data":"true"
}
```
| 字段 | 类型 | 描述 |
| :------- | :----- | :---------- |
| data | String | 是否成功 |
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment