Skip to content

Commit 298a412

Browse files
committed
Merge branch 'main' into report-endpoints
2 parents fbc3dba + 220c55e commit 298a412

File tree

7 files changed

+319
-2
lines changed

7 files changed

+319
-2
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package org.digma.intellij.plugin.protocol
2+
3+
import com.intellij.openapi.components.Service
4+
import com.intellij.openapi.diagnostic.Logger
5+
import com.intellij.openapi.project.Project
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.TimeoutCancellationException
8+
import kotlinx.coroutines.delay
9+
import kotlinx.coroutines.launch
10+
import kotlinx.coroutines.withTimeout
11+
import org.digma.intellij.plugin.common.DisposableAdaptor
12+
import org.digma.intellij.plugin.errorreporting.ErrorReporter
13+
import org.digma.intellij.plugin.log.Log
14+
import org.digma.intellij.plugin.scope.ScopeManager
15+
import org.digma.intellij.plugin.scope.SpanScope
16+
17+
const val ACTION_PARAM_NAME = "action"
18+
const val ACTION_SHOW_ASSET_PARAM_NAME = "showAsset"
19+
const val CODE_OBJECT_ID_PARAM_NAME = "codeObjectId"
20+
const val ENVIRONMENT_ID_PARAM_NAME = "environmentId"
21+
const val ACTION_SHOW_ASSETS_TAB_PARAM_NAME = "showAssetsTab"
22+
23+
@Service(Service.Level.PROJECT)
24+
class DigmaProtocolApi(val cs: CoroutineScope) : DisposableAdaptor {
25+
26+
private val logger: Logger = Logger.getInstance(this::class.java)
27+
28+
private var mainAppInitialized = false
29+
30+
fun setMainAppInitialized() {
31+
mainAppInitialized = true
32+
Log.log(logger::trace, "main app initialized , thread={}", Thread.currentThread().name)
33+
}
34+
35+
36+
/**
37+
* return null on success.
38+
* error message on failure
39+
*/
40+
fun performAction(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {
41+
try {
42+
43+
val action = getActionFromParameters(parameters) ?: return "DigmaProtocolCommand no action in request"
44+
45+
Log.log(logger::trace, "perform action {}, thread {}", action, Thread.currentThread().name)
46+
47+
return when (action) {
48+
ACTION_SHOW_ASSET_PARAM_NAME -> {
49+
showAsset(project, parameters, waitForJcef)
50+
}
51+
52+
ACTION_SHOW_ASSETS_TAB_PARAM_NAME -> {
53+
showAssetTab(project, action, waitForJcef)
54+
}
55+
56+
else -> {
57+
"DigmaProtocolCommand unknown action in request $action"
58+
}
59+
}
60+
61+
62+
} catch (e: Throwable) {
63+
ErrorReporter.getInstance().reportError("DigmaProtocolApi.performAction", e)
64+
return "DigmaProtocolCommand error $e"
65+
}
66+
}
67+
68+
69+
private fun showAsset(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {
70+
71+
val codeObjectId = getCodeObjectIdFromParameters(parameters)
72+
val environmentId = getEnvironmentIdFromParameters(parameters)
73+
74+
Log.log(
75+
logger::trace,
76+
"showing asset, codeObjectId='{}', environment='{}', thread='{}'",
77+
codeObjectId,
78+
environmentId,
79+
Thread.currentThread().name
80+
)
81+
82+
if (codeObjectId == null) {
83+
return "DigmaProtocolCommand no code object id in request"
84+
}
85+
86+
cs.launch {
87+
88+
if (waitForJcef) {
89+
waitForJcef()
90+
}
91+
92+
val scope = SpanScope(codeObjectId)
93+
94+
ScopeManager.getInstance(project).changeScope(scope, null, environmentId)
95+
}
96+
97+
return null
98+
}
99+
100+
101+
private fun showAssetTab(project: Project, action: String, waitForJcef: Boolean): String? {
102+
cs.launch {
103+
104+
if (waitForJcef) {
105+
waitForJcef()
106+
}
107+
108+
project.messageBus.syncPublisher(ProtocolCommandEvent.PROTOCOL_COMMAND_TOPIC).protocolCommand(action)
109+
}
110+
return null
111+
}
112+
113+
114+
private suspend fun waitForJcef() {
115+
116+
try {
117+
withTimeout(5000) {
118+
while (!mainAppInitialized) {
119+
delay(100)
120+
}
121+
}
122+
123+
} catch (e: TimeoutCancellationException) {
124+
//ignore
125+
}
126+
127+
//wait another second , it seems to be necessary to let jcef completely initialize
128+
delay(1000)
129+
}
130+
131+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package org.digma.intellij.plugin.protocol
2+
3+
import com.intellij.ide.RecentProjectListActionProvider
4+
import com.intellij.ide.RecentProjectsManager
5+
import com.intellij.ide.RecentProjectsManagerBase
6+
import com.intellij.ide.ReopenProjectAction
7+
import com.intellij.ide.impl.OpenProjectTask
8+
import com.intellij.navigation.PROJECT_NAME_KEY
9+
import com.intellij.navigation.ProtocolOpenProjectResult
10+
import com.intellij.navigation.openProject
11+
import com.intellij.openapi.application.ApplicationManager
12+
import com.intellij.openapi.application.JBProtocolCommand
13+
import com.intellij.openapi.components.service
14+
import com.intellij.openapi.diagnostic.Logger
15+
import org.digma.intellij.plugin.common.findActiveProject
16+
import org.digma.intellij.plugin.errorreporting.ErrorReporter
17+
import org.digma.intellij.plugin.log.Log
18+
import org.digma.intellij.plugin.ui.ToolWindowShower
19+
import java.nio.file.Path
20+
21+
const val DIGMA_COMMAND = "digma"
22+
const val DIGMA_PLUGIN_TARGET = "plugin"
23+
24+
25+
/*
26+
show digma settings
27+
"jetbrains://idea/settings?name=Digma Plugin"
28+
29+
show code object id not encoded
30+
"jetbrains://idea/digma/plugin?action=showAsset&codeObjectId=span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}&environmentId=LOCAL#ID#42E6067A-E755-4211-BA0F-B2F84BA8B065"
31+
32+
show code object id encoded
33+
"jetbrains://idea/digma/plugin?action=showAsset&codeObjectId=span%3Aio.opentelemetry.tomcat-10.0%24_%24HTTP%20GET%20%2Fowners%2F%7BownerId%7D&environmentId=LOCAL%23ID%2342E6067A-E755-4211-BA0F-B2F84BA8B065"
34+
35+
*/
36+
37+
class DigmaProtocolCommand : JBProtocolCommand(DIGMA_COMMAND) {
38+
private val logger: Logger = Logger.getInstance(this::class.java)
39+
40+
override suspend fun execute(target: String?, parameters: Map<String, String>, fragment: String?): String? {
41+
try {
42+
return executeImpl(target, parameters, fragment)
43+
} catch (e: Throwable) {
44+
ErrorReporter.getInstance().reportError("DigmaProtocolCommand.execute", e)
45+
return "Error $e"
46+
}
47+
}
48+
49+
//returns null on success, message on failure
50+
private suspend fun executeImpl(target: String?, parameters: Map<String, String>, fragment: String?): String? {
51+
52+
if (target != DIGMA_PLUGIN_TARGET) {
53+
return "DigmaProtocolCommand Supports Only Plugin Target"
54+
}
55+
56+
Log.log(
57+
logger::trace,
58+
"execute called with target={},fragment={},parameters={}, thread={}",
59+
target,
60+
fragment.toString(),
61+
parameters.toUrlQueryString(),
62+
Thread.currentThread().name
63+
)
64+
65+
66+
var project = if (parameters.containsKey(PROJECT_NAME_KEY)) {
67+
when (val openProjectResult = openProject(parameters)) {
68+
is ProtocolOpenProjectResult.Success -> openProjectResult.project
69+
is ProtocolOpenProjectResult.Error -> return openProjectResult.message
70+
}
71+
} else {
72+
findActiveProject()
73+
}
74+
75+
if (project == null) {
76+
val recentProjectPath =
77+
RecentProjectListActionProvider.getInstance().getActions().asSequence().filterIsInstance(ReopenProjectAction::class.java)
78+
.firstOrNull()?.projectPath ?: RecentProjectsManager.getInstance().lastProjectCreationLocation
79+
if (recentProjectPath != null) {
80+
@Suppress("UnstableApiUsage")
81+
project = RecentProjectsManagerBase.getInstanceEx().openProject(Path.of(recentProjectPath), OpenProjectTask())
82+
}
83+
}
84+
85+
86+
if (project != null) {
87+
88+
Log.log(logger::trace, "got project {}", project.name)
89+
90+
val action = getActionFromParameters(parameters)
91+
?: return "DigmaProtocolCommand no action in request"
92+
93+
94+
var waitForJcef = false
95+
if (!ToolWindowShower.getInstance(project).isToolWindowVisible) {
96+
waitForJcef = true
97+
ApplicationManager.getApplication().invokeAndWait {
98+
Log.log(logger::trace, "showing tool window")
99+
ToolWindowShower.getInstance(project).showToolWindow()
100+
Log.log(logger::trace, "tool window shown")
101+
}
102+
}
103+
104+
Log.log(logger::trace, "executing action {}", action)
105+
val result = project.service<DigmaProtocolApi>().performAction(project, parameters, waitForJcef)
106+
Log.log(logger::trace, "after execute action {}", action)
107+
return result
108+
}
109+
110+
return "DigmaProtocolCommand can not open any project"
111+
112+
}
113+
114+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.digma.intellij.plugin.protocol
2+
3+
import com.intellij.util.messages.Topic
4+
5+
6+
/**
7+
* this is an application event that should fire when we change the api client,
8+
* usually when user changes the api url in settings.
9+
*/
10+
fun interface ProtocolCommandEvent {
11+
12+
13+
companion object {
14+
@JvmStatic
15+
@Topic.ProjectLevel
16+
val PROTOCOL_COMMAND_TOPIC: Topic<ProtocolCommandEvent> = Topic.create(
17+
"PROTOCOL COMMAND",
18+
ProtocolCommandEvent::class.java
19+
)
20+
}
21+
22+
fun protocolCommand(action: String)
23+
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.digma.intellij.plugin.protocol
2+
3+
import com.intellij.util.text.nullize
4+
import java.net.URLEncoder
5+
6+
7+
fun Map<String, String>.toUrlQueryString() =
8+
this.map {(k,v) -> "${URLEncoder.encode(k, "UTF-8")}=${URLEncoder.encode(v, "UTF-8")}" }
9+
.joinToString("&")
10+
11+
12+
fun getActionFromParameters(parameters: Map<String, String>):String?{
13+
return parameters[ACTION_PARAM_NAME]?.nullize(nullizeSpaces = true)
14+
}
15+
16+
17+
fun getCodeObjectIdFromParameters(parameters: Map<String, String>):String?{
18+
return parameters[CODE_OBJECT_ID_PARAM_NAME]?.nullize(nullizeSpaces = true)
19+
}
20+
21+
fun getEnvironmentIdFromParameters(parameters: Map<String, String>):String?{
22+
return parameters[ENVIRONMENT_ID_PARAM_NAME]?.nullize(nullizeSpaces = true)
23+
}
24+
25+

src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppMessageRouterHandler.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package org.digma.intellij.plugin.ui.mainapp
22

33
import com.fasterxml.jackson.databind.JsonNode
4+
import com.intellij.openapi.components.service
45
import com.intellij.openapi.project.Project
56
import org.cef.browser.CefBrowser
67
import org.digma.intellij.plugin.analytics.AnalyticsServiceException
78
import org.digma.intellij.plugin.log.Log
9+
import org.digma.intellij.plugin.protocol.DigmaProtocolApi
810
import org.digma.intellij.plugin.ui.assets.AssetsMessageRouterHandler
911
import org.digma.intellij.plugin.ui.errors.ErrorsMessageRouterHandler
1012
import org.digma.intellij.plugin.ui.highlights.HighlightsMessageRouterHandler
@@ -66,6 +68,7 @@ class MainAppMessageRouterHandler(project: Project) : BaseMessageRouterHandler(p
6668
} catch (e: AnalyticsServiceException) {
6769
Log.warnWithException(logger, e, "error getting backend info")
6870
}
71+
project.service<DigmaProtocolApi>().setMainAppInitialized()
6972
}
7073

7174
}

src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppService.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import com.intellij.openapi.util.Disposer
99
import kotlinx.datetime.Clock
1010
import kotlinx.datetime.toKotlinInstant
1111
import org.digma.intellij.plugin.engagement.EngagementScoreService
12+
import org.digma.intellij.plugin.log.Log
1213
import org.digma.intellij.plugin.persistence.PersistenceService
14+
import org.digma.intellij.plugin.protocol.ACTION_SHOW_ASSETS_TAB_PARAM_NAME
15+
import org.digma.intellij.plugin.protocol.ProtocolCommandEvent
1316
import org.digma.intellij.plugin.scheduling.disposingPeriodicTask
1417
import org.digma.intellij.plugin.ui.jcef.JCefComponent
1518
import org.digma.intellij.plugin.ui.jcef.sendGenericPluginEvent
@@ -26,6 +29,22 @@ class MainAppService(private val project: Project) : Disposable {
2629

2730
init {
2831

32+
project.messageBus.connect(this).subscribe(ProtocolCommandEvent.PROTOCOL_COMMAND_TOPIC,
33+
ProtocolCommandEvent { action ->
34+
35+
Log.log(logger::trace,"got protocol command {}",action)
36+
37+
jCefComponent?.let { jcefComp ->
38+
when (action) {
39+
ACTION_SHOW_ASSETS_TAB_PARAM_NAME -> {
40+
Log.log(logger::trace,"sending generic event to open assets page")
41+
sendGenericPluginEvent(project, jcefComp.jbCefBrowser.cefBrowser, "first asset notification link click")
42+
}
43+
}
44+
} ?: Log.log(logger::trace,"jcef component is null")
45+
})
46+
47+
2948
if ( //todo: check if user clicked don't show again
3049
!PersistenceService.getInstance().isUserRequestedEarlyAccess() &&
3150
PersistenceService.getInstance().getUserEmail() == null && PersistenceService.getInstance().getUserRegistrationEmail() == null
@@ -37,8 +56,8 @@ class MainAppService(private val project: Project) : Disposable {
3756
val installTime = PersistenceService.getInstance().getFirstTimePluginLoadedTimestamp()
3857
if (installTime != null) {
3958
if (Clock.System.now() > (installTime.toKotlinInstant().plus(14.days))) {
40-
if (EngagementScoreService.getInstance().getLatestRegisteredActiveDays() >= 5){
41-
sendGenericPluginEvent(project,jcefComp.jbCefBrowser.cefBrowser,"SHOW_EARLY_ACCESS_PROMOTION")
59+
if (EngagementScoreService.getInstance().getLatestRegisteredActiveDays() >= 5) {
60+
sendGenericPluginEvent(project, jcefComp.jbCefBrowser.cefBrowser, "SHOW_EARLY_ACCESS_PROMOTION")
4261
Disposer.dispose(disposable)
4362
}
4463
}

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142

143143
<postStartupActivity implementation="org.digma.intellij.plugin.posthog.CountingProjectActivity"/>
144144

145+
<jbProtocolCommand implementation="org.digma.intellij.plugin.protocol.DigmaProtocolCommand"/>
145146

146147
</extensions>
147148

0 commit comments

Comments
 (0)