Skip to content

Commit 1fea671

Browse files
authored
Merge pull request #2703 from digma-ai/recent-activity-remove-messages
Recent activity remove messages Closes #2700
2 parents 4b4b633 + f6e0ca0 commit 1fea671

21 files changed

+407
-433
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ intellijPlatform {
216216
}
217217
select {
218218
types =
219-
listOf(IntelliJPlatformType.IntellijIdeaCommunity, IntelliJPlatformType.IntellijIdeaUltimate)
219+
listOf(IntelliJPlatformType.IntellijIdeaCommunity)
220220
channels = listOf(channel)
221221
sinceBuild = project.currentProfile().pluginSinceBuild
222222
untilBuild = project.currentProfile().pluginUntilBuild

src/main/kotlin/org/digma/intellij/plugin/ui/jcef/BaseSchemeHandlerFactory.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.cef.handler.CefResourceHandler
99
import org.cef.network.CefRequest
1010
import org.digma.intellij.plugin.errorreporting.ErrorReporter
1111
import org.digma.intellij.plugin.log.Log
12+
import org.digma.intellij.plugin.ui.jcef.pluginapi.PluginApiResourceHandler
1213
import java.net.MalformedURLException
1314
import java.net.URI
1415
import java.net.URL
@@ -74,6 +75,8 @@ abstract class BaseSchemeHandlerFactory : CefSchemeHandlerFactory {
7475
JaegerProxyResourceHandler(jaegerQueryUrl)
7576
}else if (ApiProxyResourceHandler.isApiProxyCall(url)) {
7677
ApiProxyResourceHandler(project)
78+
}else if (PluginApiResourceHandler.isPluginApiCall(url)) {
79+
PluginApiResourceHandler(project)
7780
}else{
7881
null
7982
}

src/main/kotlin/org/digma/intellij/plugin/ui/jcef/JCefBrowserUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ fun executeWindowPostMessageJavaScript(browser: CefBrowser, message: String, pro
3535
)
3636
}
3737

38+
fun <T> jsonToObject(jsonBytes: ByteArray, type: Class<T>): T {
39+
return CommonObjectMapper.objectMapper.readValue(jsonBytes, type)
40+
}
3841

