Skip to content

fix(amazonq): allow polymorphic deserialization of getConfigurationFromServer response #5565

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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 @@ -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
Expand Down Expand Up @@ -706,12 +706,12 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
)
}

private fun getWorkspaceIds(project: Project): CompletableFuture<LspServerConfigurations> {
private fun getWorkspaceIds(project: Project): CompletableFuture<Workspaces> {
val payload = GetConfigurationFromServerParams(
section = "aws.q.workspaceContext"
)
return AmazonQLspService.executeIfRunning(project) { server ->
server.getConfigurationFromServer(payload)
server.getConfigurationFromServer(payload) as CompletableFuture<Workspaces>
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,5 +28,5 @@ interface AmazonQLanguageServer : LanguageServer {
fun deleteTokenCredentials(): CompletableFuture<Unit>

@JsonRequest("aws/getConfigurationFromServer")
fun getConfigurationFromServer(params: GetConfigurationFromServerParams): CompletableFuture<LspServerConfigurations>
fun getConfigurationFromServer(params: GetConfigurationFromServerParams): CompletableFuture<GetConfigurationFromServerResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
// 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<T>() {
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
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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<WorkspaceInfo>)
data class Workspaces(val workspaces: List<WorkspaceInfo>): GetConfigurationFromServerResponse
data class Customizations(val customizations: List<Customization>): GetConfigurationFromServerResponse

data class WorkspaceInfo(val workspaceRoot: String, val workspaceId: String)
data class Customization(val arn: String, val name: String, val description: String)
Loading