Skip to content

Commit

Permalink
Open Enum Transform for AWS (#1153)
Browse files Browse the repository at this point in the history
Adds a transform which makes all AWS enums open as we've seen that they frequently need to be treated as such. Going to add a scripted test before moving this PR out of draft status.
  • Loading branch information
lewisjkl authored Aug 31, 2023
1 parent 2cb16c2 commit 908c842
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
lazy val root = (project in file("."))
.enablePlugins(Smithy4sCodegenPlugin)
.settings(
scalaVersion := "2.13.10",
libraryDependencies ++= Seq(
"com.disneystreaming.smithy4s" %% "smithy4s-aws-kernel" % smithy4sVersion.value
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.8.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
sys.props.get("plugin.version") match {
case Some(x) =>
addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % x)
case _ =>
sys.error(
"""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2021-2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.amazonaws.dynamodb.TestEnum

object Main extends App {

require(
TestEnum.hints.has(alloy.OpenEnum),
"Expected OpenEnum trait on aws enum."
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"smithy": "2.0",
"shapes": {
"com.amazonaws.dynamodb#TestEnum": {
"type": "enum",
"members": {
"FOO": {
"target": "smithy.api#Unit",
"traits": {
"smithy.api#enumValue": "foo"
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# check if smithy4sCodegen works
> compile
> run
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
smithy4s.codegen.transformers.AwsStandardTypesTransformer
smithy4s.codegen.transformers.AwsStandardTypesTransformer
smithy4s.codegen.transformers.OpenEnumTransformer
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package smithy4s.codegen
package internals

import alloy.openapi._
import smithy4s.codegen.transformers.AwsStandardTypesTransformer
import smithy4s.codegen.transformers._
import software.amazon.smithy.model.Model

import scala.jdk.CollectionConverters._
Expand Down Expand Up @@ -158,6 +158,6 @@ private[codegen] object CodegenImpl { self =>
}

private def withAwsTypeTransformer(transformers: List[String]): List[String] =
transformers :+ AwsStandardTypesTransformer.name
transformers :+ AwsStandardTypesTransformer.name :+ OpenEnumTransformer.name

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2021-2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package smithy4s.codegen.transformers

import alloy.OpenEnumTrait
import software.amazon.smithy.build.{ProjectionTransformer, TransformContext}
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes._
import software.amazon.smithy.model.traits._
import java.util.function.Function

@annotation.nowarn("msg=class EnumTrait in package traits is deprecated")
private[codegen] final class OpenEnumTransformer extends ProjectionTransformer {
override def getName: String = OpenEnumTransformer.name

private val awsNamespacePrefix = "com.amazonaws"

def transform(ctx: TransformContext): Model = {
val shapeMapper: Function[Shape, Shape] = { (shp: Shape) =>
shp match {
case shp if !shp.getId.getNamespace.startsWith(awsNamespacePrefix) =>
shp
case e: EnumShape =>
e.toBuilder.addTrait(new OpenEnumTrait()).build()
case e: IntEnumShape =>
e.toBuilder.addTrait(new OpenEnumTrait()).build()
case t: Shape if t.hasTrait(classOf[EnumTrait]) =>
(Shape
.shapeToBuilder(t): AbstractShapeBuilder[_, _])
.addTrait(new OpenEnumTrait())
.build()
case other => other
}
}
ctx.getTransformer().mapShapes(ctx.getModel(), shapeMapper)
}

}

object OpenEnumTransformer {
val name: String = "OpenEnumTransformer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package smithy4s.codegen.transformers

import software.amazon.smithy.build.TransformContext
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ShapeId
import alloy.OpenEnumTrait

final class OpenEnumTransformerSpec extends munit.FunSuite {

test("adds OpenEnum on shapes with enum trait") {
val input =
"""|$version: "2"
|
|namespace com.amazonaws.kinesis
|
|@enum([
| {value: "TEST", name: "TEST"}
|])
|string Test
|""".stripMargin

runTest(input)
}

test("adds OpenEnum on intEnum shapes") {
val input =
"""|$version: "2"
|
|namespace com.amazonaws.kinesis
|
|intEnum Test {
| ONE = 1
|}
|""".stripMargin

runTest(input)
}

test("adds OpenEnum on enum shapes") {
val input =
"""|$version: "2"
|
|namespace com.amazonaws.kinesis
|
|enum Test {
| ONE
|}
|""".stripMargin

runTest(input)
}

test("DO NOT add OpenEnum on enum shapes outside of aws namespace") {
val input =
"""|$version: "2"
|
|namespace test
|
|enum Test {
| ONE
|}
|""".stripMargin

val transformedModel =
new OpenEnumTransformer()
.transform(
TransformContext
.builder()
.model(loadModel(input))
.build()
)

val containsOpenEnum = transformedModel
.expectShape(ShapeId.fromParts("test", "Test"))
.hasTrait(classOf[OpenEnumTrait])

assert(!containsOpenEnum, "Expected openEnum trait NOT to be present")
}

private def runTest(inputModel: String)(implicit
loc: munit.Location
): Unit = {
val transformedModel =
new OpenEnumTransformer()
.transform(
TransformContext
.builder()
.model(loadModel(inputModel))
.build()
)

val containsOpenEnum = transformedModel
.expectShape(ShapeId.fromParts("com.amazonaws.kinesis", "Test"))
.hasTrait(classOf[OpenEnumTrait])

assert(containsOpenEnum, "Expected openEnum trait to be present")
}

private def loadModel(strModels: String*): Model = {
val assembler = Model
.assembler()
.disableValidation()
.discoverModels()

strModels
.foldLeft(assembler) { case (a, model) =>
a.addUnparsedModel(s"test-${model.hashCode}.smithy", model)
}
.assemble()
.unwrap()
}
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
smithy4s.transformers.SimpleRestJsonProtocolTransformer
smithy4s.transformers.SimpleRestJsonProtocolTransformer

0 comments on commit 908c842

Please sign in to comment.