3942
fun <T> jsonToObject(jsonStr: String, type: Class<T>): T {
4043
return CommonObjectMapper.objectMapper.readValue(jsonStr, type)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.digma.intellij.plugin.ui.jcef.pluginapi
2+
3+
import com.intellij.openapi.project.Project
4+
import java.net.URL
5+
import java.net.URLDecoder
6+
import java.nio.charset.StandardCharsets
7+
8+
const val CommandQueryName = "pluginCommand"
9+
10+
abstract class Command {
11+
12+
enum class Commands(val commandName: String) {
13+
RECENT_ACTIVITY_BADGE("RecentActivityBadge");
14+
15+
16+
fun createInstance():Command{
17+
return RecentActivityBadgeCommand()
18+
}
19+
20+
companion object {
21+
private val NAME_MAP = values().associateBy { it.commandName.lowercase() }
22+
23+
fun fromCommandName(commandName: String): Commands? {
24+
return NAME_MAP[commandName.lowercase()]
25+
}
26+
}
27+
}
28+
29+
30+
companion object{
31+
32+
fun getCommand(apiUrl: URL): Command? {
33+
34+
val query = apiUrl.query ?: return null // get query string or return null if none
35+
36+
val commandName = query.split("&")
37+
.mapNotNull { it -> it.split("=", limit = 2).takeIf { it.size == 2 } }
38+
.firstOrNull { it[0] == CommandQueryName }
39+
?.let { URLDecoder.decode(it[1], StandardCharsets.UTF_8) }
40+
41+
if (commandName == null) {
42+
return null
43+
}
44+
45+
val commandEnum = Commands.fromCommandName(commandName) ?: return null
46+
return commandEnum.createInstance()
47+
}
48+
}
49+
50+
abstract fun execute(project: Project, requestId: Long, requestMethod: String, postData: ByteArray?, headers: Map<String, String>): PluginApiHttpResponse
51+
52+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.digma.intellij.plugin.ui.jcef.pluginapi
2+
3+
data class ErrorData(val error: String)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.digma.intellij.plugin.ui.jcef.pluginapi
2+
3+
import org.digma.intellij.plugin.common.objectToJson
4+
import java.io.InputStream
5+
6+
class PluginApiHttpResponse(
7+
val status: Int,
8+
val headers: MutableMap<String, String>,
9+
val contentLength: Long?,
10+
val contentType: String?,
11+
val contentStream: InputStream?
12+
) {
13+
override fun toString(): String {
14+
//don't convert contentStream to string, this input stream should be read by the http layer. and it may be a large stream.
15+
return "PluginApiHttpResponse(status=$status, headers=$headers, contentLength=$contentLength, contentType=$contentType, contentStream=$contentStream)"
16+
}
17+
18+
companion object {
19+
fun createErrorResponse(statusCode: Int, error: ErrorData): PluginApiHttpResponse {
20+
val errorJson = objectToJson(error).toByteArray()
21+
return PluginApiHttpResponse(
22+
statusCode,
23+
headers = mutableMapOf(
24+
"Content-Type" to "application/json",
25+
"Content-Length" to errorJson.size.toString()
26+
),
27+
contentLength = errorJson.size.toLong(),
28+
contentType = "application/json",
29+
contentStream = errorJson.inputStream()
30+
)
31+
}
32+
}
33+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package org.digma.intellij.plugin.ui.jcef.pluginapi
2+
3+
import com.intellij.openapi.diagnostic.Logger
4+
import com.intellij.openapi.project.Project
5+
import org.cef.callback.CefCallback
6+
import org.cef.handler.CefResourceHandler
7+
import org.cef.misc.IntRef
8+
import org.cef.misc.StringRef
9+
import org.cef.network.CefRequest
10+
import org.cef.network.CefResponse
11+
import org.digma.intellij.plugin.common.Backgroundable
12+
import org.digma.intellij.plugin.errorreporting.ErrorReporter
13+
import org.digma.intellij.plugin.log.Log
14+
import org.digma.intellij.plugin.ui.jcef.hasFileElements
15+
import org.digma.intellij.plugin.ui.jcef.postDataToByteArray
16+
import java.net.URI
17+
import java.net.URL
18+
19+
class PluginApiResourceHandler(val project: Project) : CefResourceHandler {
20+
21+
private val logger = Logger.getInstance(this::class.java)
22+
23+
private var apiResponse: PluginApiHttpResponse? = null
24+
25+
companion object {
26+
private const val URL_PREFIX = "/plugin-api"
27+
28+
fun isPluginApiCall(url: URL): Boolean {
29+
return url.path.startsWith(URL_PREFIX)
30+
}
31+
}
32+
33+
34+
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
35+
Log.log(logger::trace, "processing request {}, [request id:{}]", request.url, request.identifier)
36+
37+
//todo: we don't support multi part form data yet or file elements in the post data
38+
if ((request.postData?.elementCount ?: -1) > 1) {
39+
Log.log(
40+
logger::warn,
41+
"encountered multi part post data. it is not supported by Digma plugin api handler, [request id:{}]",
42+
request.identifier
43+
)
44+
45+
val error = ErrorData("encountered multi part post data. it is not supported by Digma plugin api handler.")
46+
apiResponse = PluginApiHttpResponse.createErrorResponse(500, error)
47+
callback.Continue()
48+
return true
49+
}
50+
51+
52+
if (hasFileElements(request.postData)) {
53+
Log.log(
54+
logger::warn,
55+
"encountered file element in post data. it is not supported by Digma plugin api handler, [request id:{}]",
56+
request.identifier
57+
)
58+
59+
val error = ErrorData("encountered file element in post data. it is not supported by Digma plugin api handler.")
60+
apiResponse = PluginApiHttpResponse.createErrorResponse(500, error)
61+
callback.Continue()
62+
return true
63+
}
64+
65+
66+
//the request is valid only in the scope of this method, so take the data we need before starting a background thread
67+
val postData = request.postData?.let {
68+
postDataToByteArray(request, it)
69+
}
70+
val requestId = request.identifier
71+
val requestUrl = request.url
72+
val requestMethod = request.method
73+
val headers = mutableMapOf<String, String>()
74+
request.getHeaderMap(headers)
75+
76+
Backgroundable.executeOnPooledThread {
77+
executeRequest(requestId, requestUrl, requestMethod, postData, headers, callback)
78+
}
79+
80+
return true
81+
}
82+
83+
84+
private fun executeRequest(
85+
requestId: Long,
86+
requestUrl: String,
87+
requestMethod: String,
88+
postData: ByteArray?,
89+
headers: Map<String, String>,
90+
callback: CefCallback
91+
) {
92+
try {
93+
94+
val apiUrl = URI(requestUrl).toURL()
95+
96+
val command = getCommand(apiUrl)
97+
if (command == null) {
98+
Log.log(logger::trace, "command not found for url {}, [request id:{}]", requestUrl, requestId)
99+
val error = ErrorData("command not found for url [$requestUrl]")
100+
apiResponse = PluginApiHttpResponse.createErrorResponse(404, error)
101+
return
102+
}
103+
104+
Log.log(logger::trace, "executing command {}, [request id:{}]", command.javaClass.name, requestId)
105+
apiResponse = command.execute(project, requestId, requestMethod, postData, headers)
106+
Log.log(logger::trace, "got api response {}, [request id:{}]", apiResponse, requestId)
107+
108+
} catch (e: Throwable) {
109+
Log.warnWithException(logger, e, "processRequest {} failed, [request id:{}]", requestUrl, requestId)
110+
ErrorReporter.Companion.getInstance().reportError("PluginApiResourceHandler.processRequest", e)
111+
112+
val error = ErrorData("encountered exception in plugin api handler [$e]. please check the logs.")
113+
apiResponse = PluginApiHttpResponse.createErrorResponse(500, error)
114+
} finally {
115+
callback.Continue()
116+
}
117+
}
118+
119+
120+
private fun getCommand(apiUrl: URL): Command? {
121+
return Command.getCommand(apiUrl)
122+
}
123+
124+
125+
override fun getResponseHeaders(response: CefResponse, responseLength: IntRef, redirectUrl: StringRef) {
126+
apiResponse?.let { res ->
127+
response.status = res.status
128+
response.setHeaderMap(res.headers.toMap())
129+
response.mimeType = res.contentType
130+
131+
res.contentLength?.let { length ->
132+
responseLength.set(length.toInt())
133+
}
134+
}
135+
}
136+
137+
138+
override fun readResponse(dataOut: ByteArray, bytesToRead: Int, bytesRead: IntRef, callback: CefCallback): Boolean {
139+
return try {
140+
val inputStream = apiResponse?.contentStream
141+
val read = inputStream?.read(dataOut, 0, bytesToRead)
142+
if (read == null || read <= 0) {
143+
bytesRead.set(0)
144+
inputStream?.close()
145+
return false
146+
}
147+
bytesRead.set(read)
148+
true
149+
} catch (e: Exception) {
150+
bytesRead.set(0)
151+
Log.warnWithException(logger, e, "exception readResponse")
152+
ErrorReporter.Companion.getInstance().reportError("ApiProxyResourceHandler.readResponse", e)
153+
false
154+
}
155+
}
156+
157+
158+
override fun cancel() {
159+
apiResponse?.contentStream?.close()
160+
}
161+
162+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.digma.intellij.plugin.ui.jcef.pluginapi
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import com.intellij.openapi.diagnostic.Logger
6+
import com.intellij.openapi.project.Project
7+
import org.digma.intellij.plugin.log.Log
8+
import org.digma.intellij.plugin.ui.jcef.jsonToObject
9+
import org.digma.intellij.plugin.ui.recentactivity.RecentActivityToolWindowIconChanger
10+
import java.beans.ConstructorProperties
11+
12+
class RecentActivityBadgeCommand : Command() {
13+
14+
private val logger = Logger.getInstance(this::class.java)
15+
16+
override fun execute(
17+
project: Project,
18+
requestId: Long,
19+
requestMethod: String,
20+
postData: ByteArray?,
21+
headers: Map<String, String>
22+
): PluginApiHttpResponse {
23+
24+
try {
25+
Log.log(logger::trace, "recent activity badge command received request id {}", requestId)
26+
val badgeRequest = postData?.let { jsonToObject(it, BadgeRequest::class.java) }
27+
Log.log(logger::trace, "recent activity badge command received request id {}, badgeRequest {}", requestId, badgeRequest)
28+
29+
if (badgeRequest == null) {
30+
Log.log(logger::warn, "recent activity badge command received request id {}, invalid or missing badge request", requestId)
31+
// Return 400 Bad Request, with a small error body
32+
val error = ErrorData("Invalid or missing badge request")
33+
return PluginApiHttpResponse.createErrorResponse(400, error)
34+
}
35+
36+
Log.log(logger::trace, "recent activity badge command received request id {}, badgeRequest {}, setting recent activity tool window icon", requestId, badgeRequest)
37+
val recentActivityToolWindowIconChanger = RecentActivityToolWindowIconChanger.getInstance(project)
38+
if (badgeRequest.status) {
39+
recentActivityToolWindowIconChanger.showBadge()
40+
} else {
41+
recentActivityToolWindowIconChanger.hideBadge()
42+
}
43+
44+
return PluginApiHttpResponse(
45+
status = 200,
46+
headers = headers.toMutableMap(),
47+
contentLength = 0,
48+
contentType = null,
49+
contentStream = null
50+
)
51+
} catch (e: Throwable) {
52+
Log.warnWithException(logger, e, "recent activity badge command failed, [request id:{}]", requestId)
53+
val error = ErrorData(e.message ?: e.toString())
54+
return PluginApiHttpResponse.createErrorResponse(500, error)
55+
}
56+
}
57+
}
58+
59+
data class BadgeRequest
60+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
61+
@ConstructorProperties("status")
62+
constructor(
63+
@get:JsonProperty("status")
64+
@param:JsonProperty("status")
65+
val status: Boolean
66+
)

src/main/kotlin/org/digma/intellij/plugin/ui/recentactivity/RecentActivitiesStartup.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
package org.digma.intellij.plugin.ui.recentactivity
22

3-
import com.intellij.openapi.project.Project
43
import org.digma.intellij.plugin.ui.jcef.BaseEnvJsTemplateBuilder
54

6-
private const val RECENT_EXPIRATION_LIMIT_VARIABLE = "recentActivityExpirationLimit"
7-
85

96
class RecentActivityEnvJsTemplateBuilder(templatePath: String) : BaseEnvJsTemplateBuilder(templatePath) {
107

11-
override fun addAppSpecificEnvVariable(project: Project, data: MutableMap<String, Any>) {
12-
data[RECENT_EXPIRATION_LIMIT_VARIABLE] = RECENT_EXPIRATION_LIMIT_MILLIS
13-
}
14-
158
}

0 commit comments

Comments
 (0)