Skip to content

Commit

Permalink
Generate internal-only clients with internal visibility
Browse files Browse the repository at this point in the history
  • Loading branch information
lauzadis committed Sep 13, 2023
1 parent c236d00 commit a4f1190
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShapeId
import java.util.Optional
import java.util.logging.Logger
import kotlin.streams.toList

// shapeId of service from which to generate an SDK
private const val SERVICE = "service"
Expand All @@ -25,6 +24,7 @@ private const val PACKAGE_NAME = "name"
private const val PACKAGE_VERSION = "version"
private const val PACKAGE_DESCRIPTION = "description"
private const val BUILD_SETTINGS = "build"
private const val VISIBILITY_SETTINGS = "visibility"

// Optional specification of sdkId for models that provide them, otherwise Service's shape id name is used
private const val SDK_ID = "sdkId"
Expand Down Expand Up @@ -160,6 +160,7 @@ data class BuildSettings(
val generateDefaultBuildFiles: Boolean = true,
val optInAnnotations: List<String>? = null,
val generateMultiplatformProject: Boolean = false,
val visibility: VisibilitySettings = VisibilitySettings.Default
) {
companion object {
const val ROOT_PROJECT = "rootProject"
Expand All @@ -170,17 +171,17 @@ data class BuildSettings(
fun fromNode(node: Optional<ObjectNode>): BuildSettings = node.map {
val generateFullProject = node.get().getBooleanMemberOrDefault(ROOT_PROJECT, false)
val generateBuildFiles = node.get().getBooleanMemberOrDefault(GENERATE_DEFAULT_BUILD_FILES, true)
val generateMultiplatformProject =
node.get().getBooleanMemberOrDefault(GENERATE_MULTIPLATFORM_MODULE, false)
val generateMultiplatformProject = node.get().getBooleanMemberOrDefault(GENERATE_MULTIPLATFORM_MODULE, false)
val annotations = node.get().getArrayMember(ANNOTATIONS).map {
it.elements.mapNotNull { node ->
node.asStringNode().map { stringNode ->
stringNode.value
}.orNull()
}
}.orNull()
val visibility = VisibilitySettings.fromNode(node.get().getObjectMember(VISIBILITY_SETTINGS))

BuildSettings(generateFullProject, generateBuildFiles, annotations, generateMultiplatformProject)
BuildSettings(generateFullProject, generateBuildFiles, annotations, generateMultiplatformProject, visibility)
}.orElse(Default)

/**
Expand All @@ -193,3 +194,27 @@ data class BuildSettings(
class UnresolvableProtocolException(message: String) : CodegenException(message)

private fun <T> Optional<T>.orNull(): T? = if (isPresent) get() else null

data class VisibilitySettings(
val serviceClient: String = "public",
val structure: String = "public",
val error: String = "public",
) {
companion object {
const val SERVICE_CLIENT = "serviceClient"
const val STRUCTURE = "structure"
const val ERROR = "error"

fun fromNode(node: Optional<ObjectNode>): VisibilitySettings = node.map {
val serviceClient = node.get().getStringMemberOrDefault(SERVICE_CLIENT, "public")
val structure = node.get().getStringMemberOrDefault(STRUCTURE, "public")
val error = node.get().getStringMemberOrDefault(ERROR, "public")
VisibilitySettings(serviceClient, structure, error)
}.orElse(Default)

/**
* Default visibility settings
*/
val Default: VisibilitySettings = VisibilitySettings()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ object ExceptionBaseClassGenerator {

val name = clientName(ctx.settings.sdkId)
writer.dokka("Base class for all service related exceptions thrown by the $name client")
writer.withBlock(
"public open class #T : #T {",
"}",
writer.withBlock("#L open class #T : #T {", "}",
ctx.settings.build.visibility.error,
serviceException,
baseException,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
requireNotNull(ctx.shape) { "ServiceShape is required to render a service client" }
private val serviceSymbol = ctx.symbolProvider.toSymbol(service)
private val writer = ctx.writer
private val visibility = ctx.settings.build.visibility.serviceClient

fun render() {
writer.write("\n\n")
writer.write("public const val ServiceId: String = #S", ctx.settings.sdkId)
writer.write("public const val SdkVersion: String = #S", ctx.settings.pkg.version)
writer.write("$visibility const val ServiceId: String = #S", ctx.settings.sdkId)
writer.write("$visibility const val SdkVersion: String = #S", ctx.settings.pkg.version)
writer.write("\n\n")

writer.putContext("service.name", ctx.settings.sdkId)
Expand All @@ -82,7 +83,7 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {

writer.renderDocumentation(service)
writer.renderAnnotations(service)
writer.openBlock("public interface ${serviceSymbol.name} : #T {", RuntimeTypes.SmithyClient.SdkClient)
writer.openBlock("$visibility interface ${serviceSymbol.name} : #T {", RuntimeTypes.SmithyClient.SdkClient)
.call {
// allow access to client's Config
writer.dokka("${serviceSymbol.name}'s configuration")
Expand Down Expand Up @@ -198,7 +199,7 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
write("Any resources created on your behalf will be shared between clients, and will only be closed when ALL clients using them are closed.")
write("If you provide a resource (e.g. [HttpClientEngine]) to the SDK, you are responsible for managing the lifetime of that resource.")
}
writer.withBlock("public fun #1T.withConfig(block: #1T.Config.Builder.() -> Unit): #1T {", "}", serviceSymbol) {
writer.withBlock("$visibility fun #1T.withConfig(block: #1T.Config.Builder.() -> Unit): #1T {", "}", serviceSymbol) {
write("val newConfig = config.toBuilder().apply(block).build()")
write("return Default#L(newConfig)", serviceSymbol.name)
}
Expand All @@ -219,7 +220,7 @@ class ServiceClientGenerator(private val ctx: RenderingContext<ServiceShape>) {
writer.renderDocumentation(op)
writer.renderAnnotations(op)
writer.write(
"public suspend inline fun #T.#L(crossinline block: #T.Builder.() -> Unit): #T = #L(#T.Builder().apply(block).build())",
"$visibility suspend inline fun #T.#L(crossinline block: #T.Builder.() -> Unit): #T = #L(#T.Builder().apply(block).build())",
serviceSymbol,
operationName,
inputSymbol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ class StructureGenerator(
* Renders a normal (non-error) Smithy structure to a Kotlin class
*/
private fun renderStructure() {
writer.openBlock("public class #T private constructor(builder: Builder) {", symbol)
writer.openBlock("#L class #T private constructor(builder: Builder) {",
ctx.settings.build.visibility.structure,
symbol
)
.call { renderImmutableProperties() }
.write("")
.call { renderCompanionObject() }
Expand Down Expand Up @@ -299,7 +302,10 @@ class StructureGenerator(
val exceptionBaseClass = ExceptionBaseClassGenerator.baseExceptionSymbol(ctx.settings)
writer.addImport(exceptionBaseClass)

writer.openBlock("public class #T private constructor(builder: Builder) : ${exceptionBaseClass.name}() {", symbol)
writer.openBlock("#L class #T private constructor(builder: Builder) : ${exceptionBaseClass.name}() {",
ctx.settings.build.visibility.error,
symbol
)
.write("")
.call { renderImmutableProperties() }
.write("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ class AuthSchemeParametersGenerator : AbstractConfigGenerator() {
val implSymbol = getImplementationSymbol(ctx.settings)

ctx.delegator.useSymbolWriter(symbol) { writer ->
writer.withBlock("public interface #T {", "}", symbol) {
writer.withBlock("#L interface #T {", "}",
ctx.settings.build.visibility.structure,
symbol
) {
dokka("The name of the operation currently being invoked.")
write("public val operationName: String")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ open class AuthSchemeProviderGenerator {
write("See [#T] for the default SDK behavior of this interface.", getDefaultSymbol(ctx.settings))
}
writer.write(
"public interface #T : #T<#T>",
"#L interface #T : #T<#T>",
ctx.settings.build.visibility.structure,
symbol,
RuntimeTypes.Auth.Identity.AuthSchemeProvider,
paramsSymbol,
Expand All @@ -64,8 +65,9 @@ open class AuthSchemeProviderGenerator {
private fun renderDefaultImpl(ctx: ProtocolGenerator.GenerationContext, writer: KotlinWriter) {
// FIXME - probably can't remain an object
writer.withBlock(
"public object #T : #T {",
"#L object #T : #T {",
"}",
ctx.settings.build.visibility.structure,
getDefaultSymbol(ctx.settings),
getSymbol(ctx.settings),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class DefaultEndpointProviderGenerator(
private val defaultProviderSymbol: Symbol,
private val interfaceSymbol: Symbol,
private val paramsSymbol: Symbol,
private val settings: KotlinSettings,
private val externalFunctions: Map<String, Symbol> = emptyMap(),
private val propertyRenderers: Map<String, EndpointPropertyRenderer> = emptyMap(),
) : ExpressionRenderer {
Expand All @@ -78,7 +79,11 @@ class DefaultEndpointProviderGenerator(

fun render() {
renderDocumentation()
writer.withBlock("public class #T: #T {", "}", defaultProviderSymbol, interfaceSymbol) {
writer.withBlock("#L class #T: #T {", "}",
settings.build.visibility.structure,
defaultProviderSymbol,
interfaceSymbol
) {
renderResolve()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface EndpointDelegator {

if (rules != null) {
ctx.delegator.useFileWriter(defaultProviderSymbol) {
DefaultEndpointProviderGenerator(it, rules, defaultProviderSymbol, providerSymbol, paramsSymbol).render()
DefaultEndpointProviderGenerator(it, rules, defaultProviderSymbol, providerSymbol, paramsSymbol, ctx.settings).render()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ class EndpointDiscovererGenerator(private val ctx: CodegenContext, private val d
calls.
""".trimIndent(),
)
withBlock("public class #T {", "}", symbol) {
withBlock("#L class #T {", "}",
ctx.settings.build.visibility.structure,
symbol
) {
write(
"private val cache = #T<DiscoveryParams, #T>(10.#T, #T.System)",
RuntimeTypes.Core.Utils.ReadThroughCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
*/
package software.amazon.smithy.kotlin.codegen.rendering.endpoints

import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.test.TestModelDefault
import software.amazon.smithy.kotlin.codegen.test.assertBalancedBracesAndParens
import software.amazon.smithy.kotlin.codegen.test.formatForTest
import software.amazon.smithy.kotlin.codegen.test.shouldContainOnlyOnceWithDiff
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.rulesengine.language.EndpointRuleSet
import kotlin.test.*

Expand Down Expand Up @@ -142,7 +144,13 @@ class DefaultEndpointProviderGeneratorTest {
name = "DefaultEndpointProvider"
namespace = TestModelDefault.NAMESPACE
}
DefaultEndpointProviderGenerator(writer, rules, defaultSymbol, interfaceSymbol, paramsSymbol).render()
val settings = KotlinSettings(
service = ShapeId.from("EndpointProviderGeneratorTest"),
pkg = KotlinSettings.PackageSettings("name", "version"),
sdkId = "testSdkId"
)

DefaultEndpointProviderGenerator(writer, rules, defaultSymbol, interfaceSymbol, paramsSymbol, settings).render()
generatedClass = writer.toString()
}

Expand Down

0 comments on commit a4f1190

Please sign in to comment.