Skip to content

Commit

Permalink
Merge pull request #2539 from digma-ai/protocol-commands
Browse files Browse the repository at this point in the history
Protocol commands Closes #2511
  • Loading branch information
shalom938 authored Sep 25, 2024
2 parents ce1b873 + 916a204 commit 5455de3
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.digma.intellij.plugin.protocol

import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.digma.intellij.plugin.common.DisposableAdaptor
import org.digma.intellij.plugin.errorreporting.ErrorReporter
import org.digma.intellij.plugin.log.Log
import org.digma.intellij.plugin.scope.ScopeManager
import org.digma.intellij.plugin.scope.SpanScope

const val ACTION_PARAM_NAME = "action"
const val ACTION_SHOW_ASSET_PARAM_NAME = "showAsset"
const val CODE_OBJECT_ID_PARAM_NAME = "codeObjectId"
const val ENVIRONMENT_ID_PARAM_NAME = "environmentId"
const val ACTION_SHOW_ASSETS_TAB_PARAM_NAME = "showAssetsTab"

@Service(Service.Level.PROJECT)
class DigmaProtocolApi(val cs: CoroutineScope) : DisposableAdaptor {

private val logger: Logger = Logger.getInstance(this::class.java)

/**
* return null on success.
* error message on failure
*/
fun performAction(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {
try {


val action = getActionFromParameters(parameters) ?: return "DigmaProtocolCommand no action in request"

Log.log(logger::trace, "perform action {}, thread {}", action, Thread.currentThread().name)

return when (action) {
ACTION_SHOW_ASSET_PARAM_NAME -> {
showAsset(project, parameters, waitForJcef)
}

ACTION_SHOW_ASSETS_TAB_PARAM_NAME -> {
showAssetTab(project, action, waitForJcef)
}

else -> {
"DigmaProtocolCommand unknown action in request $action"
}
}


} catch (e: Throwable) {
ErrorReporter.getInstance().reportError("DigmaProtocolApi.performAction", e)
return "DigmaProtocolCommand error $e"
}
}


private fun showAsset(project: Project, parameters: Map<String, String>, waitForJcef: Boolean): String? {

val codeObjectId = getCodeObjectIdFromParameters(parameters)
val environmentId = getEnvironmentIdFromParameters(parameters)

Log.log(
logger::trace,
"showing asset, codeObjectId='{}', environment='{}', thread='{}'",
codeObjectId,
environmentId,
Thread.currentThread().name
)

if (codeObjectId == null) {
return "DigmaProtocolCommand no code object id in request"
}

cs.launch {
if (waitForJcef) {
delay(5000)
}

val scope = SpanScope(codeObjectId)

ScopeManager.getInstance(project).changeScope(scope, null, environmentId)
}

return null
}


private fun showAssetTab(project: Project, action: String, waitForJcef: Boolean): String? {
cs.launch {
if (waitForJcef) {
delay(5000)
}
project.messageBus.syncPublisher(ProtocolCommandEvent.PROTOCOL_COMMAND_TOPIC).protocolCommand(action)
}
return null
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.digma.intellij.plugin.protocol

import com.intellij.ide.RecentProjectListActionProvider
import com.intellij.ide.RecentProjectsManager
import com.intellij.ide.RecentProjectsManagerBase
import com.intellij.ide.ReopenProjectAction
import com.intellij.ide.impl.OpenProjectTask
import com.intellij.navigation.PROJECT_NAME_KEY
import com.intellij.navigation.ProtocolOpenProjectResult
import com.intellij.navigation.openProject
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.JBProtocolCommand
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import org.digma.intellij.plugin.common.findActiveProject
import org.digma.intellij.plugin.errorreporting.ErrorReporter
import org.digma.intellij.plugin.log.Log
import org.digma.intellij.plugin.ui.ToolWindowShower
import java.nio.file.Path

const val DIGMA_COMMAND = "digma"
const val DIGMA_PLUGIN_TARGET = "plugin"


/*
show digma settings
"jetbrains://idea/settings?name=Digma Plugin"
show code object id not encoded
"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"
show code object id encoded
"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"
*/

class DigmaProtocolCommand : JBProtocolCommand(DIGMA_COMMAND) {
private val logger: Logger = Logger.getInstance(this::class.java)

override suspend fun execute(target: String?, parameters: Map<String, String>, fragment: String?): String? {
try {
return executeImpl(target, parameters, fragment)
} catch (e: Throwable) {
ErrorReporter.getInstance().reportError("DigmaProtocolCommand.execute", e)
return "Error $e"
}
}

//returns null on success, message on failure
private suspend fun executeImpl(target: String?, parameters: Map<String, String>, fragment: String?): String? {

if (target != DIGMA_PLUGIN_TARGET) {
return "DigmaProtocolCommand Supports Only Plugin Target"
}

Log.log(
logger::trace,
"execute called with target={},fragment={},parameters={}, thread={}",
target,
fragment.toString(),
parameters.toUrlQueryString(),
Thread.currentThread().name
)


var project = if (parameters.containsKey(PROJECT_NAME_KEY)) {
when (val openProjectResult = openProject(parameters)) {
is ProtocolOpenProjectResult.Success -> openProjectResult.project
is ProtocolOpenProjectResult.Error -> return openProjectResult.message
}
} else {
findActiveProject()
}

if (project == null) {
val recentProjectPath =
RecentProjectListActionProvider.getInstance().getActions().asSequence().filterIsInstance(ReopenProjectAction::class.java)
.firstOrNull()?.projectPath ?: RecentProjectsManager.getInstance().lastProjectCreationLocation
if (recentProjectPath != null) {
@Suppress("UnstableApiUsage")
project = RecentProjectsManagerBase.getInstanceEx().openProject(Path.of(recentProjectPath), OpenProjectTask())
}
}


if (project != null) {

Log.log(logger::trace, "got project {}", project.name)

val action = getActionFromParameters(parameters)
?: return "DigmaProtocolCommand no action in request"


var waitForJcef = false
if (!ToolWindowShower.getInstance(project).isToolWindowVisible) {
waitForJcef = true
ApplicationManager.getApplication().invokeAndWait {
Log.log(logger::trace, "showing tool window")
ToolWindowShower.getInstance(project).showToolWindow()
Log.log(logger::trace, "tool window shown")
}
}

Log.log(logger::trace, "executing action {}", action)
val result = project.service<DigmaProtocolApi>().performAction(project, parameters, waitForJcef)
Log.log(logger::trace, "after execute action {}", action)
return result
}

return "DigmaProtocolCommand can not open any project"

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.digma.intellij.plugin.protocol

import com.intellij.util.messages.Topic


/**
* this is an application event that should fire when we change the api client,
* usually when user changes the api url in settings.
*/
fun interface ProtocolCommandEvent {


companion object {
@JvmStatic
@Topic.ProjectLevel
val PROTOCOL_COMMAND_TOPIC: Topic<ProtocolCommandEvent> = Topic.create(
"PROTOCOL COMMAND",
ProtocolCommandEvent::class.java
)
}

fun protocolCommand(action: String)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.digma.intellij.plugin.protocol

import com.intellij.util.text.nullize
import java.net.URLEncoder


fun Map<String, String>.toUrlQueryString() =
this.map {(k,v) -> "${URLEncoder.encode(k, "UTF-8")}=${URLEncoder.encode(v, "UTF-8")}" }
.joinToString("&")


fun getActionFromParameters(parameters: Map<String, String>):String?{
return parameters[ACTION_PARAM_NAME]?.nullize(nullizeSpaces = true)
}


fun getCodeObjectIdFromParameters(parameters: Map<String, String>):String?{
return parameters[CODE_OBJECT_ID_PARAM_NAME]?.nullize(nullizeSpaces = true)
}

fun getEnvironmentIdFromParameters(parameters: Map<String, String>):String?{
return parameters[ENVIRONMENT_ID_PARAM_NAME]?.nullize(nullizeSpaces = true)
}


Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import com.intellij.openapi.util.Disposer
import kotlinx.datetime.Clock
import kotlinx.datetime.toKotlinInstant
import org.digma.intellij.plugin.engagement.EngagementScoreService
import org.digma.intellij.plugin.log.Log
import org.digma.intellij.plugin.persistence.PersistenceService
import org.digma.intellij.plugin.protocol.ACTION_SHOW_ASSETS_TAB_PARAM_NAME
import org.digma.intellij.plugin.protocol.ProtocolCommandEvent
import org.digma.intellij.plugin.scheduling.disposingPeriodicTask
import org.digma.intellij.plugin.ui.jcef.JCefComponent
import org.digma.intellij.plugin.ui.jcef.sendGenericPluginEvent
Expand All @@ -26,6 +29,22 @@ class MainAppService(private val project: Project) : Disposable {

init {

project.messageBus.connect(this).subscribe(ProtocolCommandEvent.PROTOCOL_COMMAND_TOPIC,
ProtocolCommandEvent { action ->

Log.log(logger::trace,"got protocol command {}",action)

jCefComponent?.let { jcefComp ->
when (action) {
ACTION_SHOW_ASSETS_TAB_PARAM_NAME -> {
Log.log(logger::trace,"sending generic event to open assets page")
sendGenericPluginEvent(project, jcefComp.jbCefBrowser.cefBrowser, "first asset notification link click")
}
}
} ?: Log.log(logger::trace,"jcef component is null")
})


if ( //todo: check if user clicked don't show again
!PersistenceService.getInstance().isUserRequestedEarlyAccess() &&
PersistenceService.getInstance().getUserEmail() == null && PersistenceService.getInstance().getUserRegistrationEmail() == null
Expand All @@ -37,8 +56,8 @@ class MainAppService(private val project: Project) : Disposable {
val installTime = PersistenceService.getInstance().getFirstTimePluginLoadedTimestamp()
if (installTime != null) {
if (Clock.System.now() > (installTime.toKotlinInstant().plus(14.days))) {
if (EngagementScoreService.getInstance().getLatestRegisteredActiveDays() >= 5){
sendGenericPluginEvent(project,jcefComp.jbCefBrowser.cefBrowser,"SHOW_EARLY_ACCESS_PROMOTION")
if (EngagementScoreService.getInstance().getLatestRegisteredActiveDays() >= 5) {
sendGenericPluginEvent(project, jcefComp.jbCefBrowser.cefBrowser, "SHOW_EARLY_ACCESS_PROMOTION")
Disposer.dispose(disposable)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@

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

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

</extensions>

Expand Down

0 comments on commit 5455de3

Please sign in to comment.