Skip to content

Commit 5455de3

Browse files
authored
Merge pull request #2539 from digma-ai/protocol-commands
Protocol commands Closes #2511
2 parents ce1b873 + 916a204 commit 5455de3

File tree

6 files changed

+287
-2
lines changed

6 files changed

+287
-2
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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.delay
8+
import kotlinx.coroutines.launch
9+
import org.digma.intellij.plugin.common.DisposableAdaptor
10+
import org.digma.intellij.plugin.errorreporting.ErrorReporter
11+
import org.digma.intellij.plugin.log.Log
12+
import org.digma.intellij.plugin.scope.ScopeManager
13+
import org.digma.intellij.plugin.scope.SpanScope
14+
15+
const val ACTION_PARAM_NAME = "action"
16+
const val ACTION_SHOW_ASSET_PARAM_NAME = "showAsset"
17+
const val CODE_OBJECT_ID_PARAM_NAME = "codeObjectId"
18+
const val ENVIRONMENT_ID_PARAM_NAME = "environmentId"
19+
const val ACTION_SHOW_ASSETS_TAB_PARAM_NAME = "showAssetsTab"
20+
21+
@Service(Service.Level.PROJECT)
22+
class DigmaProtocolApi(val cs: CoroutineScope) : DisposableAdaptor {
23+
24+
private val logger: Logger = Logger.getInstance(this::class.java)
25+
26+
/**
27+
* return null on success.
28+
* error message on failure
29+
*/
30+
fun performAction(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {
31+
try {
32+
33+
34+
val action = getActionFromParameters(parameters) ?: return "DigmaProtocolCommand no action in request"
35+
36+
Log.log(logger::trace, "perform action {}, thread {}", action, Thread.currentThread().name)
37+
38+
return when (action) {
39+
ACTION_SHOW_ASSET_PARAM_NAME -> {
40+
showAsset(project, parameters, waitForJcef)
41+
}
42+
43+
ACTION_SHOW_ASSETS_TAB_PARAM_NAME -> {
44+
showAssetTab(project, action, waitForJcef)
45+
}
46+
47+
else -> {
48+
"DigmaProtocolCommand unknown action in request $action"
49+
}
50+
}
51+
52+
53+
} catch (e: Throwable) {
54+
ErrorReporter.getInstance().reportError("DigmaProtocolApi.performAction", e)
55+
return "DigmaProtocolCommand error $e"
56+
}
57+
}
58+
59+
60+
private fun showAsset(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {
61+
62+
val codeObjectId = getCodeObjectIdFromParameters(parameters)
63+
val environmentId = getEnvironmentIdFromParameters(parameters)
64+
65+
Log.log(
66+
logger::trace,
67+
"showing asset, codeObjectId='{}', environment='{}', thread='{}'",
68+
codeObjectId,
69+
environmentId,
70+
Thread.currentThread().name
71+
)
72+
73+
if (codeObjectId == null) {
74+
return "DigmaProtocolCommand no code object id in request"
75+
}
76+
77+
cs.launch {
78+
if (waitForJcef) {
79+
delay(5000)
80+
}
81+
82+
val scope = SpanScope(codeObjectId)
83+
84+
ScopeManager.getInstance(project).changeScope(scope, null, environmentId)
85+
}
86+
87+
return null
88+
}
89+
90+
91+
private fun showAssetTab(project: Project, action: String, waitForJcef: Boolean): String? {
92+
cs.launch {
93+
if (waitForJcef) {
94+
delay(5000)
95+
}
96+
project.messageBus.syncPublisher(ProtocolCommandEvent.PROTOCOL_COMMAND_TOPIC).protocolCommand(action)
97+
}
98+
return null
99+
}
100+
101+
102+
}
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/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)