From c82c7da9193d351f8d9515c214d233b283370f57 Mon Sep 17 00:00:00 2001 From: tangcent Date: Thu, 24 Oct 2019 19:40:00 +0800 Subject: [PATCH 01/10] join gitter --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bea945ac1..8c2be7dc2 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![](https://img.shields.io/jetbrains/plugin/d/12211)](https://plugins.jetbrains.com/plugin/12211-easyapi) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tangcent/easy-api.svg)](http://isitmaintained.com/project/tangcent/easy-api "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/tangcent/easy-api.svg)](http://isitmaintained.com/project/tangcent/easy-api "Percentage of issues still open") +[![Gitter](https://badges.gitter.im/Earth-1610/easy-api.svg)](https://gitter.im/Earth-1610/easy-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - Simplifies API Development - Parsing based on [javadoc](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html)&[KDoc](https://kotlinlang.org/docs/reference/kotlin-doc.html) From 475721a1a345d6edb32295808d1dae656eda880a Mon Sep 17 00:00:00 2001 From: tangcent Date: Fri, 25 Oct 2019 22:27:45 +0800 Subject: [PATCH 02/10] QA.md --- README.md | 3 +- docs/2. Supported-custom-rules.md | 4 +- docs/QA.md | 164 ++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 docs/QA.md diff --git a/README.md b/README.md index 8c2be7dc2..d4fe40e7c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ [![Gitter](https://badges.gitter.im/Earth-1610/easy-api.svg)](https://gitter.im/Earth-1610/easy-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - Simplifies API Development -- Parsing based on [javadoc](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html)&[KDoc](https://kotlinlang.org/docs/reference/kotlin-doc.html) +- Parsing based on [javadoc](#Javadoc)&[KDoc](#KDoc) +- [QA](https://github.com/tangcent/easy-api/blob/master/docs/QA.md) # Table of Contents diff --git a/docs/2. Supported-custom-rules.md b/docs/2. Supported-custom-rules.md index d3ca2c4f3..3dbe8effb 100644 --- a/docs/2. Supported-custom-rules.md +++ b/docs/2. Supported-custom-rules.md @@ -1,12 +1,12 @@ # supported rules | rule key | context | version | desc | -| :-: | :-: | :-: | :-: | +| --- | --- | --- | --- | | module | class | v0.7.2+ | how to group api | | ignore | class/method | v0.7.2+ | ignore api in class | | json.rule.field.name | field | v0.7.2+ | the property name for field | | json.rule.field.ignore | field | v0.7.2+ | ignore field | -| json.rule.convert | - | v0.7.2+ | the property type for field | +| json.rule.convert | class | v0.7.2+ | the property type for field | | doc.field | field | v0.7.2+ | the additional doc for field | | doc.method | method | v0.7.2+ | the additional doc for method | | param.required | arg | v0.7.3+ | the param is required(must not be null) | diff --git a/docs/QA.md b/docs/QA.md new file mode 100644 index 000000000..1d71e2820 --- /dev/null +++ b/docs/QA.md @@ -0,0 +1,164 @@ +1. How to add custom config? + + * add .easy.api.config in the root of project or module + + * see [Supported-custom-rules.md](https://github.com/tangcent/easy-api/blob/master/docs/2.%20Supported-custom-rules.md) + +2. How to group apis to special directory? + + * add config: + ```properties + #find module for comment tag + module=#module + ``` + + * add comment tag at class + ```java + /** + * Mock Apis + * + * @module mock + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + } + ``` + +3. How to ignore apis? + + * add config: + ```properties + #ignore class or method which has comment tag 'ignore' + ignore=#ignore + ``` + + * add comment tag at controller class for ignore all apis in controller + ```java + /** + * Mock Apis + * + * @ignore + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + } + ``` + + * add comment tag at api for ignore special api in controller + ```java + /** + * Mock Apis + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + + /** + * Mock String + * @ignore + */ + @GetMapping("/string") + public String mockString() { + return Result.success("mock string"); + } + + } + ``` + +4. How to set name&description of api/directory? + * in general: + ```java + /** + * The head line will be the name of api directory + * The rest lines will be the description of api directory + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + + /** + * The head line will be the name of api + * The rest lines will be the description of api + */ + @GetMapping("/string") + public String mockString() { + return Result.success("mock string"); + } + } + ``` + +5. How to mark api as deprecated in description + * you can add additional config: + ```properties + doc.method[#deprecated]=groovy:"\n「deprecated」" + it.doc("deprecated") + doc.method[@java.lang.Deprecated]=「deprecated」 + doc.method[@kotlin.Deprecated]=groovy:"\n「deprecated」" + it.ann("kotlin.Deprecated","message") + + doc.method[groovy:it.containingClass().hasDoc("deprecated")]=groovy:"\n「deprecated」" + it.containingClass().doc("deprecated") + doc.method[groovy:it.containingClass().hasAnn("java.lang.Deprecated")]=「deprecated」 + doc.method[groovy:it.containingClass().hasAnn("kotlin.Deprecated")]=groovy:"\n「deprecated」 " + it.containingClass().ann("kotlin.Deprecated","message") + + ``` + +6. How to declare a api requires some special permission in a description with javax.annotation.security? + + ``` + * add config for spring security: + ```properties + # security description + doc.method[@javax.annotation.security.RolesAllowed]=groovy:"\require role:"+it.ann("javax.annotation.security.RolesAllowed") + ``` + * code: + ```java + /** + * The head line will be the name of api directory + * The rest lines will be the description of api directory + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + + /** + * The head line will be the name of api + * The rest lines will be the description of api + */ + @GetMapping("/string") + @RolesAllowed("admin") + public String mockString() { + return Result.success("mock string"); + } + } + + ``` +7. How to config for spring security + + * add config for spring security: + ```properties + # security description + find_role_in_PreAuthorize=(function(exp){var str="";if(exp.indexOf("hasRole")!=-1){var roles=exp.match(/hasRole\\((.*?)\\)/);if(roles&&roles.length>1){str+="require role:"+roles[1];}};return str}) + doc.method[@org.springframework.security.access.prepost.PreAuthorize]=js:${find_role_in_PreAuthorize}(it.ann("org.springframework.security.access.prepost.PreAuthorize")) + ``` + * code: + ```java + /** + * The head line will be the name of api directory + * The rest lines will be the description of api directory + */ + @RestController + @RequestMapping(value = "mock") + public class MockCtrl { + + /** + * The head line will be the name of api + * The rest lines will be the description of api + */ + @GetMapping("/string") + @PreAuthorize("hasRole('admin')") + public String mockString() { + return Result.success("mock string"); + } + } + + ``` \ No newline at end of file From 2b2e2975f836865282e508bcbf5f5ffcfaaa8a8d Mon Sep 17 00:00:00 2001 From: tangcent Date: Sat, 26 Oct 2019 18:38:47 +0800 Subject: [PATCH 03/10] remove error token in QA.md --- docs/QA.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/QA.md b/docs/QA.md index 1d71e2820..465f99425 100644 --- a/docs/QA.md +++ b/docs/QA.md @@ -7,6 +7,7 @@ 2. How to group apis to special directory? * add config: + ```properties #find module for comment tag module=#module @@ -104,7 +105,7 @@ 6. How to declare a api requires some special permission in a description with javax.annotation.security? - ``` + * add config for spring security: ```properties # security description From 8045207763f8718bca48b417160322ad9f38abb0 Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 19:42:08 +0800 Subject: [PATCH 04/10] fix script:class.name() --- idea-plugin/libs/intellij-jvm.jar | Bin 78677 -> 80058 bytes .../idea/plugin/rule/ScriptRuleParser.kt | 27 ++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/idea-plugin/libs/intellij-jvm.jar b/idea-plugin/libs/intellij-jvm.jar index 5dc934e359531a7c891c1514a9d29819799c4faa..438e8da576488e2bd350402297b909df5283081b 100644 GIT binary patch delta 14581 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 diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt index 93f10c1c4..76baa1ad0 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt @@ -113,7 +113,7 @@ abstract class ScriptRuleParser : RuleParser { } fun name(): String { - return getName()!! + return getName() ?: "" } /** @@ -224,6 +224,10 @@ abstract class ScriptRuleParser : RuleParser { } override fun getName(): String? { + return psiClass.qualifiedName + } + + override fun getSimpleName(): String? { return psiClass.name } } @@ -364,17 +368,34 @@ abstract class ScriptRuleParser : RuleParser { private var duckType: DuckType? = null override fun getName(): String? { - return duckType?.let { getDuckTypeName(it) } + return getDuckTypeName(duckType) } - private fun getDuckTypeName(duckType: DuckType): String? { + private fun getDuckTypeName(duckType: DuckType?): String? { return when (duckType) { + null -> null + is SinglePrimitiveDuckType -> duckType.psiType().name + is SingleDuckType -> duckType.psiClass().qualifiedName ?: duckType.psiClass().name is ArrayDuckType -> getDuckTypeName(duckType.componentType()) + "[]" + else -> duckType.toString() + } + } + + override fun getSimpleName(): String? { + return getDuckTypeSimpleName(duckType) + } + + private fun getDuckTypeSimpleName(duckType: DuckType?): String? { + return when (duckType) { + null -> null + is SinglePrimitiveDuckType -> duckType.psiType().name is SingleDuckType -> duckType.psiClass().name + is ArrayDuckType -> getDuckTypeSimpleName(duckType.componentType()) + "[]" else -> duckType.toString() } } + fun methods(): Array { return getResource()?.let { psiElement -> return@let (psiElement as PsiClass).allMethods.map { ScriptPsiMethodContext(it) } From ca3ae3ea48b8a317ad9a8a5f23f78c28d01516fd Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 20:38:30 +0800 Subject: [PATCH 05/10] provide 'hasModifier(modifier)' for class/method/field --- .../idea/plugin/rule/ScriptRuleParser.kt | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt index 76baa1ad0..b82223fa8 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/rule/ScriptRuleParser.kt @@ -98,18 +98,18 @@ abstract class ScriptRuleParser : RuleParser { return psiElement!!.getPropertyValue("name")?.toString() } - override fun asPsiDocCommentOwner(): PsiDocCommentOwner { + override fun asPsiDocCommentOwner(): PsiDocCommentOwner? { if (psiElement is PsiDocCommentOwner) { return psiElement as PsiDocCommentOwner } - throw IllegalArgumentException("$psiElement has non comment") + return null } - override fun asPsiModifierListOwner(): PsiModifierListOwner { + override fun asPsiModifierListOwner(): PsiModifierListOwner? { if (psiElement is PsiModifierListOwner) { return psiElement as PsiModifierListOwner } - throw IllegalArgumentException("$psiElement has non annotation") + return null } fun name(): String { @@ -166,6 +166,9 @@ abstract class ScriptRuleParser : RuleParser { tag, subTag) } + fun hasModifier(modifier: String): Boolean { + return asPsiModifierListOwner()?.hasModifierProperty(modifier) ?: false + } } /** @@ -223,6 +226,10 @@ abstract class ScriptRuleParser : RuleParser { return false } + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiClass + } + override fun getName(): String? { return psiClass.qualifiedName } @@ -243,6 +250,10 @@ abstract class ScriptRuleParser : RuleParser { return ScriptPsiTypeContext(psiField.type) } + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiField + } + override fun getName(): String? { return psiField.name } @@ -324,9 +335,14 @@ abstract class ScriptRuleParser : RuleParser { return ScriptPsiClassContext(psiMethod.containingClass!!) } + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiMethod + } + override fun getName(): String? { return psiMethod.name } + } /** @@ -352,6 +368,10 @@ abstract class ScriptRuleParser : RuleParser { override fun getName(): String? { return psiParameter.name } + + override fun asPsiModifierListOwner(): PsiModifierListOwner? { + return psiParameter + } } /** @@ -395,7 +415,6 @@ abstract class ScriptRuleParser : RuleParser { } } - fun methods(): Array { return getResource()?.let { psiElement -> return@let (psiElement as PsiClass).allMethods.map { ScriptPsiMethodContext(it) } From 18e4ea48f1be190123227eae7860bb76984a0ac2 Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 20:38:43 +0800 Subject: [PATCH 06/10] ignore transient field by recommend config --- .../com/itangcent/idea/plugin/config/RecommendConfigReader.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt index 3687b5636..c3d9d0a74 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt @@ -80,6 +80,9 @@ class RecommendConfigReader : ConfigReader { json.rule.field.name=@com.google.gson.annotations.SerializedName#value json.rule.field.ignore=!@com.google.gson.annotations.Expose#serialize + #ignore transient field + json.rule.field.ignore=groovy:it.hasModifier("transient") + #The ObjectId and Date are parsed as strings json.rule.convert[org.bson.types.ObjectId]=java.lang.String json.rule.convert[java.util.Date]=java.lang.String From fea18c5a4afad49281151419ddfc4e6258585d9a Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 21:42:03 +0800 Subject: [PATCH 07/10] release v1.1.183.0.3 --- build.gradle | 2 +- gradle.properties | 2 +- idea-plugin/parts/pluginChanges.html | 19 +++++-------------- .../src/main/resources/META-INF/plugin.xml | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index d62637a3e..9a5737688 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,3 @@ group 'com.itangcent' -version '1.1.183.0.2' +version '1.1.183.0.3' diff --git a/gradle.properties b/gradle.properties index 5a067ea52..8c9a0ebc6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.daemon=true org.gradle.workers.max=8 idea_version=2017.3.5 plugin_name=EasyApi -plugin_version=1.1.183.0.2 +plugin_version=1.1.183.0.3 descriptionFile=parts/pluginDescription.html changesFile=parts/pluginChanges.html \ No newline at end of file diff --git a/idea-plugin/parts/pluginChanges.html b/idea-plugin/parts/pluginChanges.html index 544cf633c..650396338 100644 --- a/idea-plugin/parts/pluginChanges.html +++ b/idea-plugin/parts/pluginChanges.html @@ -1,22 +1,13 @@ -v1.1.183.0.2(2019-10-23) +v1.1.183.0.3(2019-10-28)
Full Changelog
    enhancement: -
  • support rule: name[filter]=value(#138) -
  • -
  • parse kotlin files in ApiDashboard(#141) +
  • provide 'hasModifier(modifier)' for class/method/field(#157)
    fix: -
  • support Serializer for Enum(#134) -
  • -
  • fix error base path for APIs in super class(#137) -
  • -
  • fix: ApiDashboard not show kotlin module&apis(#140) +
  • script config: class.name()(#155)
\ No newline at end of file diff --git a/idea-plugin/src/main/resources/META-INF/plugin.xml b/idea-plugin/src/main/resources/META-INF/plugin.xml index c9ad63afc..b7152ee7b 100755 --- a/idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/idea-plugin/src/main/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ com.itangcent.idea.plugin.easy-api EasyApi - 1.1.183.0.2 + 1.1.183.0.3 Tangcent From 5986b838b1a41a63aa046edf46dff4118f32834d Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 22:22:17 +0800 Subject: [PATCH 08/10] release v1.1.1.183.0.4 --- build.gradle | 2 +- gradle.properties | 2 +- idea-plugin/parts/pluginChanges.html | 2 +- idea-plugin/src/main/resources/META-INF/plugin.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 0b76c7a93..5010392c3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,3 @@ group 'com.itangcent' -version '1.1.0.183.0.2' +version '1.1.1.183.0.4' diff --git a/gradle.properties b/gradle.properties index f7331f318..8408d5e98 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.daemon=true org.gradle.workers.max=8 idea_version=2017.3.5 plugin_name=EasyYapi -plugin_version=1.1.0.183.0.2 +plugin_version=1.1.1.183.0.4 descriptionFile=parts/pluginDescription.html changesFile=parts/pluginChanges.html \ No newline at end of file diff --git a/idea-plugin/parts/pluginChanges.html b/idea-plugin/parts/pluginChanges.html index 2cda44e10..9b1c9029d 100644 --- a/idea-plugin/parts/pluginChanges.html +++ b/idea-plugin/parts/pluginChanges.html @@ -1,4 +1,4 @@ -v1.1.0.183.0.2(2019-10-23) +v1.1.1.183.0.4(2019-10-28)
Full Changelog
    enhancement: diff --git a/idea-plugin/src/main/resources/META-INF/plugin.xml b/idea-plugin/src/main/resources/META-INF/plugin.xml index 782b85063..f2e1a1bae 100755 --- a/idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/idea-plugin/src/main/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ com.itangcent.idea.plugin.easy-yapi EasyYapi - 1.1.0.183.0.2 + 1.1.1.183.0.4 Tangcent From 7f1b3c32a54c824f87e6a6e8428d79e79bcc6de0 Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 22:38:26 +0800 Subject: [PATCH 09/10] fix release tag link --- idea-plugin/parts/pluginChanges.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idea-plugin/parts/pluginChanges.html b/idea-plugin/parts/pluginChanges.html index 9b1c9029d..6e0970157 100644 --- a/idea-plugin/parts/pluginChanges.html +++ b/idea-plugin/parts/pluginChanges.html @@ -1,4 +1,4 @@ -v1.1.1.183.0.4(2019-10-28) +v1.1.1.183.0.4(2019-10-28)
    Full Changelog
      enhancement: From b979fa3b107488759fde3ace05bfac97d0263b09 Mon Sep 17 00:00:00 2001 From: tangcent Date: Mon, 28 Oct 2019 22:46:56 +0800 Subject: [PATCH 10/10] add QA.md to README --- README.md | 2 ++ README_cn.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 052e44f99..6c33283d6 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ - [demo](https://github.com/Earth-1610/spring-demo) +- [QA](https://github.com/tangcent/easy-yapi/blob/master/docs/QA.md) + # Table of Contents * 1 [Feature](#Feature) diff --git a/README_cn.md b/README_cn.md index a476be493..0971b05e0 100644 --- a/README_cn.md +++ b/README_cn.md @@ -13,6 +13,8 @@ - [demo](https://github.com/Earth-1610/spring-demo) +- [QA](https://github.com/tangcent/easy-yapi/blob/master/docs/QA.md) + - 在[easy-api](https://github.com/tangcent/easy-api)的基础上增加对yapi的支持 如果你对easy-api有兴趣或者希望支持其他第三方API管理平台,那么这也是一个很好的例子,通过对比[easy-api](https://github.com/tangcent/easy-api)与[easy-yapi](https://github.com/tangcent/easy-aypi)的差异,可以了解到如何在