-
Notifications
You must be signed in to change notification settings - Fork 32
3. Support local groovy extension
tangcent edited this page Aug 10, 2019
·
1 revision
-
Add groovy file at $project/.easyapi/ext
-
Named ${ActionName}Ext.groovy
-
Supported actions:
- ApiCallAction
- ApiDashBoardAction
- FieldsToJsonAction
- MarkdownExportAction
- PostmanExportAction
-
Inject custom class by binder in
init
method
demo: https://github.com/tangcent/spring-demo/blob/master/.easyapi/ext/MarkdownExportActionExt.groovy
import com.intellij.psi.*
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.util.PsiTypesUtil
import com.itangcent.common.constant.HttpMethod
import com.itangcent.common.exporter.ClassExporter
import com.itangcent.common.exporter.RequestHelper
import com.itangcent.common.exporter.RequestHelperKt
import com.itangcent.common.model.Header
import com.itangcent.common.model.Request
import com.itangcent.idea.plugin.api.export.AbstractClassExporter
import com.itangcent.idea.plugin.api.export.ClassExportRuleKeys
import com.itangcent.idea.plugin.script.ActionExt
import com.itangcent.idea.plugin.utils.KtHelper
import com.itangcent.idea.plugin.utils.SpringClassName
import com.itangcent.intellij.context.ActionContext
import com.itangcent.intellij.psi.ClassRuleKeys
import com.itangcent.intellij.psi.PsiAnnotationUtils
import com.itangcent.intellij.util.KV
import org.apache.commons.lang3.StringUtils
class MarkdownExportActionExt implements ActionExt {
void init(ActionContext.ActionContextBuilder builder) {
builder.bindInstance("file.save.default", "custom.ext.easy-api.md") \
builder.bind(ClassExporter.class, KtHelper.INSTANCE.ktFunction({
it.to(CustomClassExporter.class).in(com.google.inject.Singleton.class)
return null
}))
}
static class CustomClassExporter extends AbstractClassExporter {
void processClass(PsiClass cls, KV<String, Object> kv) {
logger.info("process class by ext:" + cls.name)
PsiAnnotation ctrlRequestMappingAnn = findRequestMapping(cls)
String basePath = findHttpPath(ctrlRequestMappingAnn) ?: ""
String ctrlHttpMethod = findHttpMethod(ctrlRequestMappingAnn)
kv.put("basePath", basePath)
kv.put("ctrlHttpMethod", ctrlHttpMethod)
}
protected boolean hasApi(PsiClass psiClass) {
return psiClass.annotations.any {
SpringClassName.SPRING_CONTROLLER_ANNOTATION.contains(it.qualifiedName)
}
}
boolean isApi(PsiMethod psiMethod) {
return findRequestMappingInAnn(psiMethod) != null
}
void processMethodParameter(PsiMethod method, Request request, PsiParameter param, String paramDesc, RequestHelper requestHelper) {
PsiAnnotation requestBodyAnn = findRequestBody(param)
if (requestBodyAnn != null) {
if (request.method == HttpMethod.NO_METHOD) {
requestHelper.setMethod(request, HttpMethod.POST)
}
RequestHelperKt.addHeader(requestHelper, request, "Content-Type", "application/json")
requestHelper.setJsonBody(
request,
parseRequestBody(param.type, method),
paramDesc
)
return
}
PsiAnnotation modelAttrAnn = findModelAttr(param)
if (modelAttrAnn != null) {
if (request.method == HttpMethod.GET) {
addParamAsQuery(param, request, requestHelper)
} else {
if (request.method == HttpMethod.NO_METHOD) {
requestHelper.setMethod(request, HttpMethod.POST)
}
addParamAsForm(param, request, requestHelper, null)
}
return
}
PsiAnnotation requestHeaderAnn = findRequestHeader(param)
if (requestHeaderAnn != null) {
String headName = PsiAnnotationUtils.INSTANCE.findAttr(requestHeaderAnn,
"value")
if (StringUtils.isBlank(headName)) {
headName = PsiAnnotationUtils.INSTANCE.findAttr(requestHeaderAnn,
"name")
}
if (StringUtils.isBlank(headName)) {
headName = param.name
}
boolean required = findParamRequired(requestHeaderAnn) ?: true
if (!required && ruleComputer.computer(ClassExportRuleKeys.INSTANCE.PARAM_REQUIRED, param) == true) {
required = true
}
String defaultValue = PsiAnnotationUtils.INSTANCE.findAttr(requestHeaderAnn,
"defaultValue")
if (defaultValue == null
|| defaultValue == SpringClassName.ESCAPE_REQUEST_HEADER_DEFAULT_NONE
|| defaultValue == SpringClassName.REQUEST_HEADER_DEFAULT_NONE) {
defaultValue = ""
}
Header header = new Header()
header.name = headName
header.value = defaultValue
header.example = defaultValue
header.desc = paramDesc
header.required = required
requestHelper.addHeader(request, header)
return
}
PsiAnnotation pathVariableAnn = findPathVariable(param)
if (pathVariableAnn != null) {
String pathName = PsiAnnotationUtils.INSTANCE.findAttr(pathVariableAnn,
"value")
if (pathName == null) {
pathName = param.name
}
RequestHelperKt.addPathParam(requestHelper, request, pathName, paramDesc ?: "")
return
}
String paramName = null
Boolean required = false
Object defaultVal = null
PsiAnnotation requestParamAnn = findRequestParam(param)
if (requestParamAnn != null) {
paramName = findParamName(requestParamAnn)
required = findParamRequired(requestParamAnn) ?: true
defaultVal = PsiAnnotationUtils.INSTANCE.findAttr(requestParamAnn,
"defaultValue")
if (defaultVal == null
|| defaultVal == SpringClassName.ESCAPE_REQUEST_HEADER_DEFAULT_NONE
|| defaultVal == SpringClassName.REQUEST_HEADER_DEFAULT_NONE) {
defaultVal = ""
}
}
if (!required && ruleComputer.computer(ClassExportRuleKeys.INSTANCE.PARAM_REQUIRED, param) == true) {
required = true
}
if (StringUtils.isBlank(paramName)) {
paramName = param.name
}
PsiType paramType = param.type
PsiType unboxType = psiClassHelper.unboxArrayOrList(paramType)
PsiClass paramCls = PsiTypesUtil.getPsiClass(unboxType)
if (unboxType instanceof PsiPrimitiveType) { //primitive Type
if (defaultVal == null || defaultVal == "") {
defaultVal = PsiTypesUtil.getDefaultValue(unboxType)
//Primitive type parameter is required
//Optional primitive type parameter is present but cannot be translated into a null value due to being declared as a primitive type.
//Consider declaring it as object wrapper for the corresponding primitive type.
required = true
}
} else if (psiClassHelper.isNormalType(unboxType.canonicalText)) {//normal type
if (defaultVal == null || defaultVal == "") {
defaultVal = psiClassHelper.getDefaultValue(unboxType.canonicalText)
}
} else if (paramCls != null && ruleComputer.computer(ClassRuleKeys.INSTANCE.TYPE_IS_FILE, paramCls) == true) {
if (request.method == HttpMethod.GET) {
//can not upload file in a GET method
logger.error("Couldn't upload file in 'GET':[$request.method:${request.path}],param:${param.name} type:{${paramType.canonicalText}}")
return
}
if (request.method == HttpMethod.NO_METHOD) {
request.method = HttpMethod.POST
}
RequestHelperKt.addHeader(requestHelper, request, "Content-Type", "multipart/form-data")
RequestHelperKt.addFormFileParam(requestHelper, request, paramName, required, paramDesc)
return
} else if (SpringClassName.SPRING_REQUEST_RESPONSE.contains(unboxType.presentableText)) {
//ignore @HttpServletRequest and @HttpServletResponse
return
}
if (defaultVal != null) {
RequestHelperKt.addParam(requestHelper, request,
paramName
, defaultVal.toString()
, required
, paramDesc)
} else {
if (request.method == HttpMethod.GET) {
addParamAsQuery(param, request, requestHelper, paramDesc)
} else {
if (request.method == HttpMethod.NO_METHOD) {
request.method = HttpMethod.POST
}
addParamAsForm(param, request, requestHelper, paramDesc)
}
}
}
void processMethod(PsiMethod method, KV<String, Object> kv, Request request, RequestHelper requestHelper) {
super.processMethod(method, kv, request, requestHelper)
String basePath = kv.getAs("basePath")
String ctrlHttpMethod = kv.getAs("ctrlHttpMethod")
PsiAnnotation requestMapping = findRequestMappingInAnn(method)
String httpMethod = findHttpMethod(requestMapping)
if (httpMethod == HttpMethod.NO_METHOD && ctrlHttpMethod != HttpMethod.NO_METHOD) {
httpMethod = ctrlHttpMethod
}
request.method = httpMethod
String httpPath = contractPath(basePath, findHttpPath(requestMapping))
requestHelper.setPath(request, httpPath)
}
private final String findHttpPath(PsiAnnotation requestMappingAnn) {
String path = PsiAnnotationUtils.INSTANCE.findAttr((PsiAnnotation) requestMappingAnn, ["path", "value"] as String[])
if (path != null) {
return StringUtils.substringBefore(path, ",")
} else {
return null
}
}
private final String findHttpMethod(PsiAnnotation requestMappingAnn) {
if (requestMappingAnn != null) {
String qualifiedName = requestMappingAnn.getQualifiedName()
if (qualifiedName == "org.springframework.web.bind.annotation.RequestMapping") {
String method = PsiAnnotationUtils.INSTANCE.findAttr(requestMappingAnn, ["method"] as String[])
if (method != null) {
if (method.contains(",")) {
method = StringUtils.substringBefore(method, ",")
}
if (StringUtils.isBlank(method)) {
return "ALL"
}
if (method.startsWith("RequestMethod.")) {
return StringUtils.removeStart(method, "RequestMethod.")
}
if (method.contains("RequestMethod.")) {
return StringUtils.substringAfterLast(method, "RequestMethod.")
}
return method
}
return "ALL";
}
if (qualifiedName == "org.springframework.web.bind.annotation.GetMapping") {
return "GET"
}
if (qualifiedName == "org.springframework.web.bind.annotation.PostMapping") {
return "POST"
}
if (qualifiedName == "org.springframework.web.bind.annotation.DeleteMapping") {
return "DELETE"
}
if (qualifiedName == "org.springframework.web.bind.annotation.PatchMapping") {
return "PATCH"
}
if (qualifiedName == "org.springframework.web.bind.annotation.PutMapping") {
return "PUT"
}
}
return "ALL"
}
private final PsiAnnotation findRequestMapping(PsiClass psiClass) {
PsiAnnotation requestMappingAnn = this.findRequestMappingInAnn(psiClass)
if (requestMappingAnn != null) {
return requestMappingAnn
} else {
for (PsiClass superCls = psiClass.getSuperClass(); superCls != null; superCls = superCls.getSuperClass()) {
PsiAnnotation requestMappingAnnInSuper = this.findRequestMappingInAnn(superCls)
if (requestMappingAnnInSuper != null) {
return requestMappingAnnInSuper
}
}
return null
}
}
private final PsiAnnotation findRequestMappingInAnn(PsiModifierListOwner ele) {
for (String ann in SpringClassName.SPRING_REQUEST_MAPPING_ANNOTATIONS) {
PsiAnnotation psiAnnotation = PsiAnnotationUtils.INSTANCE.findAnn(ele, ann)
if (psiAnnotation != null) {
return psiAnnotation
}
}
return null
}
private final PsiAnnotation findRequestBody(PsiParameter parameter) {
return PsiAnnotationUtils.INSTANCE.findAnn(parameter, "org.springframework.web.bind.annotation.RequestBody")
}
private final PsiAnnotation findModelAttr(PsiParameter parameter) {
return PsiAnnotationUtils.INSTANCE.findAnn(parameter, "org.springframework.web.bind.annotation.ModelAttribute")
}
private final PsiAnnotation findRequestHeader(PsiParameter parameter) {
return PsiAnnotationUtils.INSTANCE.findAnn(parameter, "org.springframework.web.bind.annotation.RequestHeader")
}
private final PsiAnnotation findPathVariable(PsiParameter parameter) {
return PsiAnnotationUtils.INSTANCE.findAnn(parameter, "org.springframework.web.bind.annotation.PathVariable")
}
private final PsiAnnotation findRequestParam(PsiParameter parameter) {
return PsiAnnotationUtils.INSTANCE.findAnn(parameter, "org.springframework.web.bind.annotation.RequestParam")
}
private final String findParamName(PsiAnnotation requestParamAnn) {
return PsiAnnotationUtils.INSTANCE.findAttr(requestParamAnn, ["name", "value"] as String[])
}
private final Boolean findParamRequired(PsiAnnotation requestParamAnn) {
String required = PsiAnnotationUtils.INSTANCE.findAttr(requestParamAnn, ["required"] as String[]);
if (required != null) {
return !required.contains("false")
} else {
return null
}
}
}
}