Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package software.aws.toolkits.jetbrains.services.amazonq.lsp

import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
import org.eclipse.lsp4j.services.LanguageServer
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload
Expand All @@ -16,4 +17,7 @@ import java.util.concurrent.CompletableFuture
interface AmazonQLanguageServer : LanguageServer {
@JsonRequest("aws/credentials/token/update")
fun updateTokenCredentials(payload: UpdateCredentialsPayload): CompletableFuture<ResponseMessage>

@JsonNotification("aws/credentials/token/delete")
fun deleteTokenCredentials(): CompletableFuture<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,28 +154,26 @@
instance = start()
}

suspend fun execute(runnable: suspend (AmazonQLanguageServer) -> Unit) {
suspend fun<T> execute(runnable: suspend (AmazonQLanguageServer) -> T): T {

Check warning on line 157 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt#L157

Added line #L157 was not covered by tests
val lsp = withTimeout(10.seconds) {
val holder = mutex.withLock { instance }.await()
holder.initializer.join()

holder.languageServer
}

runnable(lsp)
return runnable(lsp)

Check warning on line 164 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt#L164

Added line #L164 was not covered by tests
}

fun executeSync(runnable: suspend (AmazonQLanguageServer) -> Unit) {
fun<T> executeSync(runnable: suspend (AmazonQLanguageServer) -> T): T =
runBlocking(cs.coroutineContext) {
execute(runnable)
}
}

companion object {
private val LOG = getLogger<AmazonQLspService>()
fun getInstance(project: Project) = project.service<AmazonQLspService>()

fun executeIfRunning(project: Project, runnable: (AmazonQLanguageServer) -> Unit) =
fun <T> executeIfRunning(project: Project, runnable: (AmazonQLanguageServer) -> T): T? =
project.serviceIfCreated<AmazonQLspService>()?.executeSync(runnable)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.auth

import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import java.util.concurrent.CompletableFuture

interface AuthCredentialsService {
fun updateTokenCredentials(accessToken: String, encrypted: Boolean): CompletableFuture<ResponseMessage>
fun deleteTokenCredentials(): CompletableFuture<Unit>
Comment on lines +10 to +11
Copy link
Contributor

Choose a reason for hiding this comment

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

im assuming hooking these up will be a followup?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.auth

import com.intellij.openapi.project.Project
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.BearerCredentials
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayloadData
import java.util.concurrent.CompletableFuture

class DefaultAuthCredentialsService(
private val project: Project,
private val encryptionManager: JwtEncryptionManager,

Check warning on line 17 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L15-L17

Added lines #L15 - L17 were not covered by tests
) : AuthCredentialsService {

override fun updateTokenCredentials(accessToken: String, encrypted: Boolean): CompletableFuture<ResponseMessage> {
val token = if (encrypted) {
encryptionManager.decrypt(accessToken)

Check warning on line 22 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L22

Added line #L22 was not covered by tests
} else {
accessToken

Check warning on line 24 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L24

Added line #L24 was not covered by tests
}

val payload = createUpdateCredentialsPayload(token)

Check warning on line 27 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L27

Added line #L27 was not covered by tests

return AmazonQLspService.executeIfRunning(project) { server ->
server.updateTokenCredentials(payload)
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))

Check warning on line 31 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L30-L31

Added lines #L30 - L31 were not covered by tests
}

override fun deleteTokenCredentials(): CompletableFuture<Unit> =
CompletableFuture<Unit>().also { completableFuture ->

Check warning on line 35 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L35

Added line #L35 was not covered by tests
AmazonQLspService.executeIfRunning(project) { server ->
server.deleteTokenCredentials()
completableFuture.complete(null)
} ?: completableFuture.completeExceptionally(IllegalStateException("LSP Server not running"))
}

Check warning on line 40 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L37-L40

Added lines #L37 - L40 were not covered by tests

private fun createUpdateCredentialsPayload(token: String): UpdateCredentialsPayload =
UpdateCredentialsPayload(
data = encryptionManager.encrypt(
UpdateCredentialsPayloadData(
BearerCredentials(token)

Check warning on line 46 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L43-L46

Added lines #L43 - L46 were not covered by tests
)
),
encrypted = true
)

Check warning on line 50 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/auth/DefaultAuthCredentialsService.kt#L49-L50

Added lines #L49 - L50 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.auth

import com.intellij.openapi.components.serviceIfCreated
import com.intellij.openapi.project.Project
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.junit.Before
import org.junit.Test
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLanguageServer
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
import java.util.concurrent.CompletableFuture

class DefaultAuthCredentialsServiceTest {
private lateinit var project: Project
private lateinit var mockLanguageServer: AmazonQLanguageServer
private lateinit var mockEncryptionManager: JwtEncryptionManager
private lateinit var sut: DefaultAuthCredentialsService

@Before
fun setUp() {
project = mockk<Project>()
mockLanguageServer = mockk<AmazonQLanguageServer>()
mockEncryptionManager = mockk<JwtEncryptionManager>()
every { mockEncryptionManager.encrypt(any()) } returns "mock-encrypted-data"

// Mock the service methods on Project
val mockLspService = mockk<AmazonQLspService>()
every { project.getService(AmazonQLspService::class.java) } returns mockLspService
every { project.serviceIfCreated<AmazonQLspService>() } returns mockLspService

// Mock the LSP service's executeSync method as a suspend function
every {
mockLspService.executeSync<CompletableFuture<ResponseMessage>>(any())
} coAnswers {
val func = firstArg<suspend (AmazonQLanguageServer) -> CompletableFuture<ResponseMessage>>()
func.invoke(mockLanguageServer)
}

sut = DefaultAuthCredentialsService(project, this.mockEncryptionManager)
}

@Test
fun `test updateTokenCredentials unencrypted success`() {
val token = "unencryptedToken"
val isEncrypted = false

every {
mockLanguageServer.updateTokenCredentials(any())
} returns CompletableFuture.completedFuture(ResponseMessage())

sut.updateTokenCredentials(token, isEncrypted)

verify(exactly = 0) {
mockEncryptionManager.decrypt(any())
}
verify(exactly = 1) {
mockLanguageServer.updateTokenCredentials(any())
}
}

@Test
fun `test updateTokenCredentials encrypted success`() {
val encryptedToken = "encryptedToken"
val decryptedToken = "decryptedToken"
val isEncrypted = true

every { mockEncryptionManager.decrypt(encryptedToken) } returns decryptedToken
every { mockEncryptionManager.encrypt(any()) } returns "mock-encrypted-data"
every {
mockLanguageServer.updateTokenCredentials(any())
} returns CompletableFuture.completedFuture(ResponseMessage())

sut.updateTokenCredentials(encryptedToken, isEncrypted)

verify(exactly = 1) { mockEncryptionManager.decrypt(encryptedToken) }
verify(exactly = 1) { mockLanguageServer.updateTokenCredentials(any()) }
}

@Test
fun `test deleteTokenCredentials success`() {
every { mockLanguageServer.deleteTokenCredentials() } returns CompletableFuture.completedFuture(Unit)

sut.deleteTokenCredentials()

verify(exactly = 1) { mockLanguageServer.deleteTokenCredentials() }
}
}
Loading