Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve dependency handling and use the DataGrip connector to MongoDB #6

Merged
merged 16 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ subprojects {
val testImplementation by configurations
val compileOnly by configurations

configurations.named("runtimeClasspath").configure {
exclude("org.jetbrains.kotlin")
exclude("org.jetbrains.kotlinx")
}

compileOnly(rootProject.libs.kotlin.stdlib)
testImplementation(rootProject.libs.testing.jupiter.engine)
testImplementation(rootProject.libs.testing.jupiter.vintage.engine)
Expand Down
23 changes: 12 additions & 11 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ spotless-plugin="6.25.0"
jmh-plugin="0.7.2"
diktat-plugin="1.0.1"
jmhreport-plugin="0.9.0"

# Library dependencies
bson="5.0.0"
mongodb-driver="4.11.0"
kotlin-stdlib="1.9.22"
kotlinx-coroutines="1.8.0-RC2"
kotlinx-coroutines="1.7.3"
jupiter="5.10.2"
mockito="5.11.0"
mockito-kotlin="5.3.1"
Expand All @@ -27,6 +24,7 @@ jmh="1.37"
segment="3.5.1"
jsoup="1.17.2"
video-recorder="2.0"
gson="2.10.1"

[plugins]
intellij={ id="org.jetbrains.intellij", version.ref="intellij-plugin" }
Expand All @@ -37,19 +35,20 @@ changelog={ id="org.jetbrains.changelog", version.ref="intellij-changelog-plugin
jmhreport={ id="io.morethan.jmhreport", version.ref="jmhreport-plugin" }

[libraries]
mongodb-bson={ group="org.mongodb", name="bson", version.ref="bson" }
mongodb-driver={ group="org.mongodb", name="mongodb-driver-kotlin-coroutine", version.ref="mongodb-driver" }

## Kotlin compileOnly libraries. They must not be bundled because they are already part of the
## JetBrains ecosystem.
kotlin-stdlib={ group="org.jetbrains.kotlin", name="kotlin-stdlib", version.ref="kotlin-stdlib" }
kotlin-coroutines-core={ group="org.jetbrains.kotlinx", name="kotlinx-coroutines-core", version.ref="kotlinx-coroutines" }
kotlin-coroutines-swing={ group="org.jetbrains.kotlinx", name="kotlinx-coroutines-swing", version.ref="kotlinx-coroutines" }
kotlin-coroutines-test={ group="org.jetbrains.kotlinx", name="kotlinx-coroutines-test", version.ref="kotlinx-coroutines" }

######################################################
## Production Libraries.
segment={ group="com.segment.analytics.java", name="analytics", version.ref="segment" }

gson={ group="com.google.code.gson", name="gson", version.ref="gson" }
######################################################
## Testing Libraries.
testing-jupiter-engine={ group="org.junit.jupiter", name="junit-jupiter-engine", version.ref="jupiter" }
testing-jupiter-vintage-engine={ group="org.junit.vintage", name="junit-vintage-engine", version.ref="jupiter" }

testing-jsoup= { group="org.jsoup", name="jsoup", version.ref="jsoup" }
testing-mockito-core={ group="org.mockito", name="mockito-core", version.ref="mockito" }
testing-mockito-kotlin={ group="org.mockito.kotlin", name="mockito-kotlin", version.ref="mockito-kotlin" }
Expand All @@ -65,7 +64,9 @@ testing-remoteRobotDeps-retrofitGson={ group="com.squareup.retrofit2", name="con
testing-jmh-core={ group="org.openjdk.jmh", name="jmh-core", version.ref="jmh" }
testing-jmh-annotationProcessor={ group="org.openjdk.jmh", name="jmh-generator-annprocess", version.ref="jmh" }
testing-jmh-generatorByteCode={ group="org.openjdk.jmh", name="jmh-generator-bytecode", version.ref="jmh" }

######################################################
## Libraries and plugins only used for the buildScript.
buildScript-plugin-kotlin={ group="org.jetbrains.kotlin", name="kotlin-gradle-plugin", version="1.9.23" }
buildScript-plugin-versions={ group="com.github.ben-manes", name="gradle-versions-plugin", version.ref="versions-plugin" }
buildScript-plugin-spotless={ group="com.diffplug.spotless", name="spotless-plugin-gradle", version="6.25.0" }
######################################################
1 change: 0 additions & 1 deletion packages/jetbrains-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ tasks {

destinationFile.set(project.layout.projectDirectory.file("src/main/resources/build.properties"))
property("pluginVersion", rootProject.version)
property("driverVersion", rootProject.libs.versions.mongodb.driver.get())
property("segmentApiKey", System.getenv("BUILD_SEGMENT_API_KEY") ?: "<none>")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mongodb.jbplugin

import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.mongodb.jbplugin.observability.probe.PluginActivatedProbe
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

/**
* This notifies that the plugin has been activated.
*
* @param cs
*/
class ActivatePluginPostStartupActivity(private val cs: CoroutineScope) : StartupActivity, DumbAware {
override fun runActivity(project: Project) {
cs.launch {
val pluginActivated = project.getService(PluginActivatedProbe::class.java)
pluginActivated.pluginActivated()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.mongodb.jbplugin.dataaccess

import com.google.gson.Gson
import com.intellij.database.console.session.DatabaseSession
import com.intellij.database.console.session.DatabaseSessionManager
import com.intellij.database.dataSource.LocalDataSource
import com.intellij.openapi.project.Project
import kotlinx.coroutines.*
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.resume
import kotlin.time.Duration

internal class BaseAccessAdapter(
private val project: Project,
private val dataSource: LocalDataSource
) {
private val gson = Gson()

suspend inline fun <reified T : Any> runQuery(queryString: String, timeout: Duration): List<T> = withContext(Dispatchers.IO) {
suspendCancellableCoroutine { callback ->
val session = getSession()
val hasFinished = AtomicBoolean(false)

launch {
val query = DataGripQueryAdapter(queryString, T::class.java, gson, session) {
if (!hasFinished.compareAndSet(false, true)) {
callback.resume(it)
}
}

session.messageBus.dataProducer.processRequest(query)

delay(timeout)
if (!hasFinished.compareAndSet(false, true)) {
callback.cancel(TimeoutException("Timeout running query '$queryString'"))
}
}
}
}

private fun getSession(): DatabaseSession {
val sessions = DatabaseSessionManager.getSessions(project, dataSource)
if (sessions.isEmpty()) {
return DatabaseSessionManager.openSession(project, dataSource, "mongodb")
}

return sessions[0]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.mongodb.jbplugin.dataaccess

import com.google.gson.Gson
import com.intellij.database.dataSource.DatabaseConnectionCore
import com.intellij.database.datagrid.DataRequest

internal class DataGripQueryAdapter<out T: Any>(
private val queryScript: String,
private val resultClass: Class<T>,
private val gson: Gson,
ownerEx: OwnerEx,
private val continuation: (List<T>) -> Unit,
): DataRequest.RawRequest(ownerEx) {
override fun processRaw(p0: Context?, p1: DatabaseConnectionCore?) {
val remoteConnection = p1!!.remoteConnection
val statement = remoteConnection.prepareStatement(queryScript.trimIndent())

val listOfResults = mutableListOf<T>()
val resultSet = statement.executeQuery()

if (resultClass.isPrimitive || resultClass == String::class.java) {
while (resultSet.next()) {
listOfResults.add(resultSet.getObject(1) as T)
}
} else {
while (resultSet.next()) {
val hashMap = resultSet.getObject(1) as Map<String, Any>
val result = gson.fromJson(gson.toJson(hashMap), resultClass)
listOfResults.add(result)
}
}

continuation(listOfResults)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.mongodb.jbplugin.dataaccess

import com.intellij.database.dataSource.LocalDataSource
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import kotlin.time.Duration.Companion.seconds

data class ServerInfo(val version: String)

@Service(Service.Level.PROJECT)
class ServerInfoRepository(
private val project: Project
) {
suspend fun getServerInfo(ds: LocalDataSource): ServerInfo {
val (versionString) = BaseAccessAdapter(project, ds).runQuery<String>(
"""
db.version()
""".trimIndent(),
timeout = 1.seconds
)

return ServerInfo(versionString)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ object BuildInformation {
it.load(BuildInformation::class.java.getResourceAsStream("/build.properties"))
}
val pluginVersion = properties["pluginVersion"]!!.toString()
val driverVersion = properties["driverVersion"]!!.toString()
val segmentApiKey = properties["segmentApiKey"]!!.toString()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.mongodb.jbplugin.observability.actions

import com.intellij.database.dataSource.localDataSource
import com.intellij.database.psi.DbDataSource
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.components.Service
import com.intellij.openapi.rd.util.launchChildOnUi
import com.intellij.openapi.ui.Messages
import com.mongodb.jbplugin.dataaccess.ServerInfoRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Service(Service.Level.PROJECT)
class GetMongoDBVersionActionService(
private val coroutineScope: CoroutineScope
) {
fun actionPerformed(event: AnActionEvent) {
coroutineScope.launch {
val repository = event.project!!.getService(ServerInfoRepository::class.java)
val dataSource = event.dataContext.getData(PlatformDataKeys.PSI_ELEMENT) as DbDataSource

val buildInfo = repository.getServerInfo(dataSource.localDataSource!!)

coroutineScope.launchChildOnUi {
Messages.showMessageDialog(buildInfo.version, "Show DB Version", null)
}
}
}
}

class GetMongoDBVersionAction: AnAction() {
override fun actionPerformed(event: AnActionEvent) {
event.project!!.getService(GetMongoDBVersionActionService::class.java).actionPerformed(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
<depends>com.intellij.database</depends>

<extensions defaultExtensionNs="com.intellij">
<postStartupActivity implementation="com.mongodb.jbplugin.SayHiListener"/>
<postStartupActivity implementation="com.mongodb.jbplugin.ActivatePluginPostStartupActivity"/>
</extensions>
<applicationListeners>
</applicationListeners>
<actions>
<action id="com.mongodb.jbplugin.observability.actions.GetMongoDBVersionAction"
class="com.mongodb.jbplugin.observability.actions.GetMongoDBVersionAction" text="Show MongoDB Version">
<add-to-group group-id="DatabaseViewPopupMenu" anchor="after" relative-to-action="DatabaseView.Refresh"/>
</action>
</actions>
</idea-plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking

class SayHiListenerTest {
class ActivatePluginPostStartupActivityTest {
@Test
fun `emits a plugin activated probe`() = runBlocking {
val pluginActivatedProbe = mock<PluginActivatedProbe>()
val project = mockProject(pluginActivatedProbe = pluginActivatedProbe)
val listener = SayHiListener(CoroutineScope(Dispatchers.Default))
val listener = ActivatePluginPostStartupActivity(CoroutineScope(Dispatchers.Default))

listener.runActivity(project)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ import com.mongodb.jbplugin.fixtures.RequiresProject
import com.mongodb.jbplugin.fixtures.UiTest
import com.mongodb.jbplugin.fixtures.components.SayHelloMessageBoxFixture
import com.mongodb.jbplugin.fixtures.findVisible
import com.mongodb.jbplugin.meta.BuildInformation
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

@UiTest
class SayHiListenerUiTest {
class ActivatePluginPostStartupActivityUiTest {
@Test
@RequiresProject("basic-java-project-with-mongodb")
fun `shows a notification message`(remoteRobot: RemoteRobot) {
val sayHelloMessageBox = remoteRobot.findVisible<SayHelloMessageBoxFixture>()

assertEquals("Build Info", sayHelloMessageBox.title)
assertEquals(BuildInformation.driverVersion, sayHelloMessageBox.body)

sayHelloMessageBox.ok()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.mongodb.jbplugin.meta

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test

class BuildInformationTest {
@Test
fun `loads all build information from the resource file`() {
assertEquals("4.11.0", BuildInformation.driverVersion)
assertNotNull(BuildInformation.pluginVersion)
assertEquals("<none>", BuildInformation.segmentApiKey)
}
}
3 changes: 3 additions & 0 deletions packages/mongodb-access-adapter/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies {

}
Loading