Optimize the generation of openapi and postman collection

This commit is contained in:
oppofind 2020-11-30 01:16:15 +08:00
parent 1d43300b00
commit 9c404e5f81
23 changed files with 1293 additions and 328 deletions

View File

@ -11,6 +11,7 @@ import com.power.doc.model.*;
import com.power.doc.template.SpringBootDocBuildTemplate;
import com.power.doc.utils.DocUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@ -21,6 +22,7 @@ public class OpenApiBuilder {
//过滤url中的特殊字符
private static final String PATH_REGEX = "[/{};\\t+]";
/**
* 构建postman json
*
@ -49,6 +51,7 @@ public class OpenApiBuilder {
/**
* 构建openApi 文件
*
* @param config 配置文件
* @param configBuilder
*/
@ -57,7 +60,7 @@ public class OpenApiBuilder {
SpringBootDocBuildTemplate docBuildTemplate = new SpringBootDocBuildTemplate();
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);
Map<String, Object> json = new HashMap<>(8);
json.put("openapi", "3.0.0");
json.put("openapi", "3.0.3");
json.put("info", buildInfo(config));
json.put("servers", buildServers(config));
json.put("paths", buildPaths(apiDocList));
@ -74,6 +77,7 @@ public class OpenApiBuilder {
/**
* info 信息
*
* @param apiConfig 文档配置信息
* @return
*/
@ -83,20 +87,24 @@ public class OpenApiBuilder {
infoMap.put("version", "1.0.0");
return infoMap;
}
/**
* server 信息
*
* @param config 文档配置信息
* @return
*/
private static List<Map<String, Object>> buildServers(ApiConfig config) {
List<Map<String, Object>> serverList = new ArrayList<>();
Map<String, Object> serverMap = new HashMap<>(8);
serverMap.put("url", config.getServerUrl() == null ? "http://127.0.0.1" : config.getServerUrl());
serverMap.put("url", config.getServerUrl() == null ? "" : config.getServerUrl());
serverList.add(serverMap);
return serverList;
}
/**
* 构建path数据 url请求
*
* @param apiDocList api列表
* @return
*/
@ -123,8 +131,10 @@ public class OpenApiBuilder {
);
return pathMap;
}
/**
* paths 设置url请求方式
*
* @param apiMethodDoc 方法参数
* @param apiDoc 类参数
* @return
@ -134,8 +144,10 @@ public class OpenApiBuilder {
request.put(apiMethodDoc.getType().toLowerCase(), buildPathUrlsRequest(apiMethodDoc, apiDoc));
return request;
}
/**
* url的基本信息 信息简介summary 详情description 所属分类tags 请求参数parameter 请求体request 返回值responses
*
* @param apiMethodDoc 方法参数
* @param apiDoc 类参数
* @return
@ -148,25 +160,22 @@ public class OpenApiBuilder {
request.put("requestBody", buildRequestBody(apiMethodDoc));
request.put("parameters", buildParameters(apiMethodDoc));
request.put("responses", buildResponses(apiMethodDoc));
request.put("deprecated", apiMethodDoc.isDeprecated());
request.put("operationId", apiMethodDoc.getMethodId());
return request;
}
/**
* 请求体构建 只针对post请求 并且请求体不为null
*
* @param apiMethodDoc 方法参数
* @return
*/
private static Map<String, Object> buildRequestBody(ApiMethodDoc apiMethodDoc) {
int size = 0;
Map<String, Object> requestBody = new HashMap<>(8);
//判断请求体去除pathVariable参数后是否为null
if(!CollectionUtil.isEmpty(apiMethodDoc.getRequestParams())) {
List<ApiParam> apiParams = apiMethodDoc.getRequestParams();
//去除pathVariable参数
size = (int) apiParams.stream()
.filter(apiParam -> !apiParam.isPathParam()).count();
}
boolean isPost = (apiMethodDoc.getType().equals(Methods.POST.getValue()) || apiMethodDoc.getType().equals(Methods.PUT.getValue()) ||
apiMethodDoc.getType().equals(Methods.PATCH.getValue())) && size > 0;
boolean isPost = (apiMethodDoc.getType().equals(Methods.POST.getValue())
|| apiMethodDoc.getType().equals(Methods.PUT.getValue()) ||
apiMethodDoc.getType().equals(Methods.PATCH.getValue()));
//如果是post请求 且包含请求体
if (isPost) {
requestBody.put("content", buildContent(apiMethodDoc, false));
@ -175,33 +184,78 @@ public class OpenApiBuilder {
return null;
}
/**
* 构建content信息 responses requestBody 都需要content信息
*
* @param apiMethodDoc 方法参数
* @param isRep 是否是返回数据
* @return
*/
private static Map<String, Object> buildContent(ApiMethodDoc apiMethodDoc, boolean isRep) {
Map<String, Object> content = new HashMap<>(8);
content.put(apiMethodDoc.getContentType(), buildContentBody(apiMethodDoc, isRep));
String contentType = apiMethodDoc.getContentType();
if (isRep) {
contentType = "*/*";
}
content.put(contentType, buildContentBody(apiMethodDoc, isRep));
return content;
}
/**
* 构建content的数据内容
*
* @param apiMethodDoc 方法参数
* @param isRep 是否是返回数据
* @return
*/
private static Map<String, Object> buildContentBody(ApiMethodDoc apiMethodDoc, boolean isRep) {
Map<String, Object> content = new HashMap<>(8);
if (Objects.nonNull(apiMethodDoc.getReturnSchema()) && isRep) {
content.put("schema", apiMethodDoc.getReturnSchema());
} else {
if (!isRep && apiMethodDoc.getContentType().equals(DocGlobalConstants.MULTIPART_TYPE)) {
// formdata
Map<String, Object> map = new LinkedHashMap<>();
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", apiParam.getValue());
if ("file".equals(apiParam.getType())) {
if (apiParam.isHasItems()) {
detail.put("type", "array");
Map<String, Object> items = new HashMap<>();
items.put("type", "string");
items.put("format", "binary");
detail.put("items", items);
} else {
detail.put("format", "binary");
}
}
properties.put(apiParam.getField(), detail);
}
map.put("properties", properties);
content.put("schema", map);
} else if (!isRep && Objects.nonNull(apiMethodDoc.getRequestSchema())) {
content.put("schema", apiMethodDoc.getRequestSchema());
} else {
content.put("schema", buildBodySchema(apiMethodDoc.getPath(), isRep));
}
}
content.put("examples", buildBodyExample(apiMethodDoc, isRep));
return content;
}
/**
* content body 的schema 信息
*
* @param url 请求的url 去除server
* @param isRep 是否是返回数据
* @return
@ -216,8 +270,10 @@ public class OpenApiBuilder {
}
return schema;
}
/**
* 信息样例 请求和返回的信息样例
*
* @param apiMethodDoc 方法参数
* @param isRep 是否是返回数据
* @return
@ -228,8 +284,10 @@ public class OpenApiBuilder {
return content;
}
/**
* 信息样例数据构建 此处请求体requestBody构建完成
*
* @param apiMethodDoc 方法参数
* @param isRep 是否为返回数据
* @return
@ -250,31 +308,32 @@ public class OpenApiBuilder {
/**
* 构建请求参数 用于get请求 @PathVariable Header 参数构建
*
* @param apiMethodDoc 方法体
* @return
*/
private static List<Map<String, Object>> buildParameters(ApiMethodDoc apiMethodDoc) {
Map<String, Object> parameters;
List<Map<String, Object>> parametersList = new ArrayList<>();
//如果是get请求 包含@pathvariable
if (apiMethodDoc.getType().equals(Methods.GET.getValue()) || apiMethodDoc.getPath().contains("{")) {
if (apiMethodDoc.getRequestParams() == null) {
return null;
}
for (ApiParam apiParam : apiMethodDoc.getRequestParams()) {
parameters = new HashMap<>(20);
parameters.put("name", apiParam.getField());
parameters.put("description", apiParam.getDesc());
parameters.put("required", apiParam.isRequired());
parameters.put("schema", buildParametersSchema(apiParam));
if (apiParam.isPathParam()) {
for (ApiParam apiParam : apiMethodDoc.getPathParams()) {
parameters = getStringParams(apiParam);
parameters.put("in", "path");
} else {
parameters.put("in", "query");
}
List<ApiParam> children = apiParam.getChildren();
if (CollectionUtil.isEmpty(children)) {
parametersList.add(parameters);
}
}
// not handle form data
if (!apiMethodDoc.getContentType().equals(DocGlobalConstants.MULTIPART_TYPE)) {
for (ApiParam apiParam : apiMethodDoc.getQueryParams()) {
parameters = getStringParams(apiParam);
parameters.put("in", "query");
List<ApiParam> children = apiParam.getChildren();
if (CollectionUtil.isEmpty(children)) {
parametersList.add(parameters);
}
}
}
//如果包含请求头
if (!CollectionUtil.isEmpty(apiMethodDoc.getRequestHeaders())) {
for (ApiReqHeader header : apiMethodDoc.getRequestHeaders()) {
@ -289,8 +348,22 @@ public class OpenApiBuilder {
}
return parametersList;
}
@NotNull
private static Map<String, Object> getStringParams(ApiParam apiParam) {
Map<String, Object> parameters;
parameters = new HashMap<>(20);
parameters.put("name", apiParam.getField());
parameters.put("description", apiParam.getDesc());
parameters.put("required", apiParam.isRequired());
parameters.put("example", StringUtil.removeQuotes(apiParam.getValue()));
parameters.put("schema", buildParametersSchema(apiParam));
return parameters;
}
/**
* 如果是get请求或者是@PathVariable 设置请求参数
*
* @param apiParam 参数信息
* @return
*/
@ -307,8 +380,10 @@ public class OpenApiBuilder {
}
return schema;
}
/**
* 如果包含header 设置请求参数
*
* @param header 参数信息
* @return
*/
@ -319,8 +394,10 @@ public class OpenApiBuilder {
schema.put("format", "int16".equals(header.getType()) ? "int32" : header.getType());
return schema;
}
/**
* 构建返回信息
*
* @param apiMethodDoc 方法参数
* @return
*/
@ -329,22 +406,23 @@ public class OpenApiBuilder {
response.put("200", buildResponsesBody(apiMethodDoc));
return response;
}
/**
* 构建返回信息实体
*
* @param apiMethodDoc 方法参数
* @return
*/
private static Map<String, Object> buildResponsesBody(ApiMethodDoc apiMethodDoc) {
Map<String, Object> responseBody = new HashMap<>(10);
responseBody.put("description", "a pet to be returned");
if (!CollectionUtil.isEmpty(apiMethodDoc.getResponseParams())) {
responseBody.put("description", "OK");
responseBody.put("content", buildContent(apiMethodDoc, true));
}
return responseBody;
}
/**
* 构建component
*
* @param apiDocs 请求列表
* @return
*/
@ -358,7 +436,12 @@ public class OpenApiBuilder {
method -> {
//request components
List<ApiParam> requestParams = method.getRequestParams();
if (CollectionUtil.isNotEmpty(requestParams)) {
Map<String, Object> prop = buildProperties(requestParams);
if (Objects.nonNull(prop) && prop.size() > 0) {
component.put(method.getPath().replaceAll(PATH_REGEX, "_") + "request", buildProperties(requestParams));
}
}
//response components
List<ApiParam> responseParams = method.getResponseParams();
component.put(method.getPath().replaceAll(PATH_REGEX, "_") + "response", buildProperties(responseParams));
@ -372,24 +455,32 @@ public class OpenApiBuilder {
/**
* component schema properties 信息
*
* @param apiParam 参数列表
* @return
*/
private static Map<String, Object> buildProperties(List<ApiParam> apiParam) {
Map<String, Object> component = new HashMap<>();
Map<String, Object> propertiesData = new HashMap<>();
Map<String, Object> propertiesData = new LinkedHashMap<>();
List<String> requiredList = new ArrayList<>();
if (apiParam != null) {
int paramsSize = apiParam.size();
for (ApiParam param : apiParam) {
if (param.isRequired()) {
requiredList.add(param.getField());
}
if (param.getType().equals("map") && paramsSize == 1) {
continue;
}
if (param.isQueryParam() || param.isPathParam()) {
continue;
}
String field = param.getField();
propertiesData.put(field, buildPropertiesData(param));
}
if (!propertiesData.isEmpty()) {
component.put("properties", propertiesData);
}
if (!CollectionUtil.isEmpty(requiredList)) {
component.put("required", requiredList);
}
@ -399,8 +490,10 @@ public class OpenApiBuilder {
}
}
/**
* component schema properties 实体信息构建
*
* @param apiParam 参数基本信息
* @return
*/

View File

@ -28,20 +28,22 @@ 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.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiMethodDoc;
import com.power.doc.model.ApiReqHeader;
import com.power.doc.model.*;
import com.power.doc.model.postman.InfoBean;
import com.power.doc.model.postman.ItemBean;
import com.power.doc.model.postman.RequestItem;
import com.power.doc.model.postman.UrlBean;
import com.power.doc.model.postman.request.ParamBean;
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.thoughtworks.qdox.JavaProjectBuilder;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@ -119,13 +121,59 @@ public class PostmanJsonBuilder {
requestBean.setHeader(buildHeaderBeanList(apiMethodDoc));
requestBean.setBody(buildBodyBean(apiMethodDoc));
requestBean.setUrl(apiMethodDoc.getRequestExample().getUrl() == null ? apiMethodDoc.getUrl() : apiMethodDoc.getRequestExample().getUrl());
requestBean.setUrl(buildUrlBean(apiMethodDoc));
item.setRequest(requestBean);
return item;
}
private static UrlBean buildUrlBean(ApiMethodDoc apiMethodDoc) {
UrlBean urlBean = new UrlBean();
String url = apiMethodDoc.getRequestExample().getUrl() == null ? apiMethodDoc.getUrl() : apiMethodDoc.getRequestExample().getUrl();
urlBean.setRaw(PathUtil.toPostmanPath(url));
try {
URL url1 = new java.net.URL(apiMethodDoc.getServerUrl());
urlBean.setPort(String.valueOf(url1.getPort()));
List<String> hosts = new ArrayList<>();
hosts.add(url1.getHost());
urlBean.setHost(hosts);
} catch (MalformedURLException e) {
}
String shortUrl = PathUtil.toPostmanPath(apiMethodDoc.getPath());
String[] paths = shortUrl.split("/");
List<String> pathList = new ArrayList<>();
for (String str : paths) {
if (StringUtil.isNotEmpty(str)) {
pathList.add(str);
}
}
if (shortUrl.endsWith("/")) {
pathList.add("");
}
urlBean.setPath(pathList);
List<ParamBean> queryParams = new ArrayList<>();
for (ApiParam apiParam : apiMethodDoc.getQueryParams()) {
ParamBean queryParam = new ParamBean();
queryParam.setDescription(apiParam.getDesc());
queryParam.setKey(apiParam.getField());
queryParam.setValue(apiParam.getValue());
queryParams.add(queryParam);
}
List<ParamBean> variables = new ArrayList<>();
for (ApiParam apiParam : apiMethodDoc.getPathParams()) {
ParamBean queryParam = new ParamBean();
queryParam.setDescription(apiParam.getDesc());
queryParam.setKey(apiParam.getField());
queryParam.setValue(apiParam.getValue());
variables.add(queryParam);
}
urlBean.setVariable(variables);
urlBean.setQuery(queryParams);
return urlBean;
}
/**
* 构造请求体
*

View File

@ -71,7 +71,7 @@ public interface DocGlobalConstants {
String POSTMAN_JSON = "/postman.json";
String OPEN_API_JSON = "/openApi3.0.json";
String OPEN_API_JSON = "/openapi.json";
String CONTROLLER_FULLY = "org.springframework.stereotype.Controller";

View File

@ -109,6 +109,9 @@ public class JsonBuildHelper {
}
}
if (JavaClassValidateUtil.isPrimitive(typeName)) {
if(DocGlobalConstants.JAVA_STRING_FULLY.equals(typeName)){
return "string";
}
return StringUtil.removeQuotes(DocUtil.jsonValueByType(typeName));
}
if (javaClass.isEnum()) {

View File

@ -216,8 +216,14 @@ public class ParamsBuildHelper {
continue;
}
if (JavaClassValidateUtil.isPrimitive(subTypeName)) {
String fieldValue = "";
if (tagsMap.containsKey(DocTags.MOCK) && StringUtil.isNotEmpty(tagsMap.get(DocTags.MOCK))) {
fieldValue = tagsMap.get(DocTags.MOCK);
} else {
fieldValue = DocUtil.getValByTypeAndFieldName(typeSimpleName, field.getName());
}
ApiParam param = ApiParam.of().setField(pre + fieldName);
param.setPid(pid);
param.setPid(pid).setValue(fieldValue);
String processedType = isShowJavaType ? typeSimpleName : DocClassUtil.processTypeNameForParams(typeSimpleName.toLowerCase());
param.setType(processedType);
if (StringUtil.isNotEmpty(comment)) {

View File

@ -26,6 +26,7 @@ import com.power.doc.model.request.ApiRequestExample;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* java api method info model.
@ -149,6 +150,16 @@ public class ApiMethodDoc implements Serializable {
*/
private boolean deprecated;
/**
* return schema
*/
private Map<String,Object> returnSchema;
/**
* request schema
*/
private Map<String,Object> requestSchema;
public String getMethodId() {
return methodId;
@ -318,6 +329,23 @@ public class ApiMethodDoc implements Serializable {
this.queryParams = queryParams;
}
public Map<String, Object> getReturnSchema() {
return returnSchema;
}
public void setReturnSchema(Map<String, Object> returnSchema) {
this.returnSchema = returnSchema;
}
public Map<String, Object> getRequestSchema() {
return requestSchema;
}
public void setRequestSchema(Map<String, Object> requestSchema) {
this.requestSchema = requestSchema;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");

View File

@ -0,0 +1,77 @@
/*
* smart-doc
*
* Copyright (C) 2018-2020 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;
import java.util.List;
/**
* @author yu 2020/11/26.
*/
public class ApiMethodReqParam {
/**
* path params
*/
private List<ApiParam> pathParams;
/**
* query params
*/
private List<ApiParam> queryParams;
/**
* http request params
*/
private List<ApiParam> requestParams;
public static ApiMethodReqParam builder(){
return new ApiMethodReqParam();
}
public List<ApiParam> getPathParams() {
return pathParams;
}
public ApiMethodReqParam setPathParams(List<ApiParam> pathParams) {
this.pathParams = pathParams;
return this;
}
public List<ApiParam> getQueryParams() {
return queryParams;
}
public ApiMethodReqParam setQueryParams(List<ApiParam> queryParams) {
this.queryParams = queryParams;
return this;
}
public List<ApiParam> getRequestParams() {
return requestParams;
}
public ApiMethodReqParam setRequestParams(List<ApiParam> requestParams) {
this.requestParams = requestParams;
return this;
}
}

View File

@ -23,6 +23,7 @@
package com.power.doc.model;
import java.util.List;
import java.util.Map;
/**
* @author yu 2019/9/27.
@ -75,7 +76,7 @@ public class ApiParam {
private boolean queryParam;
/**
*
* param mock value
*/
private String value;
@ -84,6 +85,11 @@ public class ApiParam {
*/
private List<ApiParam> children;
/**
* openapi items
*/
private boolean hasItems;
public static ApiParam of(){
return new ApiParam();
}
@ -187,6 +193,15 @@ public class ApiParam {
return this;
}
public boolean isHasItems() {
return hasItems;
}
public ApiParam setHasItems(boolean hasItems) {
this.hasItems = hasItems;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");

View File

@ -13,6 +13,10 @@ public class DocJavaMethod {
private JavaMethod javaMethod;
private Map<String,Object> returnSchema;
private Map<String,Object> requestSchema;
private Map<String, JavaType> actualTypesMap;
public static DocJavaMethod builder(){
@ -36,4 +40,22 @@ public class DocJavaMethod {
this.actualTypesMap = actualTypesMap;
return this;
}
public Map<String,Object> getReturnSchema() {
return returnSchema;
}
public DocJavaMethod setReturnSchema(Map<String,Object> returnSchema) {
this.returnSchema = returnSchema;
return this;
}
public Map<String, Object> getRequestSchema() {
return requestSchema;
}
public DocJavaMethod setRequestSchema(Map<String, Object> requestSchema) {
this.requestSchema = requestSchema;
return this;
}
}

View File

@ -0,0 +1,69 @@
package com.power.doc.model.postman;
import com.power.doc.model.postman.request.ParamBean;
import java.util.List;
/**
* @author yu 2020/11/28.
*/
public class UrlBean {
private String raw;
private List<String> path;
private List<String> host;
private String port;
private List<ParamBean> query;
private List<ParamBean> variable;
public String getRaw() {
return raw;
}
public void setRaw(String raw) {
this.raw = raw;
}
public List<String> getPath() {
return path;
}
public void setPath(List<String> path) {
this.path = path;
}
public List<ParamBean> getQuery() {
return query;
}
public void setQuery(List<ParamBean> query) {
this.query = query;
}
public List<ParamBean> getVariable() {
return variable;
}
public void setVariable(List<ParamBean> variable) {
this.variable = variable;
}
public List<String> getHost() {
return host;
}
public void setHost(List<String> host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}

View File

@ -0,0 +1,34 @@
package com.power.doc.model.postman.request;
/**
* @author yu 2020/11/28.
*/
public class ParamBean {
private String key;
private String value;
private String description;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -22,6 +22,7 @@
*/
package com.power.doc.model.postman.request;
import com.power.doc.model.postman.UrlBean;
import com.power.doc.model.postman.request.body.BodyBean;
import com.power.doc.model.postman.request.header.HeaderBean;
@ -33,7 +34,7 @@ import java.util.List;
public class RequestBean {
private String method;
private BodyBean body;
private String url;
private UrlBean url;
private String description;
private List<HeaderBean> header;
@ -53,11 +54,11 @@ public class RequestBean {
this.body = body;
}
public String getUrl() {
public UrlBean getUrl() {
return url;
}
public void setUrl(String url) {
public void setUrl(UrlBean url) {
this.url = url;
}

View File

@ -37,9 +37,7 @@ public class BodyBean {
private BodyOptions options;
public BodyBean(boolean isFormData) {
if (isFormData) {
} else {
if (!isFormData) {
this.options = new BodyOptions();
}
}

View File

@ -29,8 +29,8 @@ import com.power.doc.helper.ParamsBuildHelper;
import com.power.doc.model.*;
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.OpenApiSchemaUtil;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaType;
@ -120,15 +120,15 @@ public interface IDocBuildTemplate<T> {
return null;
}
if (JavaClassValidateUtil.isPrimitive(typeName)) {
String processedName = projectBuilder.getApiConfig().getShowJavaType() ?
JavaClassUtil.getClassSimpleName(typeName) : DocClassUtil.processTypeNameForParams(typeName);
return ParamsBuildHelper.primitiveReturnRespComment(processedName);
docJavaMethod.setReturnSchema(OpenApiSchemaUtil.primaryTypeSchema(typeName));
return null;
}
if (JavaClassValidateUtil.isCollection(typeName)) {
if (returnType.contains("<")) {
String gicName = returnType.substring(returnType.indexOf("<") + 1, returnType.lastIndexOf(">"));
if (JavaClassValidateUtil.isPrimitive(gicName)) {
return ParamsBuildHelper.primitiveReturnRespComment("array of " + DocClassUtil.processTypeNameForParams(gicName));
docJavaMethod.setReturnSchema(OpenApiSchemaUtil.arrayTypeSchema(gicName));
return null;
}
return ParamsBuildHelper.buildParams(gicName, "", 0, null, projectBuilder.getCustomRespFieldMap(),
Boolean.TRUE, new HashMap<>(), projectBuilder, null, 0);
@ -142,7 +142,8 @@ public interface IDocBuildTemplate<T> {
return null;
}
if (JavaClassValidateUtil.isPrimitive(keyValue[1])) {
return ParamsBuildHelper.primitiveReturnRespComment("key value");
docJavaMethod.setReturnSchema(OpenApiSchemaUtil.mapTypeSchema(keyValue[1]));
return null;
}
return ParamsBuildHelper.buildParams(keyValue[1], "", 0, null, projectBuilder.getCustomRespFieldMap(),
Boolean.TRUE, new HashMap<>(), projectBuilder, null, 0);

View File

@ -39,6 +39,7 @@ 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;
@ -50,7 +51,7 @@ import static com.power.doc.constants.DocTags.IGNORE;
* @author yu 2019/12/21.
*/
public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
private static Logger log = Logger.getLogger(SpringBootDocBuildTemplate.class.getName());
private List<ApiReqHeader> headers;
/**
@ -148,15 +149,16 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
if (method.isPrivate() || Objects.nonNull(method.getTagByName(IGNORE))) {
continue;
}
if (StringUtil.isEmpty(method.getComment()) && apiConfig.isStrict()) {
throw new RuntimeException("Unable to find comment for method " + method.getName() + " in " + cls.getCanonicalName());
}
//handle request mapping
RequestMapping requestMapping = new SpringMVCRequestMappingHandler()
.handle(projectBuilder.getServerUrl(), baseUrl, method, constantsMap);
if (Objects.isNull(requestMapping)) {
continue;
}
if (StringUtil.isEmpty(method.getComment()) && apiConfig.isStrict()) {
throw new RuntimeException("Unable to find comment for method " + method.getName() + " in " + cls.getCanonicalName());
}
methodOrder++;
ApiMethodDoc apiMethodDoc = new ApiMethodDoc();
apiMethodDoc.setName(method.getName());
@ -186,12 +188,20 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
apiMethodDoc.setServerUrl(projectBuilder.getServerUrl());
apiMethodDoc.setPath(requestMapping.getShortUrl());
apiMethodDoc.setDeprecated(requestMapping.isDeprecated());
ApiMethodReqParam apiMethodReqParam = requestParams(docJavaMethod, projectBuilder);
apiMethodDoc.setPathParams(apiMethodReqParam.getPathParams());
apiMethodDoc.setQueryParams(apiMethodReqParam.getQueryParams());
// build request params
List<ApiParam> requestParams = requestParams(docJavaMethod, projectBuilder);
if (paramsDataToTree) {
requestParams = ApiParamTreeUtil.apiParamToTree(requestParams);
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());
}
apiMethodDoc.setRequestParams(requestParams);
List<ApiReqHeader> allApiReqHeaders;
if (this.headers != null) {
allApiReqHeaders = Stream.of(this.headers, apiReqHeaders)
@ -217,6 +227,8 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
if (paramsDataToTree) {
responseParams = ApiParamTreeUtil.apiParamToTree(responseParams);
}
apiMethodDoc.setReturnSchema(docJavaMethod.getReturnSchema());
apiMethodDoc.setRequestSchema(docJavaMethod.getRequestSchema());
apiMethodDoc.setResponseParams(responseParams);
methodDocList.add(apiMethodDoc);
}
@ -444,7 +456,7 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
return requestExample;
}
private List<ApiParam> requestParams(final DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder) {
private ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, ProjectDocConfigBuilder builder) {
JavaMethod javaMethod = docJavaMethod.getJavaMethod();
boolean isStrict = builder.getApiConfig().isStrict();
Map<String, CustomRespField> responseFieldMap = new HashMap<>();
@ -453,11 +465,12 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
Map<String, String> paramTagMap = DocUtil.getParamsComments(javaMethod, DocTags.PARAM, className);
List<JavaParameter> parameterList = javaMethod.getParameters();
if (parameterList.size() < 1) {
return null;
return new ApiMethodReqParam();
}
List<ApiParam> paramList = new ArrayList<>();
Map<String, String> constantsMap = builder.getConstantsMap();
boolean requestFieldToUnderline = builder.getApiConfig().isRequestFieldToUnderline();
List<ApiParam> paramList = new ArrayList<>();
Map<String, JavaType> actualTypesMap = docJavaMethod.getActualTypesMap();
int requestBodyCounter = 0;
out:
@ -495,7 +508,12 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
if (typeName.contains(DocGlobalConstants.MULTIPART_FILE_FULLY)) {
ApiParam param = ApiParam.of().setField(paramName).setType("file")
.setId(paramList.size() + 1).setQueryParam(true)
.setDesc(comment).setRequired(true).setVersion(DocGlobalConstants.DEFAULT_VERSION);
.setRequired(true).setVersion(DocGlobalConstants.DEFAULT_VERSION);
if(typeName.contains("[]")){
comment = comment+"(array of file)";
param.setDesc(comment);
param.setHasItems(true);
}
paramList.add(param);
continue;
}
@ -542,10 +560,7 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
}
boolean required = Boolean.parseBoolean(strRequired);
boolean queryParam = false;
if (isPathVariable) {
comment = comment + " (This is path parameter.)";
} else if (!isRequestBody && requestBodyCounter > 0) {
comment = comment + " (This is query parameter.)";
if (!isRequestBody && !isPathVariable) {
queryParam = true;
}
if (JavaClassValidateUtil.isCollection(fullTypeName) || JavaClassValidateUtil.isArray(fullTypeName)) {
@ -563,6 +578,10 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
.setId(paramList.size() + 1)
.setType("array");
paramList.add(param);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.arrayTypeSchema(gicName);
docJavaMethod.setRequestSchema(map);
}
} else {
if (requestBodyCounter > 0) {
//for json
@ -583,7 +602,14 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
.setQueryParam(queryParam).setValue(value)
.setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(param);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.primaryTypeSchema(simpleName);
docJavaMethod.setRequestSchema(map);
}
} 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 (DocGlobalConstants.JAVA_MAP_FULLY.equals(typeName)) {
ApiParam apiParam = ApiParam.of().setField(paramName).setType("map")
.setId(paramList.size() + 1)
@ -591,10 +617,29 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
.setQueryParam(queryParam)
.setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION);
paramList.add(apiParam);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.mapTypeSchema("object");
docJavaMethod.setRequestSchema(map);
}
continue;
}
String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName);
paramList.addAll(ParamsBuildHelper.buildParams(gicNameArr[1], DocGlobalConstants.EMPTY, 0, "true", responseFieldMap, Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0));
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);
if (requestBodyCounter > 0) {
Map<String, Object> map = OpenApiSchemaUtil.mapTypeSchema(gicNameArr[1]);
docJavaMethod.setRequestSchema(map);
}
} else {
paramList.addAll(ParamsBuildHelper.buildParams(gicNameArr[1], DocGlobalConstants.EMPTY, 0,
"true", responseFieldMap, Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0));
}
}
// param is enum
else if (javaClass.isEnum()) {
@ -610,7 +655,26 @@ public class SpringBootDocBuildTemplate implements IDocBuildTemplate<ApiDoc> {
paramList.addAll(ParamsBuildHelper.buildParams(typeName, DocGlobalConstants.EMPTY, 0, "true", responseFieldMap, Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0));
}
}
return paramList;
List<ApiParam> pathParams = new ArrayList<>();
List<ApiParam> queryParams = new ArrayList<>();
List<ApiParam> bodyParams = new ArrayList<>();
for (ApiParam param : paramList) {
if (param.isPathParam()) {
param.setId(pathParams.size() + 1);
pathParams.add(param);
} else if (param.isQueryParam() || requestBodyCounter < 1) {
param.setId(queryParams.size() + 1);
queryParams.add(param);
} else {
param.setId(bodyParams.size() + 1);
bodyParams.add(param);
}
}
ApiMethodReqParam apiMethodReqParam = ApiMethodReqParam.builder()
.setRequestParams(bodyParams)
.setPathParams(pathParams)
.setQueryParams(queryParams);
return apiMethodReqParam;
}
private String getParamName(String paramName, JavaAnnotation annotation) {

View File

@ -463,9 +463,8 @@ public class DocUtil {
switch (type) {
case "int32":
case "int16":
return "integer";
case "int64":
return "long";
return "integer";
case "double":
case "float":
case "number":

View File

@ -0,0 +1,35 @@
package com.power.doc.utils;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author yu 2020/11/29.
*/
public class OpenApiSchemaUtil {
public static Map<String,Object> primaryTypeSchema(String primaryType){
Map<String, Object> map = new HashMap<>();
map.put("type", DocClassUtil.processTypeNameForParams(primaryType));
return map;
}
public static Map<String,Object> mapTypeSchema(String primaryType){
Map<String, Object> map = new LinkedHashMap<>();
map.put("type", "object");
Map<String, Object> items = new HashMap<>();
items.put("type", DocClassUtil.processTypeNameForParams(primaryType));
map.put("additionalProperties", items);
return map;
}
public static Map<String,Object> arrayTypeSchema(String primaryType){
Map<String, Object> map = new HashMap<>();
map.put("type", "array");
Map<String, Object> items = new HashMap<>();
items.put("type", DocClassUtil.processTypeNameForParams(primaryType));
map.put("items", items);
return map;
}
}

View File

@ -46,4 +46,18 @@ public class PathUtil {
className = className.replaceAll("\\.", "\\" + File.separator);
return parentDir + className + ".java";
}
/**
* to postman path
* @param path path
* @return String
*/
public static String toPostmanPath(String path){
if(StringUtil.isNotEmpty(path)){
path = path.replace("{",":");
path = path.replace("}","");
return path;
}
return null;
}
}

View File

@ -49,8 +49,38 @@ ${doc.headers}
|====================
<%}%>
<%if(isNotEmpty(doc.pathParams)){%>
*Path-parameters:*
[width="100%",options="header"]
[stripes=even]
|====================
|Parameter | Type|Description|Required|Since
<%
for(param in doc.pathParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.queryParams)){%>
*Query-parameters:*
[width="100%",options="header"]
[stripes=even]
|====================
|Parameter | Type|Description|Required|Since
<%
for(param in doc.queryParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
*Request-parameters:*
*Body-parameters:*
[width="100%",options="header"]
[stripes=even]

View File

@ -44,9 +44,33 @@ Header | Type|Description|Required|Since
---|---|---|---|----
${doc.headers}
<%}%>
<%if(isNotEmpty(doc.pathParams)){%>
**Path-parameters:**
Parameter | Type|Description|Required|Since
---|---|---|---|---
<%
for(param in doc.pathParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
<%if(isNotEmpty(doc.queryParams)){%>
**Query-parameters:**
Parameter | Type|Description|Required|Since
---|---|---|---|---
<%
for(param in doc.queryParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
**Request-parameters:**
**Body-parameters:**
Parameter | Type|Description|Required|Since
---|---|---|---|---

File diff suppressed because one or more lines are too long

View File

@ -31,6 +31,36 @@ ${doc.headers}
<%}%>
|====================
<%if(isNotEmpty(doc.pathParams)){%>
*Path-parameters:*
[width="100%",options="header"]
[stripes=even]
|====================
|Parameter | Type|Description|Required|Since
<%
for(param in doc.pathParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.queryParams)){%>
*Query-parameters:*
[width="100%",options="header"]
[stripes=even]
|====================
|Parameter | Type|Description|Required|Since
<%
for(param in doc.queryParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
*Request-parameters:*
@ -43,8 +73,9 @@ for(param in doc.requestParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.requestUsage)&&isRequestExample){%>
*Request-example:*
@ -64,8 +95,9 @@ for(param in doc.responseParams){
%>
|${param.field}|${param.type}|${param.desc}|${param.version}
<%}%>
<%}%>
|====================
<%}%>
<%if(isNotEmpty(doc.responseUsage)&&isResponseExample){%>
*Response-example:*

View File

@ -28,8 +28,32 @@ Header | Type|Description|Required|Since
${doc.headers}
<%}%>
<%if(isNotEmpty(doc.pathParams)){%>
**Path-parameters:**
Parameter|Type|Description|Required|Since
---|---|---|---|---
<%
for(param in doc.pathParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
<%if(isNotEmpty(doc.queryParams)){%>
**Query-parameters:**
Parameter|Type|Description|Required|Since
---|---|---|---|---
<%
for(param in doc.queryParams){
%>
${param.field}|${param.type}|${param.desc}|${param.required}|${param.version}
<%}%>
<%}%>
<%if(isNotEmpty(doc.requestParams)){%>
**Request-parameters:**
**Body-parameters:**
Parameter|Type|Description|Required|Since
---|---|---|---|---