From df4e47b179ea8fca04e26f056a61d991b7cf60e7 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 11 Apr 2025 14:22:07 -0700 Subject: [PATCH] fix(amazonq): allow polymorphic deserialization of getConfigurationFromServer response this is still janky and will probably need to be revisited. however, the return type as modeled is 'LSPAny' so this will need to be somewhat organic --- .../service/CodeWhispererService.kt | 6 +-- .../amazonq/lsp/AmazonQLanguageServer.kt | 4 +- .../services/amazonq/lsp/AmazonQLspService.kt | 4 ++ ...ionFromServerResponseTypeAdapterFactory.kt | 52 +++++++++++++++++++ .../lsp/model/aws/LspServerConfigurations.kt | 9 ++-- 5 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/adapter/GetConfigurationFromServerResponseTypeAdapterFactory.kt diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt index 50a579fedd1..5754471d290 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt @@ -55,7 +55,7 @@ import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererCon import software.aws.toolkits.jetbrains.services.amazonq.SUPPLEMENTAL_CONTEXT_TIMEOUT import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.GetConfigurationFromServerParams -import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LspServerConfigurations +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.Workspaces import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator @@ -706,12 +706,12 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable { ) } - private fun getWorkspaceIds(project: Project): CompletableFuture { + private fun getWorkspaceIds(project: Project): CompletableFuture { val payload = GetConfigurationFromServerParams( section = "aws.q.workspaceContext" ) return AmazonQLspService.executeIfRunning(project) { server -> - server.getConfigurationFromServer(payload) + server.getConfigurationFromServer(payload) as CompletableFuture } ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageServer.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageServer.kt index 18cf95d3973..23e62fcbfeb 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageServer.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageServer.kt @@ -8,7 +8,7 @@ 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.GetConfigurationFromServerParams -import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LspServerConfigurations +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.GetConfigurationFromServerResponse import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.DidChangeDependencyPathsParams import java.util.concurrent.CompletableFuture @@ -28,5 +28,5 @@ interface AmazonQLanguageServer : LanguageServer { fun deleteTokenCredentials(): CompletableFuture @JsonRequest("aws/getConfigurationFromServer") - fun getConfigurationFromServer(params: GetConfigurationFromServerParams): CompletableFuture + fun getConfigurationFromServer(params: GetConfigurationFromServerParams): CompletableFuture } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt index 59658c3a878..6e855a6f387 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt @@ -50,6 +50,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactMa import software.aws.toolkits.jetbrains.services.amazonq.lsp.auth.DefaultAuthCredentialsService import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.DefaultModuleDependenciesService import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.adapter.GetConfigurationFromServerResponseTypeAdapterFactory import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.createExtendedClientMetadata import software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument.TextDocumentServiceHandler import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders @@ -272,6 +273,9 @@ private class AmazonQServerInstance(private val project: Project, private val cs // otherwise Gson treats all numbers as double which causes deser issues it.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) + + // type adapter factory for polymorphic deserialization + it.registerTypeAdapterFactory(GetConfigurationFromServerResponseTypeAdapterFactory()) }.traceMessages( PrintWriter( object : StringWriter() { diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/adapter/GetConfigurationFromServerResponseTypeAdapterFactory.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/adapter/GetConfigurationFromServerResponseTypeAdapterFactory.kt new file mode 100644 index 00000000000..6e7aec548d9 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/adapter/GetConfigurationFromServerResponseTypeAdapterFactory.kt @@ -0,0 +1,52 @@ +// 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.model.adapter + +import com.google.gson.Gson +import com.google.gson.JsonElement +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.Customizations +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.GetConfigurationFromServerResponse +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.Workspaces + +class GetConfigurationFromServerResponseTypeAdapterFactory : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + // Only handle GetConfigurationFromServerResponse + if (GetConfigurationFromServerResponse::class.java != type.rawType) { + return null + } + + val delegateAdapter = gson.getDelegateAdapter(this, type) + val elementAdapter = gson.getAdapter(JsonElement::class.java) + + @Suppress("UNCHECKED_CAST") + return object : TypeAdapter() { + override fun write(out: JsonWriter, value: T) { + delegateAdapter.write(out, value) + } + + override fun read(reader: JsonReader): T? { + val jsonElement = elementAdapter.read(reader) + if (!jsonElement.isJsonObject) { + return null + } + + val jsonObject = jsonElement.asJsonObject + + val ret = when { + jsonObject.has("workspaces") -> gson.fromJson(jsonElement, Workspaces::class.java) + jsonObject.has("customizations") -> gson.fromJson(jsonElement, Customizations::class.java) + else -> delegateAdapter.fromJsonTree(jsonElement) + } as T + + return ret + } + } + } +} + diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/LspServerConfigurations.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/LspServerConfigurations.kt index e762b938d28..b30cbc425f6 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/LspServerConfigurations.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/LspServerConfigurations.kt @@ -3,8 +3,11 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws -// This represents each item in the array -data class WorkspaceInfo(val workspaceRoot: String, val workspaceId: String) +interface GetConfigurationFromServerResponse // This represents the entire array -data class LspServerConfigurations(val workspaces: List) +data class Workspaces(val workspaces: List): GetConfigurationFromServerResponse +data class Customizations(val customizations: List): GetConfigurationFromServerResponse + +data class WorkspaceInfo(val workspaceRoot: String, val workspaceId: String) +data class Customization(val arn: String, val name: String, val description: String)