From 8fd2154b6e53c4f8b4ee5fb0c1dab66e16359233 Mon Sep 17 00:00:00 2001 From: hanxiao Date: Wed, 8 Dec 2021 12:44:09 +0800 Subject: [PATCH 1/2] solon smartdoc adapter init --- solonhat-smartdoc/pom.xml | 23 +- .../solonhat/smartdoc/ChangeBodyFormat.java | 25 + .../noear/solonhat/smartdoc/Constants.java | 20 + .../smartdoc/SolonDocBuildTemplate.java | 629 ++++++++++++++++++ .../smartdoc/SolonHtmlApiDocBuilder.java | 215 ++++++ .../smartdoc/SolonRequestMappingHandler.java | 120 ++++ .../smartdoc/integration/XPluginImp.java | 15 - .../solon/solonhat.smartdoc.properties | 1 - 8 files changed, 1031 insertions(+), 17 deletions(-) create mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/ChangeBodyFormat.java create mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/Constants.java create mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonDocBuildTemplate.java create mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonHtmlApiDocBuilder.java create mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonRequestMappingHandler.java delete mode 100644 solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/integration/XPluginImp.java delete mode 100644 solonhat-smartdoc/src/main/resources/META-INF/solon/solonhat.smartdoc.properties diff --git a/solonhat-smartdoc/pom.xml b/solonhat-smartdoc/pom.xml index 2a0392f..46686a0 100644 --- a/solonhat-smartdoc/pom.xml +++ b/solonhat-smartdoc/pom.xml @@ -13,12 +13,33 @@ solonhat-smartdoc jar - + solon smartdoc adapter org.noear solon ${solon.ver} + + + + org.noear + solon-test + ${solon.ver} + + + + org.noear + snack3 + ${snack3.ver} + + + + + + com.github.shalousun + smart-doc + 1.9.4 + \ No newline at end of file diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/ChangeBodyFormat.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/ChangeBodyFormat.java new file mode 100644 index 0000000..73e153b --- /dev/null +++ b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/ChangeBodyFormat.java @@ -0,0 +1,25 @@ +package org.noear.solonhat.smartdoc; + +public class ChangeBodyFormat { + + public static String urlParamToJson(String p){ + if (p==null){ + return ""; + } + StringBuffer stringBuffer=new StringBuffer(); + String[] split = p.split("&"); + stringBuffer.append("{"); + boolean b=false; + for (String s : split) { + + String[] split1 = s.split("="); + stringBuffer.append("\""+split1[0]+"\":\""+(split1.length>1?split1[1]:"")+"\","); + b=true; + } + if (b) { + stringBuffer.deleteCharAt(stringBuffer.length() - 1); + } + stringBuffer.append("}"); + return stringBuffer.toString(); + } +} diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/Constants.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/Constants.java new file mode 100644 index 0000000..7568e03 --- /dev/null +++ b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/Constants.java @@ -0,0 +1,20 @@ +package org.noear.solonhat.smartdoc; + +public class Constants { + + + public static final String REQUEST_MAPPING ="Mapping"; + public static final String REQUEST_MAPPING_FULLY ="org.noear.solon.annotation.Mapping"; + + public static final String GET_MAPPING = "Get"; + public static final String GET_MAPPING_FULLY ="org.noear.solon.annotation.Get" ; + + public static final String POST_MAPPING ="Post" ; + public static final String POST_MAPPING_FULLY = "org.noear.solon.annotation.Post"; + public static final String PUT_MAPPING = "Put"; + public static final String PUT_MAPPING_FULLY = "org.noear.solon.annotation.Put"; + public static final String PATCH_MAPPING = "Patch"; + public static final String PATCH_MAPPING_FULLY = "org.noear.solon.annotation.Patch"; + public static final String DELETE_MAPPING = "Delete"; + public static final String DELETE_MAPPING_FULLY = "org.noear.solon.annotation.Delete"; +} diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonDocBuildTemplate.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonDocBuildTemplate.java new file mode 100644 index 0000000..a05a666 --- /dev/null +++ b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonDocBuildTemplate.java @@ -0,0 +1,629 @@ +package org.noear.solonhat.smartdoc; + +import com.power.common.util.*; +import com.power.doc.builder.ProjectDocConfigBuilder; +import com.power.doc.constants.*; +import com.power.doc.handler.SpringMVCRequestHeaderHandler; +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.RequestMapping; +import com.power.doc.template.IDocBuildTemplate; +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.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.DocTags.IGNORE; + + +/** + * @author yu 2019/12/21. + */ +public class SolonDocBuildTemplate implements IDocBuildTemplate { + + private List headers; + + /** + * api index + */ + private final AtomicInteger atomicInteger = new AtomicInteger(1); + + @Override + public List getApiData(ProjectDocConfigBuilder projectBuilder) { + ApiConfig apiConfig = projectBuilder.getApiConfig(); + this.headers = apiConfig.getRequestHeaders(); + List apiDocList = new ArrayList<>(); + int order = 0; + Collection classes = projectBuilder.getJavaProjectBuilder().getClasses(); + boolean setCustomOrder = false; + for (JavaClass cls : classes) { + String ignoreTag = JavaClassUtil.getClassTagsValue(cls, DocTags.IGNORE, Boolean.FALSE); + if (!checkController(cls) || StringUtil.isNotEmpty(ignoreTag)) { + continue; + } + if (StringUtil.isNotEmpty(apiConfig.getPackageFilters())) { + if (!DocUtil.isMatch(apiConfig.getPackageFilters(), cls.getCanonicalName())) { + continue; + } + } + String strOrder = JavaClassUtil.getClassTagsValue(cls, DocTags.ORDER, Boolean.TRUE); + order++; + if (ValidateUtil.isNonnegativeInteger(strOrder)) { + setCustomOrder = true; + order = Integer.parseInt(strOrder); + } + List 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 ignoreParams) { + if (JavaClassValidateUtil.isMvcIgnoreParams(typeName, ignoreParams)) { + return DocGlobalConstants.MODE_AND_VIEW_FULLY.equals(typeName); + } + return false; + } + + private List buildControllerMethod(final JavaClass cls, ApiConfig apiConfig, + ProjectDocConfigBuilder projectBuilder) { + String clazName = cls.getCanonicalName(); + boolean paramsDataToTree = projectBuilder.getApiConfig().isParamsDataToTree(); + String classAuthor = JavaClassUtil.getClassTagsValue(cls, DocTags.AUTHOR, Boolean.TRUE); + List classAnnotations = cls.getAnnotations(); + Map constantsMap = projectBuilder.getConstantsMap(); + String baseUrl = ""; + for (JavaAnnotation annotation : classAnnotations) { + String annotationName = annotation.getType().getValue(); + if (Constants.REQUEST_MAPPING.equals(annotationName) || + Constants.REQUEST_MAPPING_FULLY.equals(annotationName)) { + if (annotation.getNamedParameter("value") != null) { + baseUrl = StringUtil.removeQuotes(annotation.getNamedParameter("value").toString()); + } + } + } + List methods = cls.getMethods(); + List methodDocList = new ArrayList<>(methods.size()); + int methodOrder = 0; + for (JavaMethod method : methods) { + if (method.isPrivate()) { + 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.setOrder(methodOrder); + apiMethodDoc.setName(method.getName()); + apiMethodDoc.setDesc(method.getComment()); + 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 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); + } + if (apiConfig.isShowAuthor() && StringUtil.isEmpty(authorValue)) { + apiMethodDoc.setAuthor(classAuthor); + } + apiMethodDoc.setDetail(apiNoteValue); + //handle request mapping + RequestMapping requestMapping = new SolonRequestMappingHandler() + .handle(projectBuilder.getServerUrl(), baseUrl, method, constantsMap); + //handle headers + List apiReqHeaders = new SpringMVCRequestHeaderHandler().handle(method); + apiMethodDoc.setRequestHeaders(apiReqHeaders); + if (Objects.nonNull(requestMapping)) { + if (null != method.getTagByName(IGNORE)) { + continue; + } + apiMethodDoc.setType(requestMapping.getMethodType()); + apiMethodDoc.setUrl(requestMapping.getUrl()); + apiMethodDoc.setServerUrl(projectBuilder.getServerUrl()); + apiMethodDoc.setPath(requestMapping.getShortUrl()); + apiMethodDoc.setDeprecated(requestMapping.isDeprecated()); + // build request params + List requestParams = requestParams(method, projectBuilder); + if (paramsDataToTree) { + requestParams = ApiParamTreeUtil.apiParamToTree(requestParams); + } + apiMethodDoc.setRequestParams(requestParams); + List allApiReqHeaders; + if (this.headers != null) { + allApiReqHeaders = Stream.of(this.headers, apiReqHeaders) + .flatMap(Collection::stream).distinct().collect(Collectors.toList()); + } else { + allApiReqHeaders = apiReqHeaders; + } + //reduce create in template + apiMethodDoc.setHeaders(this.createDocRenderHeaders(allApiReqHeaders, apiConfig.isAdoc())); + apiMethodDoc.setRequestHeaders(allApiReqHeaders); + + // build request json + ApiRequestExample requestExample = buildReqJson(method, apiMethodDoc, requestMapping.getMethodType(), + projectBuilder); + String requestJson = requestExample.getExampleBody(); + // set request example detail + apiMethodDoc.setRequestExample(requestExample); + apiMethodDoc.setRequestUsage(requestJson == null ? requestExample.getUrl() : requestJson); + // build response usage + apiMethodDoc.setResponseUsage(JsonBuildHelper.buildReturnJson(method, projectBuilder)); + // build response params + List responseParams = buildReturnApiParams(method, projectBuilder); + if (paramsDataToTree) { + responseParams = ApiParamTreeUtil.apiParamToTree(responseParams); + } + apiMethodDoc.setResponseParams(responseParams); + methodDocList.add(apiMethodDoc); + } + } + return methodDocList; + } + + private ApiRequestExample buildReqJson(JavaMethod method, ApiMethodDoc apiMethodDoc, String methodType, + ProjectDocConfigBuilder configBuilder) { + List parameterList = method.getParameters(); + List reqHeaderList = apiMethodDoc.getRequestHeaders(); + + StringBuilder header = new StringBuilder(reqHeaderList.size()); + for (ApiReqHeader reqHeader : reqHeaderList) { + header.append(" -H ").append("'").append(reqHeader.getName()) + .append(":").append(reqHeader.getValue()).append("'"); + } + if (parameterList.size() < 1) { + String format = String.format(DocGlobalConstants.CURL_REQUEST_TYPE, methodType, + header.toString(), apiMethodDoc.getUrl()); + return ApiRequestExample.builder().setUrl(apiMethodDoc.getUrl()).setExampleBody(format); + } + + Map constantsMap = configBuilder.getConstantsMap(); + boolean requestFieldToUnderline = configBuilder.getApiConfig().isRequestFieldToUnderline(); + Map replacementMap = configBuilder.getReplaceClassMap(); + Map pathParamsMap = new LinkedHashMap<>(); + Map paramsComments = DocUtil.getParamsComments(method, DocTags.PARAM, null); + List springMvcRequestAnnotations = SpringMvcRequestAnnotationsEnum.listSpringMvcRequestAnnotations(); + List formDataList = new ArrayList<>(); + ApiRequestExample requestExample = ApiRequestExample.builder(); + out: + for (JavaParameter parameter : parameterList) { + JavaType javaType = parameter.getType(); + String paramName = parameter.getName(); + 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().toLowerCase(); + typeName = DocClassUtil.rewriteRequestParam(typeName); + gicTypeName = DocClassUtil.rewriteRequestParam(gicTypeName); + JavaClass javaClass = configBuilder.getJavaProjectBuilder().getClassByName(typeName); + String[] globGicName = DocClassUtil.getSimpleGicName(gicTypeName); + String comment = this.paramCommentResolve(paramsComments.get(paramName)); + String mockValue = ""; + if ("POST".equals(methodType) ||"PUT".equals(methodType)) { + apiMethodDoc.setContentType(JSON_CONTENT_TYPE); + } + 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); + } + if ("POST".equals(methodType) ||"PUT".equals(methodType)){ + apiMethodDoc.setContentType(JSON_CONTENT_TYPE); + StringBuilder builder ; + if (requestExample.getJsonBody()==null){ + builder= new StringBuilder(); + builder.append("{"); + }else{ + builder=new StringBuilder(requestExample.getJsonBody()); + builder.delete(builder.length()-1,builder.length()); + builder.append(","); + } + + builder.append("\"") + .append(paramName) + .append("\":") + .append(DocUtil.handleJsonStr(mockValue)) + .append("}"); + requestExample.setJsonBody(JsonFormatUtil.formatJson(builder.toString())).setJson(true); + + } + } + if (requestFieldToUnderline) { + paramName = StringUtil.camelToUnderline(paramName); + } + List annotations = parameter.getAnnotations(); + boolean paramAdded = false; + for (JavaAnnotation annotation : annotations) { + String annotationName = annotation.getType().getValue(); + String fullName = annotation.getType().getSimpleName(); + if (!springMvcRequestAnnotations.contains(fullName) || paramAdded) { + continue; + } + if (SpringMvcAnnotations.REQUEST_HERDER.equals(annotationName)) { + continue out; + } + AnnotationValue annotationDefaultVal = annotation.getProperty(DocAnnotationConstants.DEFAULT_VALUE_PROP); + if (null != annotationDefaultVal) { + mockValue = StringUtil.removeQuotes(annotationDefaultVal.toString()); + } + paramName = getParamName(paramName, annotation); + for (Map.Entry 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(entry.getKey())) { + mockValue = mockValue.replace(key, value); + } + } + if (Constants.POST_MAPPING.equals(annotationName) ||Constants.POST_MAPPING.equals(annotationName)) { + apiMethodDoc.setContentType(JSON_CONTENT_TYPE); + 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); + } else { + String json = JsonBuildHelper.buildJson(typeName, gicTypeName, Boolean.FALSE, 0, new HashMap<>(), configBuilder); + requestExample.setJsonBody(JsonFormatUtil.formatJson(json)).setJson(true); + } + paramAdded = true; + } else if (Constants.GET_MAPPING.contains(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 (gicTypeName.contains(DocGlobalConstants.MULTIPART_FILE_FULLY)) { + apiMethodDoc.setContentType(FILE_CONTENT_TYPE); + FormData formData = new FormData(); + formData.setKey(paramName); + formData.setType("file"); + formData.setDesc(comment); + formData.setValue(mockValue); + formDataList.add(formData); + } else if (JavaClassValidateUtil.isPrimitive(typeName)) { + FormData formData = new FormData(); + formData.setKey(paramName); + formData.setDesc(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)) { + throw new RuntimeException("Spring MVC 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.setDesc(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.setDesc(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); + String format = String.format(DocGlobalConstants.CURL_REQUEST_TYPE, methodType, header.toString(), url); + format=format.replace("-X ANY",""); + if (requestExample.isJson()) { + if (StringUtil.isNotEmpty(requestExample.getJsonBody())) { + exampleBody = String.format(DocGlobalConstants.CURL_POST_PUT_JSON, methodType, header.toString(), url, + requestExample.getJsonBody()); + } else { + exampleBody = format; + } + } else { + if (StringUtil.isNotEmpty(body)) { +// exampleBody = String.format(DocGlobalConstants.CURL_REQUEST_TYPE_DATA, methodType, header.toString(), url, body); + exampleBody = String.format(DocGlobalConstants.CURL_POST_PUT_JSON, methodType, header.toString(), url, + ChangeBodyFormat.urlParamToJson(body)); +// exampleBody+="\r\n OR \r\n"+exampleBody2; + } else { + exampleBody = format; + } + } + 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); + exampleBody = String.format(DocGlobalConstants.CURL_REQUEST_TYPE, methodType, header.toString(), url); + exampleBody=exampleBody.replace("-X ANY",""); + requestExample.setExampleBody(exampleBody) + .setJsonBody(DocGlobalConstants.EMPTY) + .setUrl(url); + } + return requestExample; + } + + private List requestParams(final JavaMethod javaMethod, ProjectDocConfigBuilder builder) { + boolean isStrict = builder.getApiConfig().isStrict(); + Map responseFieldMap = new HashMap<>(); + String className = javaMethod.getDeclaringClass().getCanonicalName(); + Map replacementMap = builder.getReplaceClassMap(); + Map paramTagMap = DocUtil.getParamsComments(javaMethod, DocTags.PARAM, className); + List parameterList = javaMethod.getParameters(); + if (parameterList.size() < 1) { + return null; + } + Map constantsMap = builder.getConstantsMap(); + boolean requestFieldToUnderline = builder.getApiConfig().isRequestFieldToUnderline(); + List paramList = new ArrayList<>(); + int requestBodyCounter = 0; + out: + for (JavaParameter parameter : parameterList) { + String paramName = parameter.getName(); + String typeName = parameter.getType().getGenericCanonicalName(); + String simpleName = parameter.getType().getValue().toLowerCase(); + String fullTypeName = parameter.getType().getFullyQualifiedName(); + + 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 @param 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 (typeName.contains(DocGlobalConstants.MULTIPART_FILE_FULLY)) { + ApiParam param = ApiParam.of().setField(paramName).setType("file") + .setId(paramList.size() + 1) + .setDesc(comment).setRequired(true).setVersion(DocGlobalConstants.DEFAULT_VERSION); + paramList.add(param); + continue; + } + JavaClass javaClass = builder.getJavaProjectBuilder().getClassByName(fullTypeName); + List annotations = parameter.getAnnotations(); + List groupClasses = JavaClassUtil.getParamGroupJavaClass(annotations); + String strRequired = "true"; + boolean isPathVariable = false; + for (JavaAnnotation annotation : annotations) { + String annotationName = annotation.getType().getValue(); + if (SpringMvcAnnotations.REQUEST_HERDER.equals(annotationName)) { + continue out; + } + if (SpringMvcAnnotations.REQUEST_PARAM.equals(annotationName) || + DocAnnotationConstants.SHORT_PATH_VARIABLE.equals(annotationName)) { + if (DocAnnotationConstants.SHORT_PATH_VARIABLE.equals(annotationName)) { + isPathVariable = true; + } + paramName = getParamName(paramName, annotation); + for (Map.Entry entry : constantsMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (paramName.contains(key)) { + paramName = paramName.replace(key, value); + } + } + + AnnotationValue annotationRequired = annotation.getProperty(DocAnnotationConstants.REQUIRED_PROP); + if (null != annotationRequired) { + strRequired = annotationRequired.toString(); + } + } + if (SpringMvcAnnotations.REQUEST_BODY.equals(annotationName)) { + if (requestBodyCounter > 0) { + throw new RuntimeException("You have use @RequestBody Passing multiple variables for method " + + javaMethod.getName() + " in " + className + ",@RequestBody annotation could only bind one variables."); + } + requestBodyCounter++; + } + } + boolean required = Boolean.parseBoolean(strRequired); + if (isPathVariable) { + comment = comment + " (This is path param)"; + } + if (JavaClassValidateUtil.isCollection(fullTypeName) || JavaClassValidateUtil.isArray(fullTypeName)) { + String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName); + String gicName = gicNameArr[0]; + if (JavaClassValidateUtil.isArray(gicName)) { + gicName = gicName.substring(0, gicName.indexOf("[")); + } + if (JavaClassValidateUtil.isPrimitive(gicName)) { + String shortSimple = DocClassUtil.processTypeNameForParams(gicName); + ApiParam param = ApiParam.of().setField(paramName).setDesc(comment + ",[array of " + shortSimple + "]") + .setRequired(required) + .setPathParams(isPathVariable) + .setId(paramList.size() + 1) + .setType("array"); + paramList.add(param); + } else { + if (requestBodyCounter > 0) { + //for json + paramList.addAll(ParamsBuildHelper.buildParams(gicNameArr[0], DocGlobalConstants.EMPTY, 0, + "true", responseFieldMap, Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0)); + } else { + throw new RuntimeException("Spring MVC 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) + .setPathParams(isPathVariable) + .setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION); + paramList.add(param); + } else if (JavaClassValidateUtil.isMap(fullTypeName)) { + if (DocGlobalConstants.JAVA_MAP_FULLY.equals(typeName)) { + ApiParam apiParam = ApiParam.of().setField(paramName).setType("map") + .setId(paramList.size() + 1) + .setPathParams(isPathVariable) + .setDesc(comment).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION); + paramList.add(apiParam); + continue; + } + String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName); + 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()) { + + String o = JavaClassUtil.getEnumParams(javaClass); + ApiParam param = ApiParam.of().setField(paramName) + .setId(paramList.size() + 1) + .setPathParams(isPathVariable) + .setType("enum").setDesc(StringUtil.removeQuotes(o)).setRequired(required).setVersion(DocGlobalConstants.DEFAULT_VERSION); + paramList.add(param); + } else { + paramList.addAll(ParamsBuildHelper.buildParams(typeName, DocGlobalConstants.EMPTY, 0, "true", responseFieldMap, Boolean.FALSE, new HashMap<>(), builder, groupClasses, 0)); + } + } + return paramList; + } + + private String getParamName(String paramName, JavaAnnotation annotation) { + AnnotationValue annotationValue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP); + if (null != annotationValue) { + paramName = StringUtil.removeQuotes(annotationValue.toString()); + } + AnnotationValue annotationOfName = annotation.getProperty(DocAnnotationConstants.NAME_PROP); + if (null != annotationOfName) { + paramName = StringUtil.removeQuotes(annotationOfName.toString()); + } + return paramName; + } + + private boolean checkController(JavaClass cls) { + List classAnnotations = cls.getAnnotations(); + for (JavaAnnotation annotation : classAnnotations) { + String name = annotation.getType().getValue(); + if (SpringMvcAnnotations.CONTROLLER.equals(name) || SpringMvcAnnotations.REST_CONTROLLER.equals(name)) { + return true; + } + } + // use custom doc tag to support Feign. + List docletTags = cls.getTags(); + for (DocletTag docletTag : docletTags) { + String value = docletTag.getName(); + if (DocTags.REST_API.equals(value)) { + return true; + } + } + return false; + } + + private String getRewriteClassName(Map 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]; + } else { + rewriteClassName = replacementMap.get(fullTypeName); + } + return rewriteClassName; + } +} diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonHtmlApiDocBuilder.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonHtmlApiDocBuilder.java new file mode 100644 index 0000000..a9319af --- /dev/null +++ b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonHtmlApiDocBuilder.java @@ -0,0 +1,215 @@ +/* + * smart-doc https://github.com/shalousun/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 org.noear.solonhat.smartdoc; + +import com.power.common.util.CollectionUtil; +import com.power.common.util.DateTimeUtil; +import com.power.common.util.FileUtil; +import com.power.doc.builder.DocBuilderTemplate; +import com.power.doc.builder.ProjectDocConfigBuilder; +import com.power.doc.constants.DocGlobalConstants; +import com.power.doc.constants.DocLanguage; +import com.power.doc.constants.TemplateVariable; +import com.power.doc.model.ApiConfig; +import com.power.doc.model.ApiDoc; +import com.power.doc.model.ApiDocDict; +import com.power.doc.model.ApiErrorCode; +import com.power.doc.template.IDocBuildTemplate; +import com.power.doc.utils.BeetlTemplateUtil; +import com.power.doc.utils.MarkDownUtil; +import com.thoughtworks.qdox.JavaProjectBuilder; +import org.apache.commons.lang3.StringUtils; +import org.beetl.core.Template; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.power.doc.constants.DocGlobalConstants.*; + +/** + * @author yu 2019/9/20. + * @since 1.7+ + */ +public class SolonHtmlApiDocBuilder { + + private static long now = System.currentTimeMillis(); + + private static final String STR_TIME = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND); + + private static String INDEX_HTML = "index.html"; + + + /** + * build controller api + * + * @param config config + */ + public static void buildApiDoc(ApiConfig config) { + JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder(); + buildApiDoc(config, javaProjectBuilder); + } + + /** + * Only for smart-doc maven plugin and gradle plugin. + * + * @param config ApiConfig + * @param javaProjectBuilder ProjectDocConfigBuilder + */ + public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) { + DocBuilderTemplate builderTemplate = new DocBuilderTemplate(); + builderTemplate.checkAndInit(config); + config.setParamsDataToTree(false); + ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder); + IDocBuildTemplate docBuildTemplate = new SolonDocBuildTemplate(); + List apiDocList = docBuildTemplate.getApiData(configBuilder); + if (config.isAllInOne()) { + Template indexCssTemplate = BeetlTemplateUtil.getByName(ALL_IN_ONE_CSS); + FileUtil.nioWriteFile(indexCssTemplate.render(), config.getOutPath() + FILE_SEPARATOR + ALL_IN_ONE_CSS); + if (StringUtils.isNotEmpty(config.getAllInOneDocFileName())) { + INDEX_HTML = config.getAllInOneDocFileName(); + } + builderTemplate.buildAllInOne(apiDocList, config, javaProjectBuilder, ALL_IN_ONE_HTML_TPL, INDEX_HTML); + } else { + List apiDocDictList = builderTemplate.buildDictionary(config, javaProjectBuilder); + buildIndex(apiDocList, config); + copyCss(config.getOutPath()); + buildDoc(apiDocList, config); + buildErrorCodeDoc(config.getErrorCodes(), config.getOutPath()); + buildDictionary(apiDocDictList, config.getOutPath()); + } + } + + private static void copyCss(String outPath) { + Template indexCssTemplate = BeetlTemplateUtil.getByName(INDEX_CSS_TPL); + Template mdCssTemplate = BeetlTemplateUtil.getByName(MARKDOWN_CSS_TPL); + FileUtil.nioWriteFile(indexCssTemplate.render(), outPath + FILE_SEPARATOR + INDEX_CSS_TPL); + FileUtil.nioWriteFile(mdCssTemplate.render(), outPath + FILE_SEPARATOR + MARKDOWN_CSS_TPL); + } + + /** + * build api.html + * + * @param apiDocList list of api doc + * @param config ApiConfig + */ + private static void buildIndex(List apiDocList, ApiConfig config) { + FileUtil.mkdirs(config.getOutPath()); + Template indexTemplate = BeetlTemplateUtil.getByName(INDEX_TPL); + if (CollectionUtil.isEmpty(apiDocList)) { + return; + } + ApiDoc doc = apiDocList.get(0); + String homePage = doc.getAlias(); + indexTemplate.binding(TemplateVariable.HOME_PAGE.getVariable(), homePage); + indexTemplate.binding(TemplateVariable.VERSION.getVariable(), now); + indexTemplate.binding(TemplateVariable.API_DOC_LIST.getVariable(), apiDocList); + indexTemplate.binding(TemplateVariable.ERROR_CODE_LIST.getVariable(), config.getErrorCodes()); + indexTemplate.binding(TemplateVariable.DICT_LIST.getVariable(), config.getDataDictionaries()); + if (CollectionUtil.isEmpty(config.getErrorCodes())) { + indexTemplate.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 1); + } else { + indexTemplate.binding(TemplateVariable.DICT_ORDER.getVariable(), apiDocList.size() + 2); + } + if (null != config.getLanguage()) { + if (DocLanguage.CHINESE.code.equals(config.getLanguage().getCode())) { + indexTemplate.binding(TemplateVariable.ERROR_LIST_TITLE.getVariable(), ERROR_CODE_LIST_CN_TITLE); + indexTemplate.binding(TemplateVariable.DICT_LIST_TITLE.getVariable(), DocGlobalConstants.DICT_CN_TITLE); + } else { + indexTemplate.binding(TemplateVariable.ERROR_LIST_TITLE.getVariable(), ERROR_CODE_LIST_EN_TITLE); + indexTemplate.binding(TemplateVariable.DICT_LIST_TITLE.getVariable(), DocGlobalConstants.DICT_EN_TITLE); + } + } else { + indexTemplate.binding(TemplateVariable.ERROR_LIST_TITLE.getVariable(), ERROR_CODE_LIST_CN_TITLE); + indexTemplate.binding(TemplateVariable.DICT_LIST_TITLE.getVariable(), DocGlobalConstants.DICT_CN_TITLE); + } + FileUtil.nioWriteFile(indexTemplate.render(), config.getOutPath() + FILE_SEPARATOR + "api.html"); + } + + /** + * build ever controller api + * + * @param apiDocList list of api doc + * @param config ApiConfig + */ + private static void buildDoc(List apiDocList, ApiConfig config) { + FileUtil.mkdirs(config.getOutPath()); + Template htmlApiDoc; + for (ApiDoc doc : apiDocList) { + Template apiTemplate = BeetlTemplateUtil.getByName(API_DOC_MD_TPL); + apiTemplate.binding(TemplateVariable.REQUEST_EXAMPLE.getVariable(), config.isRequestExample()); + apiTemplate.binding(TemplateVariable.RESPONSE_EXAMPLE.getVariable(), config.isResponseExample()); + apiTemplate.binding(TemplateVariable.DESC.getVariable(), doc.getDesc()); + apiTemplate.binding(TemplateVariable.NAME.getVariable(), doc.getName()); + apiTemplate.binding(TemplateVariable.LIST.getVariable(), doc.getList());//类名 + Map templateVariables = new HashMap<>(); + templateVariables.put(TemplateVariable.TITLE.getVariable(), doc.getDesc()); + htmlApiDoc = initTemplate(apiTemplate, HTML_API_DOC_TPL, templateVariables); + FileUtil.nioWriteFile(htmlApiDoc.render(), config.getOutPath() + FILE_SEPARATOR + doc.getAlias() + ".html"); + } + } + + /** + * build error_code html + * + * @param errorCodeList list of error code + * @param outPath + */ + private static void buildErrorCodeDoc(List errorCodeList, String outPath) { + if (CollectionUtil.isNotEmpty(errorCodeList)) { + Template errorTemplate = BeetlTemplateUtil.getByName(ERROR_CODE_LIST_MD_TPL); + errorTemplate.binding(TemplateVariable.LIST.getVariable(), errorCodeList); + Map templateVariables = new HashMap<>(); + templateVariables.put(TemplateVariable.TITLE.getVariable(), ERROR_CODE_LIST_EN_TITLE); + Template errorCodeDoc = initTemplate(errorTemplate, HTML_API_DOC_TPL, templateVariables); + FileUtil.nioWriteFile(errorCodeDoc.render(), outPath + FILE_SEPARATOR + "error_code.html"); + } + } + + /** + * build dictionary + * + * @param apiDocDictList dictionary list + * @param outPath + */ + private static void buildDictionary(List apiDocDictList, String outPath) { + if (CollectionUtil.isNotEmpty(apiDocDictList)) { + Template template = BeetlTemplateUtil.getByName(DICT_LIST_MD_TPL); + template.binding(TemplateVariable.DICT_LIST.getVariable(), apiDocDictList); + Map templateVariables = new HashMap<>(); + templateVariables.put(TemplateVariable.TITLE.getVariable(), DICT_EN_TITLE); + Template dictTpl = initTemplate(template, HTML_API_DOC_TPL, templateVariables); + FileUtil.nioWriteFile(dictTpl.render(), outPath + FILE_SEPARATOR + "dict.html"); + } + } + + private static Template initTemplate(Template template, String templateName, Map templateVariables) { + String errorHtml = MarkDownUtil.toHtml(template.render()); + Template template1 = BeetlTemplateUtil.getByName(templateName); + template1.binding(TemplateVariable.VERSION.getVariable(), now); + template1.binding(TemplateVariable.HTML.getVariable(), errorHtml); + template1.binding(TemplateVariable.CREATE_TIME.getVariable(), STR_TIME); + template1.binding(templateVariables); + return template1; + } +} diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonRequestMappingHandler.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonRequestMappingHandler.java new file mode 100644 index 0000000..dbd31b0 --- /dev/null +++ b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/SolonRequestMappingHandler.java @@ -0,0 +1,120 @@ +/* + * smart-doc https://github.com/shalousun/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 org.noear.solonhat.smartdoc; + +import com.power.common.util.StringUtil; +import com.power.common.util.UrlUtil; +import com.power.doc.constants.DocAnnotationConstants; +import com.power.doc.constants.Methods; +import com.power.doc.model.request.RequestMapping; +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.Arrays; +import java.util.List; +import java.util.Map; + +import static com.power.doc.constants.DocTags.IGNORE; + +/** + * @author 馒头虫 2021/11/16. + */ +public class SolonRequestMappingHandler { + + /** + * handle spring request mapping + * + * @param serverUrl server url + * @param controllerBaseUrl spring mvc controller base url + * @param method JavaMethod + * @param constantsMap project constant container + * @return RequestMapping + */ + public RequestMapping handle(String serverUrl, String controllerBaseUrl, JavaMethod method, Map constantsMap) { + List annotations = method.getAnnotations(); + String url; + String methodType = "ANY"; + String shortUrl = null; + String mediaType = null; + + boolean deprecated = false; + for (JavaAnnotation annotation : annotations) { + String annotationName = annotation.getType().getName(); + Object produces = annotation.getNamedParameter("produces"); + if (produces != null) { + mediaType = produces.toString(); + } + if (DocAnnotationConstants.DEPRECATED.equals(annotationName)) { + deprecated = true; + } + if (Constants.REQUEST_MAPPING.equals(annotationName) || Constants.REQUEST_MAPPING_FULLY.equals(annotationName)) { + shortUrl = DocUtil.handleMappingValue(annotation); + + } + if (Constants.GET_MAPPING.equals(annotationName) || Constants.GET_MAPPING_FULLY.equals(annotationName)) { + methodType = Methods.GET.getValue(); + } else if (Constants.POST_MAPPING.equals(annotationName) || Constants.POST_MAPPING_FULLY.equals(annotationName)) { + methodType = Methods.POST.getValue(); + mediaType="application/json;charset=UTF-8"; + } else if (Constants.PUT_MAPPING.equals(annotationName) || Constants.PUT_MAPPING_FULLY.equals(annotationName)) { + mediaType="application/json;charset=UTF-8"; + methodType = Methods.PUT.getValue(); + } else if (Constants.PATCH_MAPPING.equals(annotationName) || Constants.PATCH_MAPPING_FULLY.equals(annotationName)) { + methodType = Methods.PATCH.getValue(); + } else if (Constants.DELETE_MAPPING.equals(annotationName) || Constants.DELETE_MAPPING_FULLY.equals(annotationName)) { + methodType = Methods.DELETE.getValue(); + } + } + if (shortUrl != null) { + if (null != method.getTagByName(IGNORE)) { + 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)); + } else { + url = UrlUtil.simplifyUrl(serverUrl + "/" + controllerBaseUrl + "/" + shortUrl); + shortUrl = UrlUtil.simplifyUrl("/" + controllerBaseUrl + "/" + shortUrl); + } + for (Map.Entry 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("+", ""); + } + } + return RequestMapping.builder().setMediaType(mediaType).setMethodType(methodType) + .setUrl(StringUtil.trim(url)).setShortUrl(StringUtil.trim(shortUrl)).setDeprecated(deprecated); + } + return null; + } +} diff --git a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/integration/XPluginImp.java b/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/integration/XPluginImp.java deleted file mode 100644 index e04e786..0000000 --- a/solonhat-smartdoc/src/main/java/org/noear/solonhat/smartdoc/integration/XPluginImp.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.noear.solonhat.smartdoc.integration; - - -import org.noear.solon.SolonApp; -import org.noear.solon.core.Plugin; - -/** - * @author noear 2021/11/18 created - */ -public class XPluginImp implements Plugin { - @Override - public void start(SolonApp app) { - - } -} diff --git a/solonhat-smartdoc/src/main/resources/META-INF/solon/solonhat.smartdoc.properties b/solonhat-smartdoc/src/main/resources/META-INF/solon/solonhat.smartdoc.properties deleted file mode 100644 index 481ddf3..0000000 --- a/solonhat-smartdoc/src/main/resources/META-INF/solon/solonhat.smartdoc.properties +++ /dev/null @@ -1 +0,0 @@ -solon.plugin=org.noear.solonhat.swagger2.XPluginImp \ No newline at end of file -- Gitee From f461bcfeee253befd6959b9195ae62b7b3711810 Mon Sep 17 00:00:00 2001 From: hanxiao Date: Wed, 8 Dec 2021 14:04:00 +0800 Subject: [PATCH 2/2] add readme --- solonhat-smartdoc/src/readme.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 solonhat-smartdoc/src/readme.md diff --git a/solonhat-smartdoc/src/readme.md b/solonhat-smartdoc/src/readme.md new file mode 100644 index 0000000..c665001 --- /dev/null +++ b/solonhat-smartdoc/src/readme.md @@ -0,0 +1,18 @@ +# solonhat smartdoc 使用说明 +- smartdoc是一个无代码侵入的文档生成工具,生成的文档是静态html/md 对项目运行无任何不良影响 +- 目前版本暂只支持html生成 +## 引用 + 引用时,scope可设置为test,package时不用打包进去 +## 使用 + copy Doc.java到你的项目中的test代码目录,运行 的Doc.testBuilderControllersApi + +## 输出 + 生成html及css文件 +## 配置 +- config.setServerUrl("http://localhost:8089") 设置服务URL,用于生成请求示例 +- config.setOutPath(DocGlobalConstants.HTML_DOC_OUT_PATH); 设置输出路径,默认src/main/resources/static/doc + + +## 示例 + https://gitee.com/mantouchong/solonhat-smartdoc-test + \ No newline at end of file -- Gitee