Skip to content

Commit 6cf8830

Browse files
authored
refactor: implement cache invalidation for CachedYapiApiHelper (#1233)
1 parent 579c7fd commit 6cf8830

File tree

10 files changed

+838
-76
lines changed

10 files changed

+838
-76
lines changed

.cursorrules

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"excluded": [
3+
"idea-plugin/bin/"
4+
]
5+
}

idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/yapi/AbstractYapiApiHelper.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ abstract class AbstractYapiApiHelper : YapiApiHelper {
139139
override fun copyApi(from: Map<String, String>, target: Map<String, String>) {
140140
val fromToken = from.getToken() ?: throw IllegalArgumentException("no token be found in from")
141141
val targetToken = target.getToken() ?: throw IllegalArgumentException("no token be found in target")
142-
val targetCatId = target["catId"]
142+
val targetCatId = target["catid"]
143143
listApis(from) { api ->
144144
val copyApi = HashMap(api)
145145
copyApi.remove("_id")
@@ -187,7 +187,7 @@ abstract class AbstractYapiApiHelper : YapiApiHelper {
187187
return
188188
}
189189

190-
val catId = from["catId"]
190+
val catId = from["catid"]
191191
if (catId != null) {
192192
listApis(fromToken, catId)?.map { it.asMap() }?.forEach(api)
193193
return
@@ -211,7 +211,7 @@ abstract class AbstractYapiApiHelper : YapiApiHelper {
211211
}
212212
}
213213
return try {
214-
httpClientProvide!!.getHttpClient()
214+
httpClientProvide.getHttpClient()
215215
.get(rawUrl)
216216
.call()
217217
.use { it.string() }

idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/yapi/CachedYapiApiHelper.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ open class CachedYapiApiHelper : DefaultYapiApiHelper() {
2222
{ "" }
2323
}
2424

25+
/**
26+
* Cache for cart data, projectId -> carts
27+
*/
2528
private val cartCache = ConcurrentHashMap<String, List<Any?>>()
29+
30+
/**
31+
* Cache for API data, "$token-$catId" -> APIs
32+
*/
2633
private val apiCache = ConcurrentHashMap<String, JsonArray>()
2734

2835
override fun getProjectIdByToken(token: String): String? {
@@ -55,4 +62,33 @@ open class CachedYapiApiHelper : DefaultYapiApiHelper() {
5562
return@computeIfAbsent super.listApis(token, catId, limit) ?: JsonArray()
5663
}
5764
}
65+
66+
override fun saveApiInfo(apiInfo: HashMap<String, Any?>): Boolean {
67+
val result = super.saveApiInfo(apiInfo)
68+
69+
// Clear API cache when API info is updated
70+
if (result && apiInfo.containsKey("token") && apiInfo.containsKey("catid")) {
71+
val token = apiInfo["token"] as String
72+
val catId = apiInfo["catid"].toString()
73+
apiCache.remove("$token-$catId")
74+
}
75+
76+
return result
77+
}
78+
79+
override fun addCart(
80+
projectId: String,
81+
token: String,
82+
name: String,
83+
desc: String
84+
): Boolean {
85+
val result = super.addCart(projectId, token, name, desc)
86+
87+
// Clear cart cache when new cart is added
88+
if (result) {
89+
cartCache.remove(projectId)
90+
}
91+
92+
return result
93+
}
5894
}

idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/yapi/DefaultYapiApiHelper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ open class DefaultYapiApiHelper : AbstractYapiApiHelper(), YapiApiHelper {
7575
}
7676

7777
override fun saveApiInfo(apiInfo: HashMap<String, Any?>): Boolean {
78-
if (saveInterceptor.beforeSaveApi(this, apiInfo) == false) {
78+
if (saveInterceptor.beforeSaveApi(actionContext, apiInfo) == false) {
7979
return false
8080
}
8181

idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/yapi/YapiApiHelper.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.itangcent.idea.plugin.api.export.yapi
22

33
import com.google.gson.JsonArray
4+
import com.google.gson.JsonElement
45
import com.google.gson.JsonObject
56
import com.google.inject.ImplementedBy
67
import com.itangcent.common.logger.traceError
@@ -65,7 +66,17 @@ fun YapiApiHelper.findExistApi(apiInfo: HashMap<String, Any?>): JsonObject? {
6566
val path = apiInfo["path"] as? String ?: return null
6667
val method = apiInfo["method"] as? String ?: return null
6768
val token = apiInfo["token"] as? String ?: return null
68-
return this.findExistApi(token, path, method)
69+
val catId = apiInfo["catid"]?.toString()
70+
71+
return if (catId != null) {
72+
// If catId is provided, search in that specific category
73+
this.listApis(token, catId, null)?.find { api ->
74+
api.matchesPathAndMethod(path, method)
75+
} as? JsonObject
76+
} else {
77+
// Otherwise, search in all categories
78+
this.findExistApi(token, path, method)
79+
}
6980
}
7081

7182
fun YapiApiHelper.findExistApi(token: String, path: String, method: String): JsonObject? {
@@ -74,7 +85,7 @@ fun YapiApiHelper.findExistApi(token: String, path: String, method: String): Jso
7485
for (cart in carts) {
7586
val catId = (cart as? Map<*, *>)?.get("_id")?.toString() ?: continue
7687
val api = this.listApis(token, catId, null)?.find { api ->
77-
api.sub("path")?.asString == path && api.sub("method")?.asString == method
88+
api.matchesPathAndMethod(path, method)
7889
}
7990
if (api != null) {
8091
return api as JsonObject
@@ -83,6 +94,13 @@ fun YapiApiHelper.findExistApi(token: String, path: String, method: String): Jso
8394
return null
8495
}
8596

97+
/**
98+
* Check if the JsonElement matches the given path and method
99+
*/
100+
private fun JsonElement.matchesPathAndMethod(path: String, method: String): Boolean {
101+
return this.sub("path")?.asString == path && this.sub("method")?.asString == method
102+
}
103+
86104
fun YapiApiHelper.getCartForFolder(folder: Folder, privateToken: String): CartInfo? {
87105

88106
val name: String = folder.name ?: "anonymous"

idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/yapi/YapiSaveInterceptor.kt

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ interface YapiSaveInterceptor {
2929
* @return return {@code false} [YapiApiHelper] will discard this apiInfo.
3030
* else [YapiApiHelper] will save this apiInfo to yapi server.
3131
*/
32-
fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean?
32+
fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean?
3333
}
3434

3535
@Singleton
@@ -44,7 +44,7 @@ class YapiSaveInterceptorCompositeProvider : SpiCompositeBeanProvider<YapiSaveIn
4444
*/
4545
@ConditionOnSetting("yapiExportMode", havingValue = "ALWAYS_UPDATE")
4646
class AlwaysUpdateYapiSaveInterceptor : YapiSaveInterceptor {
47-
override fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean {
47+
override fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean {
4848
return true
4949
}
5050
}
@@ -57,8 +57,9 @@ val ALWAYS_UPDATE_API_SAVE_INTERCEPTOR = AlwaysUpdateYapiSaveInterceptor()
5757
*/
5858
@ConditionOnSetting("yapiExportMode", havingValue = "NEVER_UPDATE")
5959
class NeverUpdateYapiSaveInterceptor : YapiSaveInterceptor {
60-
override fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean {
61-
return !apiHelper.existed(apiInfo)
60+
override fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean {
61+
val yapiApiHelper = actionContext.instance(YapiApiHelper::class)
62+
return !yapiApiHelper.existed(apiInfo)
6263
}
6364
}
6465

@@ -73,22 +74,24 @@ class AlwaysAskYapiSaveInterceptor : YapiSaveInterceptor {
7374
private var selectedYapiSaveInterceptor: YapiSaveInterceptor? = null
7475

7576
@Synchronized
76-
override fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean? {
77+
override fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean? {
7778
if (selectedYapiSaveInterceptor != null) {
78-
return selectedYapiSaveInterceptor!!.beforeSaveApi(apiHelper, apiInfo)
79+
return selectedYapiSaveInterceptor!!.beforeSaveApi(actionContext, apiInfo)
7980
}
80-
if (!apiHelper.existed(apiInfo)) {
81+
82+
val yapiApiHelper = actionContext.instance(YapiApiHelper::class)
83+
if (!yapiApiHelper.existed(apiInfo)) {
8184
return true
8285
}
86+
8387
val valueHolder = ValueHolder<Boolean>()
84-
val context = ActionContext.getContext() ?: return true
85-
context.instance(MessagesHelper::class).showAskWithApplyAllDialog(
88+
actionContext.instance(MessagesHelper::class).showAskWithApplyAllDialog(
8689
"The api [${apiInfo["title"]}] already existed in the project.\n" +
8790
"Do you want update it?",
8891
ConfirmationDialogLabels(okText = "Update", noText = "Skip", cancelText = "Cancel")
8992
) { ret, applyAll ->
9093
if (ret == Messages.CANCEL) {
91-
context.stop()
94+
actionContext.stop()
9295
valueHolder.success(false)
9396
return@showAskWithApplyAllDialog
9497
}
@@ -115,25 +118,27 @@ class AlwaysAskYapiSaveInterceptor : YapiSaveInterceptor {
115118
* Only works when the configuration "yapi.no_update.description" is set to true.
116119
*/
117120
class NoUpdateDescriptionYapiSaveInterceptor : YapiSaveInterceptor {
118-
override fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean? {
119-
recoverDescription(apiHelper, apiInfo)
121+
override fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean? {
122+
val noUpdateDescription = actionContext
123+
.instance(ConfigReader::class)
124+
.first("yapi.no_update.description")
125+
?.toBool(false) == true
126+
127+
if (noUpdateDescription) {
128+
recoverDescription(actionContext, apiInfo)
129+
}
120130
return null
121131
}
122132

123133
/**
124134
* Retrieves the existing API information from YAPI and copies the description and markdown
125135
* fields to the new API information to prevent them from being overwritten.
126136
*/
127-
private fun recoverDescription(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>) {
128-
val disable = ActionContext.getContext()
129-
?.instance(ConfigReader::class)
130-
?.first("yapi.no_update.description")
131-
?.toBool(false) ?: false
132-
if (!disable) return
133-
134-
val existedApi = apiHelper.findExistApi(apiInfo) ?: return
137+
private fun recoverDescription(actionContext: ActionContext, apiInfo: HashMap<String, Any?>) {
138+
val yapiApiHelper = actionContext.instance(YapiApiHelper::class)
139+
val existedApi = yapiApiHelper.findExistApi(apiInfo) ?: return
135140
val apiId = existedApi.sub("_id")?.asString ?: return
136-
val existedApiInfo = apiHelper.getApiInfo(apiInfo["token"] as String, apiId)
141+
val existedApiInfo = yapiApiHelper.getApiInfo(apiInfo["token"] as String, apiId)
137142

138143
val existedDescription = existedApiInfo.sub("desc")?.asString ?: ""
139144
apiInfo["desc"] = existedDescription
@@ -149,17 +154,17 @@ class NoUpdateDescriptionYapiSaveInterceptor : YapiSaveInterceptor {
149154
*/
150155
@ConditionOnSetting("yapiExportMode", havingValue = "UPDATE_IF_CHANGED")
151156
class UpdateIfChangedYapiSaveInterceptor : YapiSaveInterceptor {
152-
@Inject
153-
private lateinit var logger: Logger
154-
155-
override fun beforeSaveApi(apiHelper: YapiApiHelper, apiInfo: HashMap<String, Any?>): Boolean? {
156-
if (!apiHelper.existed(apiInfo)) {
157+
override fun beforeSaveApi(actionContext: ActionContext, apiInfo: HashMap<String, Any?>): Boolean? {
158+
val yapiApiHelper = actionContext.instance(YapiApiHelper::class)
159+
val logger = actionContext.instance(Logger::class)
160+
161+
if (!yapiApiHelper.existed(apiInfo)) {
157162
return true
158163
}
159164

160-
val existedApi = apiHelper.findExistApi(apiInfo) ?: return true
165+
val existedApi = yapiApiHelper.findExistApi(apiInfo) ?: return true
161166
val apiId = existedApi.sub("_id")?.asString ?: return true
162-
val existedApiInfo = apiHelper.getApiInfo(apiInfo["token"] as String, apiId) ?: return true
167+
val existedApiInfo = yapiApiHelper.getApiInfo(apiInfo["token"] as String, apiId) ?: return true
163168

164169
// Compare key fields that determine if the API has changed
165170
if (isApiUnchanged(apiInfo, existedApiInfo)) {

0 commit comments

Comments
 (0)