All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.smithy.kotlin.codegen.rendering.endpoints.discovery.EndpointDiscoveryIntegration.kt Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
package software.amazon.smithy.kotlin.codegen.rendering.endpoints.discovery

import software.amazon.smithy.aws.traits.clientendpointdiscovery.ClientDiscoveredEndpointTrait
import software.amazon.smithy.aws.traits.clientendpointdiscovery.ClientEndpointDiscoveryTrait
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.*
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding
import software.amazon.smithy.kotlin.codegen.model.*
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointResolverAdapterGenerator
import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpProtocolClientGenerator
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
import software.amazon.smithy.kotlin.codegen.rendering.util.ConfigProperty
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ServiceShape

class EndpointDiscoveryIntegration : KotlinIntegration {
    override fun additionalServiceConfigProps(ctx: CodegenContext): List {
        val endpointDiscoveryOptional = ctx
            .model
            .operationShapes
            .none { it.getTrait()?.isRequired == true }
        val discovererSymbol = EndpointDiscovererGenerator.symbolFor(ctx.settings)
        return super.additionalServiceConfigProps(ctx) + listOf(
            ConfigProperty {
                name = "endpointDiscoverer"

                if (endpointDiscoveryOptional) {
                    documentation = """
                        The endpoint discoverer for this client, if applicable. By default, no endpoint
                        discovery is provided. To use endpoint discovery, set this to a valid
                        [${discovererSymbol.name}] instance.
                    """.trimIndent()
                    symbol = discovererSymbol.asNullable()
                } else {
                    documentation = "The endpoint discoverer for this client"
                    useSymbolWithNullableBuilder(discovererSymbol, "${discovererSymbol.name}()")
                }
            },
        )
    }

    override fun customizeMiddleware(
        ctx: ProtocolGenerator.GenerationContext,
        resolved: List,
    ): List = super.customizeMiddleware(ctx, resolved) + listOf(DiscoveredEndpointMiddleware)

    override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
        model.expectShape(settings.service).hasTrait()

    override val sectionWriters: List = listOf(
        SectionWriterBinding(HttpProtocolClientGenerator.EndpointResolverAdapterBinding, ::renderEndpointResolver),
    )

    private fun renderEndpointResolver(writer: KotlinWriter, previousValue: String?) {
        val ctx = writer.getContextValue(HttpProtocolClientGenerator.EndpointResolverAdapterBinding.GenerationContext)
        val op = writer.getContextValue(HttpProtocolClientGenerator.EndpointResolverAdapterBinding.OperationShape)
        val clientSymbol = ctx.symbolProvider.toSymbol(ctx.service)
        val defaultClientName = "Default${clientSymbol.name}"

        when (op.getTrait()?.isRequired) {
            null -> writer.write("#L", previousValue)

            true -> writer.write(
                "execution.endpointResolver = config.endpointDiscoverer.asEndpointResolver(this@#L, #T(config))",
                defaultClientName,
                EndpointResolverAdapterGenerator.getSymbol(ctx.settings),
            )

            false -> writer.write(
                "execution.endpointResolver = config.endpointDiscoverer?.asEndpointResolver(this@#1L, #2T(config)) ?: #2T(config)",
                defaultClientName,
                EndpointResolverAdapterGenerator.getSymbol(ctx.settings),
            )
        }
    }

    override fun writeAdditionalFiles(ctx: CodegenContext, delegator: KotlinDelegator) {
        EndpointDiscovererGenerator(ctx, delegator).render()
        super.writeAdditionalFiles(ctx, delegator)
    }
}

private object DiscoveredEndpointMiddleware : ProtocolMiddleware {
    override val name: String = "DiscoveredEndpointMiddleware"

    override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean =
        op.getTrait()?.optionalError?.getOrNull() != null &&
            op.hasTrait()

    override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
        val interceptor = buildSymbol {
            name = "DiscoveredEndpointErrorInterceptor"
            namespace(KotlinDependency.HTTP_CLIENT, "aws.smithy.kotlin.runtime.http.interceptors")
        }

        val errorShapeId = ctx.service.expectTrait().optionalError.get()
        val errorShape = ctx.model.expectShape(errorShapeId)
        val errorSymbol = ctx.symbolProvider.toSymbol(errorShape)
        writer.write(
            "config.endpointDiscoverer?.let { op.interceptors.add(#T(#T, it::invalidate)) }",
            interceptor,
            errorSymbol,
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy