diff --git a/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsProvider.java b/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsProvider.java index 09abdfe2d..beb37c55b 100644 --- a/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsProvider.java +++ b/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsProvider.java @@ -5,7 +5,7 @@ import org.digma.intellij.plugin.model.rest.assets.AssetDisplayInfo; import org.digma.intellij.plugin.model.rest.codelens.*; import org.digma.intellij.plugin.model.rest.codespans.CodeContextSpans; -import org.digma.intellij.plugin.model.rest.common.SpanHistogramQuery; +import org.digma.intellij.plugin.model.rest.common.*; import org.digma.intellij.plugin.model.rest.debugger.DebuggerEventRequest; import org.digma.intellij.plugin.model.rest.env.*; import org.digma.intellij.plugin.model.rest.environment.Env; @@ -172,4 +172,6 @@ public interface AnalyticsProvider extends Closeable { List getSpanEnvironmentsStats(String spanCodeObjectId); DiscoveredDataResponse getDiscoveredData(); + + SpanInfoByUid resolveSpanByUid(String uid); } diff --git a/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java b/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java index 12333cb4a..02427b39e 100644 --- a/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java +++ b/analytics-provider/src/main/java/org/digma/intellij/plugin/analytics/RestAnalyticsProvider.java @@ -11,7 +11,7 @@ import org.digma.intellij.plugin.model.rest.assets.AssetDisplayInfo; import org.digma.intellij.plugin.model.rest.codelens.*; import org.digma.intellij.plugin.model.rest.codespans.CodeContextSpans; -import org.digma.intellij.plugin.model.rest.common.SpanHistogramQuery; +import org.digma.intellij.plugin.model.rest.common.*; import org.digma.intellij.plugin.model.rest.debugger.DebuggerEventRequest; import org.digma.intellij.plugin.model.rest.env.*; import org.digma.intellij.plugin.model.rest.environment.Env; @@ -489,6 +489,11 @@ public DiscoveredDataResponse getDiscoveredData() { return execute(client.analyticsProvider::getDiscoveredData); } + @Override + public SpanInfoByUid resolveSpanByUid(String uid){ + return execute(() -> client.analyticsProvider.resolveSpanByUid(uid)); + } + @Override public HttpResponse lowLevelCall(HttpRequest request) { @@ -1284,5 +1289,11 @@ Call setInsightCustomStartTime( }) @GET("CodeAnalytics/discovered-data") Call getDiscoveredData(); + + @Headers({ + "Content-Type:application/json" + }) + @GET("spans/spanCodeObjectId/{uid}") + Call resolveSpanByUid(@Path("uid") String uid); } } diff --git a/gradle.properties b/gradle.properties index 57e673d36..2c652d98b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,11 +9,12 @@ pluginRepositoryUrl=https://github.com/digma-ai/digma-intellij-plugin.git ##change buildProfile to load the profile dependencies in the IDE. refresh gradle after change. ##it is necessary when need to debug something in a specific version of intellij. changing the profile here ## will load that version's dependencies to the IDE -#buildProfile=p231 - its the default so not necessary +#buildProfile=p231 - it's the default #buildProfile=p232 #buildProfile=p233 #buildProfile=p241 #buildProfile=p242 +#buildProfile=p243 ##build with real ide, will load the ide dependencies in the project. refresh gradle after change. ##its is necessary when need to debug something for a specific IDE diff --git a/ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java b/ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java index b2bbad6f5..89ca819c1 100644 --- a/ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java +++ b/ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java @@ -5,6 +5,7 @@ import com.intellij.openapi.project.Project; import com.intellij.ui.JBColor; import org.apache.commons.lang3.time.StopWatch; +import org.apache.maven.artifact.versioning.ComparableVersion; import org.digma.intellij.plugin.auth.AuthManager; import org.digma.intellij.plugin.common.*; import org.digma.intellij.plugin.errorreporting.ErrorReporter; @@ -14,7 +15,7 @@ import org.digma.intellij.plugin.model.rest.assets.AssetDisplayInfo; import org.digma.intellij.plugin.model.rest.codelens.*; import org.digma.intellij.plugin.model.rest.codespans.CodeContextSpans; -import org.digma.intellij.plugin.model.rest.common.SpanHistogramQuery; +import org.digma.intellij.plugin.model.rest.common.*; import org.digma.intellij.plugin.model.rest.debugger.DebuggerEventRequest; import org.digma.intellij.plugin.model.rest.env.*; import org.digma.intellij.plugin.model.rest.environment.Env; @@ -32,7 +33,7 @@ import org.digma.intellij.plugin.persistence.PersistenceService; import org.digma.intellij.plugin.posthog.ActivityMonitor; import org.digma.intellij.plugin.ui.*; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.*; import java.io.Closeable; import java.lang.reflect.*; @@ -379,7 +380,7 @@ public String getIssuesReportStats(@NotNull Map queryParams) thr analyticsProviderProxy.getIssuesReportStats(queryParams)); } - public String getServiceReport(@NotNull String queryParams) throws AnalyticsServiceException { + public String getServiceReport(@NotNull String queryParams) throws AnalyticsServiceException { return executeCatching(() -> analyticsProviderProxy.getServiceReport(queryParams)); } @@ -560,6 +561,34 @@ public HttpResponse lowLevelCall(HttpRequest request) throws AnalyticsServiceExc return executeCatching(() -> analyticsProviderProxy.lowLevelCall(request)); } + + @Nullable + public SpanInfoByUid resolveSpanByUid(String uid) throws AnalyticsServiceException { + + if (backendVersionOlderThen("0.3.155")) { + return null; + } + + return executeCatching(() -> analyticsProviderProxy.resolveSpanByUid(uid)); + + } + + + private boolean backendVersionOlderThen(String version) { + String backendVersion = BackendInfoHolder.getInstance(project).getAbout().getApplicationVersion(); + + //dev environment may return unknown + if ("unknown".equalsIgnoreCase(backendVersion)) { + return false; + } + + ComparableVersion currentBackendVersion = new ComparableVersion(backendVersion); + ComparableVersion requiredBackendVersion = new ComparableVersion(version); + + return currentBackendVersion.compareTo(requiredBackendVersion) < 0; + } + + @Override public void dispose() { try { diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendUtils.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendUtils.kt index f19a771b3..e752c7673 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendUtils.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendUtils.kt @@ -1,11 +1,33 @@ package org.digma.intellij.plugin.analytics import com.intellij.openapi.project.Project +import org.digma.intellij.plugin.common.findActiveProject fun isCentralized(project: Project): Boolean { return BackendInfoHolder.getInstance(project).isCentralized() } -fun getVersion(project: Project): String { - return BackendInfoHolder.getInstance(project).getAbout()?.applicationVersion ?: "unknown"; -} \ No newline at end of file +fun getBackendVersion(project: Project): String { + return BackendInfoHolder.getInstance(project).getAbout()?.applicationVersion ?: "unknown" +} + +fun getBackendVersion(): String { + val project = findActiveProject() + return project?.let { + BackendInfoHolder.getInstance(it).getAbout()?.applicationVersion ?: "unknown" + } ?: "unknown" +} + +fun getBackendDeploymentType(): String { + val project = findActiveProject() + return project?.let { + BackendInfoHolder.getInstance(it).getAbout()?.deploymentType?.name ?: "unknown" + } ?: "unknown" +} + +fun isCentralized(): Boolean { + val project = findActiveProject() + return project?.let { + BackendInfoHolder.getInstance(it).getAbout()?.isCentralize ?: false + } ?:false +} diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AboutHttpService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AboutHttpService.kt new file mode 100644 index 000000000..230dd419d --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AboutHttpService.kt @@ -0,0 +1,79 @@ +package org.digma.intellij.plugin.api + +import com.fasterxml.jackson.core.JsonFactory +import com.fasterxml.jackson.core.JsonGenerator +import com.intellij.openapi.application.ApplicationInfo +import com.intellij.openapi.application.ApplicationNamesInfo +import com.intellij.openapi.project.ProjectManager +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream +import com.intellij.util.PlatformUtils +import com.intellij.util.io.jackson.array +import com.intellij.util.io.jackson.obj +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.analytics.getBackendDeploymentType +import org.digma.intellij.plugin.analytics.getBackendVersion +import org.digma.intellij.plugin.analytics.isCentralized +import org.digma.intellij.plugin.semanticversion.SemanticVersionUtil +import java.io.OutputStream + +/** + * {get} /digma/about The application info + */ +internal class AboutHttpService : AbstractHttpService() { + + override fun getServiceName() = "digma/about" + + + override fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String? { + val byteOut = BufferExposingByteArrayOutputStream() + writeApplicationInfoJson(byteOut) + send(byteOut, request, context) + return null + } + + + private fun writeApplicationInfoJson(out: OutputStream) { + JsonFactory().createGenerator(out).useDefaultPrettyPrinter().use { writer -> + writer.obj { + writeAboutJson(writer) + } + } + } + + + private fun writeAboutJson(writer: JsonGenerator) { + writer.writeStringField("source", "Digma Plugin") + var appName = ApplicationInfo.getInstance().fullApplicationName + @Suppress("UnstableApiUsage") + if (!PlatformUtils.isIdeaUltimate()) { + val productName = ApplicationNamesInfo.getInstance().productName + appName = appName + .replace("$productName ($productName)", productName) + .removePrefix("JetBrains ") + } + writer.writeStringField("name", appName) + writer.writeStringField("productName", ApplicationNamesInfo.getInstance().productName) + writer.writeStringField("edition", ApplicationNamesInfo.getInstance().editionName) + + val build = ApplicationInfo.getInstance().build + writer.writeNumberField("baselineVersion", build.baselineVersion) + if (!build.isSnapshot) { + writer.writeStringField("buildNumber", build.asStringWithoutProductCode()) + } + + writer.writeStringField("pluginVersion", SemanticVersionUtil.getPluginVersionWithoutBuildNumberAndPreRelease("unknown")) + writer.writeStringField("backendVersion", getBackendVersion()) + writer.writeStringField("backendDeploymentType", getBackendDeploymentType()) + writer.writeBooleanField("isCentralized", isCentralized()) + + writer.array("openProjects") { + for (project in ProjectManager.getInstance().openProjects) { + writer.writeString(project.name) + } + } + } + +} + diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractApiCommand.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractApiCommand.kt new file mode 100644 index 000000000..ce2915b8b --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractApiCommand.kt @@ -0,0 +1,13 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import io.netty.handler.codec.http.QueryStringDecoder + +abstract class AbstractApiCommand { + + protected val logger: Logger = Logger.getInstance(this::class.java) + + abstract fun execute(project: Project, urlDecoder: QueryStringDecoder) + +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractHttpService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractHttpService.kt new file mode 100644 index 000000000..0b734317d --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/AbstractHttpService.kt @@ -0,0 +1,28 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.diagnostic.Logger +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.HttpRequest +import io.netty.handler.codec.http.QueryStringDecoder +import org.jetbrains.ide.RestService + +/** + * base http service that allows all origin hosts + */ +abstract class AbstractHttpService : RestService() { + + protected val logger: Logger = Logger.getInstance(this::class.java) + + override fun isOriginAllowed(request: HttpRequest): OriginCheckResult { + return OriginCheckResult.ALLOW + } + + override fun isHostTrusted(request: FullHttpRequest, urlDecoder: QueryStringDecoder): Boolean { + return true + } + + @Deprecated("Use {@link #isHostTrusted(FullHttpRequest, QueryStringDecoder)}", ReplaceWith("true")) + override fun isHostTrusted(request: FullHttpRequest): Boolean { + return true + } +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiProjectService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiProjectService.kt new file mode 100644 index 000000000..2b8e50d77 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiProjectService.kt @@ -0,0 +1,120 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.WindowManager +import com.intellij.ui.AppIcon +import io.netty.handler.codec.http.QueryStringDecoder +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import org.digma.intellij.plugin.common.DisposableAdaptor +import org.digma.intellij.plugin.common.EDT +import org.digma.intellij.plugin.log.Log +import org.digma.intellij.plugin.scheduling.blockingOneShotTask +import org.digma.intellij.plugin.ui.ToolWindowShower +import kotlin.time.Duration.Companion.seconds + +@Service(Service.Level.PROJECT) +class ApiProjectService(val project: Project) : DisposableAdaptor { + + private val logger: Logger = Logger.getInstance(this::class.java) + + private var mainAppInitializedTime: Instant? = null + private val delayAfterInitialize = 1.seconds + + fun setMainAppInitialized() { + if (mainAppInitializedTime == null) { + Log.log(logger::trace, "main app initialized , thread={}", Thread.currentThread().name) + mainAppInitializedTime = Clock.System.now() + } + } + + fun executeAction(action: String, urlDecoder: QueryStringDecoder) { + + val command = when (action) { + ACTION_GO_TO_HOME_PARAM_VALUE -> GoToHomeCommand() + ACTION_GO_TO_SPAN_PARAM_VALUE -> GoToSpanCommand() + ACTION_OPEN_REPORT_PARAM_VALUE -> OpenReportCommand() + else -> { + throw RuntimeException("unknown action $action") + } + } + + //open digma tool window and wait for main app to initialize + openDigma() + + //the command should run after the tool window is initialized + command.execute(project, urlDecoder) + + } + + private fun openDigma() { + + if (!ToolWindowShower.getInstance(project).isToolWindowVisible) { + ApplicationManager.getApplication().invokeAndWait { + Log.log(logger::trace, "showing tool window") + ToolWindowShower.getInstance(project).showToolWindow() + Log.log(logger::trace, "tool window shown") + } + } + + val result = blockingOneShotTask("ApiProjectService.waitForMainAppInitialize", 10.seconds.inWholeMilliseconds) { + delayAfterMainAppInitialized() + } + + if (!result) { + Log.log(logger::warn, "Digma did not initialize within 10 seconds, will try to execute the action anyway.") + } + + ideToFront() + + } + + + private fun delayAfterMainAppInitialized() { + + waitForJcefInitialize() + + //it shouldn't happen that timeToCallJcef will be null because we wait for mainAppInitializedTime to be non-null. + // unless we got timeout waiting for jcef to initialize + val timeToCallJcef = mainAppInitializedTime?.plus(delayAfterInitialize) ?: return + + Log.log(logger::trace, "waiting $delayAfterInitialize after jcef initialize") + + while (Clock.System.now() < timeToCallJcef && !Thread.currentThread().isInterrupted) { + Thread.sleep(100) + } + Log.log(logger::trace, "done waiting $delayAfterInitialize after jcef initialize") + + } + + + private fun waitForJcefInitialize() { + + if (mainAppInitializedTime != null) { + return + } + + Log.log(logger::trace, "waiting for jcef initialize") + + while (mainAppInitializedTime == null && !Thread.currentThread().isInterrupted) { + Thread.sleep(100) + } + + Log.log(logger::trace, "done waiting for jcef initialize") + } + + + private fun ideToFront() { + EDT.ensureEDT { + AppIcon.getInstance().requestAttention(project, true) + val ideFrame = WindowManager.getInstance().getIdeFrame(project) + if (ideFrame != null) { + AppIcon.getInstance().requestFocus(ideFrame) + } + } + } + +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiService.kt new file mode 100644 index 000000000..ed8641bd7 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ApiService.kt @@ -0,0 +1,33 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.Logger +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.common.DisposableAdaptor +import org.digma.intellij.plugin.log.Log +import org.jetbrains.ide.RestService.Companion.getStringParameter + +@Service(Service.Level.APP) +class ApiService : DisposableAdaptor { + + private val logger: Logger = Logger.getInstance(this::class.java) + + fun executeAction(action: String, urlDecoder: QueryStringDecoder) { + + val projectName = getStringParameter(PROJECT_NAME_PARAM_NAME, urlDecoder) + val project = projectName?.let { + findProjectOrNull(projectName) + } ?: findActiveOrRecentProject() + + if (project == null) { + Log.log(logger::warn, "could not find a project to use for api call") + throw NoProjectException("could not find project $projectName nor any other project to use") + } + + Log.log(logger::trace,"found project to use {}",project.name) + + project.service().executeAction(action, urlDecoder) + } + +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToHomeCommand.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToHomeCommand.kt new file mode 100644 index 000000000..fb2a5a947 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToHomeCommand.kt @@ -0,0 +1,37 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.project.Project +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.common.objectToJsonNode +import org.digma.intellij.plugin.log.Log +import org.digma.intellij.plugin.scope.ScopeContext +import org.digma.intellij.plugin.scope.ScopeManager +import org.jetbrains.ide.RestService.Companion.getStringParameter +import java.util.Objects + +class GoToHomeCommand : AbstractApiCommand(){ + + override fun execute(project: Project, urlDecoder: QueryStringDecoder) { + val environmentId = getStringParameter(ENVIRONMENT_ID_PARAM_NAME, urlDecoder) + val targetTab = getStringParameter(TARGET_TAB_PARAM_NAME, urlDecoder) + Objects.requireNonNull(environmentId, "environmentId parameter must not be null") + Objects.requireNonNull(targetTab, "targetTab parameter must not be null") + + Log.log( + logger::trace, + "GoToHome called with environmentId={}, targetTab={}, projectName={}, thread={}", + environmentId, + targetTab, + project.name, + Thread.currentThread().name + ) + + val contextPayload = objectToJsonNode(GoToHomeContextPayload(targetTab!!)) + val scopeContext = ScopeContext("IDE/REST_API_CALL", contextPayload) + + ScopeManager.getInstance(project).changeToHome(true, scopeContext, environmentId) + } + + + data class GoToHomeContextPayload(val targetTab: String) +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToSpanCommand.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToSpanCommand.kt new file mode 100644 index 000000000..993a82c06 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/GoToSpanCommand.kt @@ -0,0 +1,55 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.project.Project +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.analytics.AnalyticsService +import org.digma.intellij.plugin.common.objectToJsonNode +import org.digma.intellij.plugin.log.Log +import org.digma.intellij.plugin.scope.ScopeContext +import org.digma.intellij.plugin.scope.ScopeManager +import org.digma.intellij.plugin.scope.SpanScope +import org.jetbrains.ide.RestService.Companion.getStringParameter +import java.util.Objects + +class GoToSpanCommand : AbstractApiCommand() { + + override fun execute(project: Project, urlDecoder: QueryStringDecoder) { + val spanUid = getStringParameter(SPAN_UID_PARAM_NAME, urlDecoder) + val targetTab = getStringParameter(TARGET_TAB_PARAM_NAME, urlDecoder) + Objects.requireNonNull(spanUid, "spanUid parameter must not be null") + Objects.requireNonNull(targetTab, "targetTab parameter must not be null") + + Log.log( + logger::trace, + "GoToSpan called with spanUid={}, targetTab={}, projectName={}, thread={}", + spanUid, + targetTab, + project.name, + Thread.currentThread().name + ) + + + val spanInfoByUid = AnalyticsService.getInstance(project).resolveSpanByUid(spanUid) + ?: throw RuntimeException("could not find span by uid $spanUid") + + val scope = SpanScope(spanInfoByUid.spanCodeObjectId) + val contextPayload = objectToJsonNode(GoToSpanScopeContextPayload(targetTab!!)) + val scopeContext = ScopeContext("IDE/REST_API_CALL", contextPayload) + + Log.log( + logger::trace, + "calling ScopeManager.changeScope with scope='{}',scopeContext='{}',environmentId='{}', thread='{}'", + scope, + scopeContext, + spanInfoByUid.environmentId, + Thread.currentThread().name + ) + + ScopeManager.getInstance(project).changeScope(scope, scopeContext, spanInfoByUid.environmentId) + + } + + + data class GoToSpanScopeContextPayload(val targetTab: String) + +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/NoProjectException.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/NoProjectException.kt new file mode 100644 index 000000000..8b92a697a --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/NoProjectException.kt @@ -0,0 +1,3 @@ +package org.digma.intellij.plugin.api + +class NoProjectException(msg: String) : RuntimeException(msg) \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/OpenReportCommand.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/OpenReportCommand.kt new file mode 100644 index 000000000..64968eec0 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/OpenReportCommand.kt @@ -0,0 +1,22 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.project.Project +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.events.OpenDashboardRequest +import org.digma.intellij.plugin.log.Log + +class OpenReportCommand : AbstractApiCommand(){ + + override fun execute(project: Project, urlDecoder: QueryStringDecoder) { + + Log.log( + logger::trace, + "OpenReport called with projectName={}, thread={}", + project.name, + Thread.currentThread().name + ) + + project.messageBus.syncPublisher(OpenDashboardRequest.OPEN_DASHBOARD_REQUEST_TOPIC).openReportRequest("Report") + + } +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ShowAssetsHttpService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ShowAssetsHttpService.kt new file mode 100644 index 000000000..be881b5eb --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/ShowAssetsHttpService.kt @@ -0,0 +1,47 @@ +package org.digma.intellij.plugin.api + +import com.intellij.openapi.components.service +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.QueryStringDecoder +import org.digma.intellij.plugin.errorreporting.ErrorReporter +import org.digma.intellij.plugin.log.Log +import java.util.Objects + +//http://localhost:63344/api/digma/show?action=GoToSpan&spanUid=995822fa-9d0d-11ef-9d3e-0adc1d604f09&targetTab=assets&projectName=spring-petclinic +//http://localhost:63344/api/digma/show?action=GoToHome&environment_id=LOCAL%23ID%2325A68508-B8A3-4DF2-ACA3-ECDAAB203793&targetTab=assets&projectName=spring-petclinic +//http://localhost:63344/api/digma/show?action=OpenReport&projectName=spring-petclinic +class ShowAssetsHttpService : AbstractHttpService() { + + override fun getServiceName() = "digma/show" + + + override fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String? { + + try { + executeImpl(urlDecoder,request,context) + sendOk(request,context) + return null + } catch (e: Throwable) { + ErrorReporter.getInstance().reportError("ShowAssetsHttpService.execute", e) + return "Error $e" + } + + } + + private fun executeImpl(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext) { + + val action = getStringParameter(ACTION_PARAM_NAME, urlDecoder) + Objects.requireNonNull(action,"action parameter must not be null") + + Log.log( + logger::trace, + "execute called with action={} , thread={}", + action, + Thread.currentThread().name + ) + + service().executeAction(action!!,urlDecoder) + } + +} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/api-utils.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/api-utils.kt new file mode 100644 index 000000000..bfba8042e --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/api/api-utils.kt @@ -0,0 +1,51 @@ +package org.digma.intellij.plugin.api + +import com.intellij.ide.RecentProjectListActionProvider +import com.intellij.ide.RecentProjectsManager +import com.intellij.ide.ReopenProjectAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectManager +import org.digma.intellij.plugin.common.findActiveProject + +const val ACTION_PARAM_NAME = "action" +const val TARGET_TAB_PARAM_NAME = "targetTab" +const val SPAN_UID_PARAM_NAME = "spanUid" +const val ENVIRONMENT_ID_PARAM_NAME = "environment_id" +const val PROJECT_NAME_PARAM_NAME = "projectName" + +const val ACTION_GO_TO_HOME_PARAM_VALUE = "GoToHome" +const val ACTION_GO_TO_SPAN_PARAM_VALUE = "GoToSpan" +const val ACTION_OPEN_REPORT_PARAM_VALUE = "OpenReport" + + +fun findProjectOrNull(projectName: String): Project? { + + var project = ProjectManager.getInstance().openProjects.find { it.name == projectName } + if (project == null) { + val projectPath = + RecentProjectListActionProvider.getInstance().getActions().asSequence().filterIsInstance(ReopenProjectAction::class.java).firstOrNull { + it.projectName == projectName + }?.projectPath + + if (projectPath != null) { + project = ProjectManager.getInstance().loadAndOpenProject(projectPath) + } + } + + return project +} + + +fun findActiveOrRecentProject(): Project? { + return findActiveProject() ?: findRecentProject() +} + +fun findRecentProject(): Project? { + val recentProjectPath = + RecentProjectListActionProvider.getInstance().getActions().asSequence().filterIsInstance(ReopenProjectAction::class.java) + .firstOrNull()?.projectPath ?: RecentProjectsManager.getInstance().lastProjectCreationLocation + + return recentProjectPath?.let { + ProjectManager.getInstance().loadAndOpenProject(recentProjectPath) + } +} diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/events/OpenDashboardRequest.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/events/OpenDashboardRequest.kt new file mode 100644 index 000000000..78233ccfa --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/events/OpenDashboardRequest.kt @@ -0,0 +1,19 @@ +package org.digma.intellij.plugin.events + +import com.intellij.util.messages.Topic + +interface OpenDashboardRequest { + + companion object { + @JvmStatic + @Topic.ProjectLevel + val OPEN_DASHBOARD_REQUEST_TOPIC: Topic = Topic.create( + "OPEN_DASHBOARD_REQUEST", + OpenDashboardRequest::class.java + ) + } + + fun openReportRequest(dashboardName: String) + fun openDashboardRequest(dashboardName: String) + +} diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/DigmaProtocolApi.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/DigmaProtocolApi.kt index 22b57e294..44e55109f 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/DigmaProtocolApi.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/DigmaProtocolApi.kt @@ -100,7 +100,7 @@ class DigmaProtocolApi(val cs: CoroutineScope) : DisposableAdaptor { val scope = SpanScope(codeObjectId) val contextPayload = objectToJsonNode(CustomUrlScopeContextPayload(targetTab)) - val scopeContext = ScopeContext("IDE/CUSTOM_PROTOCOL_LINK_CLICKED", contextPayload) + val scopeContext = ScopeContext("IDE/REST_API_CALL", contextPayload) Log.log( logger::trace, diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/NOT_USED b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/NOT_USED new file mode 100644 index 000000000..d572947f2 --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/NOT_USED @@ -0,0 +1,3 @@ +this package is not used. +we don't use jetbrains protocol. +we keep it as example. \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/ProtocolCommandEvent.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/ProtocolCommandEvent.kt index 78a9e97dd..fe088e6bf 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/ProtocolCommandEvent.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/protocol/ProtocolCommandEvent.kt @@ -3,10 +3,7 @@ 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 { diff --git a/model/src/main/kotlin/org/digma/intellij/plugin/model/rest/common/SpanInfoByUid.kt b/model/src/main/kotlin/org/digma/intellij/plugin/model/rest/common/SpanInfoByUid.kt new file mode 100644 index 000000000..48a42a99f --- /dev/null +++ b/model/src/main/kotlin/org/digma/intellij/plugin/model/rest/common/SpanInfoByUid.kt @@ -0,0 +1,14 @@ +package org.digma.intellij.plugin.model.rest.common + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import java.beans.ConstructorProperties + +@JsonIgnoreProperties(ignoreUnknown = true) +data class SpanInfoByUid +@JsonCreator(mode = JsonCreator.Mode.PROPERTIES) +@ConstructorProperties("environmentId", "spanCodeObjectId") +constructor( + val environmentId: String, + val spanCodeObjectId: String, +) diff --git a/src/main/java/org/digma/intellij/plugin/dashboard/DashboardService.java b/src/main/java/org/digma/intellij/plugin/dashboard/DashboardService.java index 5255c6120..b4eff66f6 100644 --- a/src/main/java/org/digma/intellij/plugin/dashboard/DashboardService.java +++ b/src/main/java/org/digma/intellij/plugin/dashboard/DashboardService.java @@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project; import org.digma.intellij.plugin.analytics.*; import org.digma.intellij.plugin.common.EDT; +import org.digma.intellij.plugin.events.OpenDashboardRequest; import org.digma.intellij.plugin.reload.*; import org.jetbrains.annotations.NotNull; @@ -25,6 +26,19 @@ public final class DashboardService implements Disposable, ReloadableJCefContain public DashboardService(Project project) { this.project = project; ApplicationManager.getApplication().getService(ReloadService.class).register(this, this); + + + project.getMessageBus().connect(this).subscribe(OpenDashboardRequest.getOPEN_DASHBOARD_REQUEST_TOPIC(), new OpenDashboardRequest() { + @Override + public void openReportRequest(@NotNull String dashboardName) { + openReport(dashboardName); + } + + @Override + public void openDashboardRequest(@NotNull String dashboardName) { + openDashboard(dashboardName); + } + }); } @Override @@ -98,8 +112,6 @@ public String getDashboard(@NotNull Map queryParams) throws Anal } - - @Override public void reload() { diff --git a/src/main/java/org/digma/intellij/plugin/dashboard/DashboardServiceStarter.java b/src/main/java/org/digma/intellij/plugin/dashboard/DashboardServiceStarter.java new file mode 100644 index 000000000..0ea044832 --- /dev/null +++ b/src/main/java/org/digma/intellij/plugin/dashboard/DashboardServiceStarter.java @@ -0,0 +1,14 @@ +package org.digma.intellij.plugin.dashboard; + +import com.intellij.openapi.project.Project; +import org.digma.intellij.plugin.startup.DigmaProjectActivity; +import org.jetbrains.annotations.NotNull; + +public class DashboardServiceStarter extends DigmaProjectActivity { + + @Override + public void executeProjectStartup(@NotNull Project project) { + //need to start DashboardService so that it will start listening to events + DashboardService.getInstance(project); + } +} diff --git a/src/main/kotlin/org/digma/intellij/plugin/ui/highlights/HighlightsMessageRouterHandler.kt b/src/main/kotlin/org/digma/intellij/plugin/ui/highlights/HighlightsMessageRouterHandler.kt index 9d0787afa..01b386875 100644 --- a/src/main/kotlin/org/digma/intellij/plugin/ui/highlights/HighlightsMessageRouterHandler.kt +++ b/src/main/kotlin/org/digma/intellij/plugin/ui/highlights/HighlightsMessageRouterHandler.kt @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode import com.intellij.openapi.project.Project import org.apache.maven.artifact.versioning.ComparableVersion import org.cef.browser.CefBrowser -import org.digma.intellij.plugin.analytics.getVersion +import org.digma.intellij.plugin.analytics.getBackendVersion import org.digma.intellij.plugin.common.newerThan import org.digma.intellij.plugin.log.Log import org.digma.intellij.plugin.model.rest.highlights.HighlightsRequest @@ -20,7 +20,7 @@ class HighlightsMessageRouterHandler(project: Project) : BaseCommonMessageRouter override fun doOnQuery(project: Project, browser: CefBrowser, requestJsonNode: JsonNode, rawRequest: String, action: String): Boolean { - val version = getVersion(project) + val version = getBackendVersion(project) val comparableVersion = ComparableVersion(version) if (version == "unknown" || comparableVersion.newerThan(ComparableVersion("0.3.7"))) { when (action) { diff --git a/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppMessageRouterHandler.kt b/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppMessageRouterHandler.kt index 437d31400..616c2752b 100644 --- a/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppMessageRouterHandler.kt +++ b/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppMessageRouterHandler.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import org.cef.browser.CefBrowser -import org.digma.intellij.plugin.protocol.DigmaProtocolApi +import org.digma.intellij.plugin.api.ApiProjectService import org.digma.intellij.plugin.ui.assets.AssetsMessageRouterHandler import org.digma.intellij.plugin.ui.errors.ErrorsMessageRouterHandler import org.digma.intellij.plugin.ui.highlights.HighlightsMessageRouterHandler @@ -65,7 +65,7 @@ class MainAppMessageRouterHandler(project: Project) : BaseMessageRouterHandler(p doCommonInitialize(browser) updateDigmaEngineStatus(project, browser) - project.service().setMainAppInitialized() + project.service().setMainAppInitialized() } } \ No newline at end of file diff --git a/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppService.kt b/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppService.kt index 6da0e1d8c..dbca23eca 100644 --- a/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppService.kt +++ b/src/main/kotlin/org/digma/intellij/plugin/ui/mainapp/MainAppService.kt @@ -9,10 +9,7 @@ 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 @@ -29,22 +26,6 @@ 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 diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index aab186107..f63b1f655 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -22,7 +22,7 @@ - + com.intellij.modules.java @@ -127,7 +127,8 @@ implementation="org.digma.intellij.plugin.activation.UserActivationStartup"/> - + @@ -142,12 +143,12 @@ - + + -