Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ dependencies {
testImplementation("net.java.dev.jna:jna:5.10.0")
testImplementation("org.awaitility:awaitility-kotlin:4.2.1")
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("io.mockk:mockk:1.13.13")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.0.0")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
Expand Down
4 changes: 1 addition & 3 deletions src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.sourcegraph.cody.agent
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.util.net.HttpConfigurable
Expand Down Expand Up @@ -92,7 +91,6 @@ private constructor(

companion object {
private val logger = Logger.getInstance(CodyAgent::class.java)
private val PLUGIN_ID = PluginId.getId("com.sourcegraph.jetbrains")
private const val DEFAULT_AGENT_DEBUG_PORT = 3113 // Also defined in agent/src/cli/jsonrpc.ts

@JvmField val executorService: ExecutorService = Executors.newCachedThreadPool()
Expand Down Expand Up @@ -345,7 +343,7 @@ private constructor(
return if (fromProperty.isNotEmpty()) {
Paths.get(fromProperty)
} else {
PluginManagerCore.getPlugin(PLUGIN_ID)?.pluginPath
PluginManagerCore.getPlugin(ConfigUtil.getPluginId())?.pluginPath
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,6 @@ interface _LegacyAgentServer {

@JsonRequest("testing/requestErrors")
fun testingRequestErrors(): CompletableFuture<List<NetworkRequest>>

@JsonRequest("extension/reset") fun extension_reset(params: Null?): CompletableFuture<Null?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ class CodyAuthenticationManager :

fun showInvalidAccessTokenError() = getIsTokenInvalid().getNow(null) == true

fun removeAll() {
accountManager.accounts.forEach { accountManager.removeAccount(it) }
}

override fun dispose() {
scheduler.shutdown()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationType
import com.intellij.notification.impl.NotificationFullContent
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
Expand All @@ -16,13 +15,15 @@ import com.intellij.openapi.updateSettings.impl.PluginDownloader
import com.intellij.openapi.updateSettings.impl.UpdateChecker
import com.intellij.openapi.util.BuildNumber
import com.sourcegraph.common.NotificationGroups
import com.sourcegraph.config.ConfigUtil
import java.lang.reflect.InvocationTargetException

class CheckUpdatesTask(project: Project) :
Task.Backgroundable(project, "Checking for Sourcegraph Cody + Code Search update...", false) {

override fun run(indicator: ProgressIndicator) {
val availableUpdate = getAvailablePluginDownloaders(indicator).find { it.id == pluginId }
val availableUpdate =
getAvailablePluginDownloaders(indicator).find { it.id == ConfigUtil.getPluginId() }
if (availableUpdate != null) {
CustomPluginRepositoryService.getInstance().clearCache()
notifyAboutTheUpdate(project)
Expand All @@ -31,7 +32,6 @@ class CheckUpdatesTask(project: Project) :

companion object {
private val logger = Logger.getInstance(CheckUpdatesTask::class.java)
private val pluginId = PluginId.getId("com.sourcegraph.jetbrains")

fun getAvailablePluginDownloaders(indicator: ProgressIndicator): Collection<PluginDownloader> {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.sourcegraph.cody.initialization

import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginInstaller
import com.intellij.ide.plugins.PluginStateListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.sourcegraph.cody.agent.CodyAgentService
import com.sourcegraph.cody.agent.protocol.BillingCategory
import com.sourcegraph.cody.agent.protocol.BillingMetadata
import com.sourcegraph.cody.agent.protocol.BillingProduct
import com.sourcegraph.cody.agent.protocol.TelemetryEventParameters
import com.sourcegraph.cody.config.CodyAuthenticationManager
import com.sourcegraph.cody.telemetry.TelemetryV2
import com.sourcegraph.config.ConfigUtil
import java.util.concurrent.TimeUnit

class UninstallListener : StartupActivity {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed it looks like it is currently broken:

image

override fun runActivity(project: Project) {
PluginInstaller.addStateListener(
object : PluginStateListener {
override fun uninstall(descriptor: IdeaPluginDescriptor) {
// Only run for this plugin
if (descriptor.pluginId != ConfigUtil.getPluginId()) {
return
}
val authManager = CodyAuthenticationManager.getInstance()
authManager.setActiveAccount(null)
authManager.removeAll()
TelemetryV2.sendTelemetryEvent(
project,
"cody.extension",
"uninstalled",
TelemetryEventParameters(
billingMetadata =
BillingMetadata(BillingProduct.CODY, BillingCategory.BILLABLE)))
CodyAgentService.withAgent(project) {
it.server.extension_reset(null).get(20, TimeUnit.SECONDS)
}
}

override fun install(descriptor: IdeaPluginDescriptor) {}
})
}
}
5 changes: 4 additions & 1 deletion src/main/kotlin/com/sourcegraph/config/ConfigUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ object ConfigUtil {
const val CODY_DISPLAY_NAME = "Cody"
const val CODE_SEARCH_DISPLAY_NAME = "Code Search"
const val SOURCEGRAPH_DISPLAY_NAME = "Sourcegraph"
const val PLUGIN_ID = "com.sourcegraph.jetbrains"
private const val FEATURE_FLAGS_ENV_VAR = "CODY_JETBRAINS_FEATURES"

private val logger = Logger.getInstance(ConfigUtil::class.java)
Expand Down Expand Up @@ -149,11 +150,13 @@ object ConfigUtil {
return settingsProperties + additionalProperties
}

@JvmStatic @Contract(pure = true) fun getPluginId(): PluginId = PluginId.getId(PLUGIN_ID)

@JvmStatic
@Contract(pure = true)
fun getPluginVersion(): String {
// Internal version
val plugin = PluginManagerCore.getPlugin(PluginId.getId("com.sourcegraph.jetbrains"))
val plugin = PluginManagerCore.getPlugin(getPluginId())
return if (plugin != null) plugin.version else "unknown"
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
serviceImplementation="com.sourcegraph.find.FindService"/>
<postStartupActivity
implementation="com.sourcegraph.cody.initialization.PostStartupActivity"/>
<postStartupActivity
implementation="com.sourcegraph.cody.initialization.UninstallListener"/>

<!-- Cody -->
<toolWindow
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.sourcegraph.cody.initialization

import com.intellij.ide.plugins.*
import com.intellij.openapi.extensions.PluginId
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.sourcegraph.cody.config.CodyAuthenticationManager
import com.sourcegraph.cody.telemetry.TelemetryV2
import com.sourcegraph.config.ConfigUtil
import io.mockk.*

@Suppress("UnstableApiUsage")
class UninstallListenerTest : BasePlatformTestCase() {

private val uninstallListener = UninstallListener()

// Mock dependencies
// relaxed = true to allow unit returning methods to auto-mock
private val authManager = mockk<CodyAuthenticationManager>(relaxed = true)

override fun setUp() {
super.setUp()

// setup mock objects
mockkObject(CodyAuthenticationManager)
every { CodyAuthenticationManager.getInstance() } returns authManager
mockkObject(TelemetryV2)
every { TelemetryV2.sendTelemetryEvent(any(), any(), any()) } returns Unit
}

private fun getPlugin() =
PluginManagerCore.findPlugin(ConfigUtil.getPluginId()) ?: throw Exception("Plugin not found")

fun `test plugin uninstall cleans up resources`() {
// Execute uninstall
uninstallListener.runActivity(project)
val plugin = getPlugin()
PluginInstaller.prepareToUninstall(plugin)
verify {
authManager.setActiveAccount(null)
authManager.removeAll()
TelemetryV2.sendTelemetryEvent(any(), "cody.extension", "uninstalled", any())
}
}

fun `test plugin uninstall does nothing for unrelated plugins`() {
// Execute uninstall
val plugin = getPlugin()
uninstallListener.runActivity(project)

// Now mock out config util so that it returns a different plugin id
// so that the UninstallListener thinks it's a different plugin
mockkStatic(ConfigUtil::getPluginId)
every { ConfigUtil.getPluginId() } returns PluginId.getId("com.sourcegraph.cody.test")

PluginInstaller.prepareToUninstall(plugin)
// Remove the static method mock so that it doesn't interfere with other tests
unmockkStatic(ConfigUtil::getPluginId)

// Verify that the uninstall listener didn't do anything
verify(exactly = 0) {
authManager.setActiveAccount(null)
authManager.removeAll()
TelemetryV2.sendTelemetryEvent(any(), "cody.extension", "uninstalled", any())
}
}
}