From 4522eb42da40e505692c19864d4f08b7dd604707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Mon, 24 Jul 2023 18:46:13 +0200 Subject: [PATCH 1/8] Add changelog entries for 0.17.7-12 --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82119646a..7780c2d7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,70 @@ +# 0.17.12 + +* Remove reserved types in https://github.com/disneystreaming/smithy4s/pull/1052 + +Remove a legacy mechanism of dealing with name conflicts in generated types. Fixes #1051 + +* Flatten AWS newtypes in https://github.com/disneystreaming/smithy4s/pull/1110 + +Adjusts the rendering of Smithy shapes from AWS specs, as it would've often been inconvenient due to the change above. + +* Bump webjar dependency to 0.47 in https://github.com/disneystreaming/smithy4s/pull/1100 + +Updates a previously frozen dependency to alleviate potential security issues. + +# 0.17.11 + +This is mostly a bugfix and cleanup release. + +* [aws] Keep casing in file credential provider in https://github.com/disneystreaming/smithy4s/pull/1076 + +Resolves a case-sensitivity issue in the file-based AWS credentials provider. + +* Deprecate `ClientBuilder.use`, add `.make` in https://github.com/disneystreaming/smithy4s/pull/1073 + +Deprecates a method - the migration path would be just to move to another one with the same shape. + +* Error transformations as middleware in https://github.com/disneystreaming/smithy4s/pull/1084 + +Changes the error transformation logic in the http4s servers so that it's implemented using the (public) per-endpoint middleware construct. + +# 0.17.10 + +* Revert original behavior where middleware get all errors in https://github.com/disneystreaming/smithy4s/pull/1034 + +This change adds a fix for an accidental behavior change around error handling/capture in middlewares. + +## Other changes + +* Adding a comment in flatMapErrors in https://github.com/disneystreaming/smithy4s/pull/1030 + +# 0.17.9 + +* Update smithy-model to 1.31.0, alloy to 0.2.2 in https://github.com/disneystreaming/smithy4s/pull/1022 + +# 0.17.8 + +* backport of [improve: fallback unspecified members of deprecated trait to N/A] in https://github.com/disneystreaming/smithy4s/pull/989 +* Dynamic module guide in https://github.com/disneystreaming/smithy4s/pull/960 +* Add an option to encode missing fields as nulls in https://github.com/disneystreaming/smithy4s/pull/995 + +# 0.17.7 + +Make sure error handling logic in routing is applied before and after middleware application . + +* Add course link to learning resources in https://github.com/disneystreaming/smithy4s/pull/965 +* Http4s: pre- and post-error handling middleware in https://github.com/disneystreaming/smithy4s/pull/877 + + +# 0.17.6 + +This release is backward binary-compatible with the previous releases from the 0.17.x lineage. + +## Bug fixes + +* Fixes a [bug](https://github.com/disneystreaming/smithy4s/pull/898) related to swagger-ui redirects that would occur with empty paths. +* Fixes a [bug](https://github.com/disneystreaming/smithy4s/pull/904) related to the undocumented "dynamic" module not respecting the order of fields specified in smithy models + # 0.17.5 This release is backward binary-compatible with the previous releases from the 0.17.x lineage. From 2664f235f380ff2193cabc022813bfa7db9d56db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Mon, 24 Jul 2023 18:47:15 +0200 Subject: [PATCH 2/8] [skip ci] remove extra newline --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7780c2d7e..f91150b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,6 @@ Make sure error handling logic in routing is applied before and after middleware * Add course link to learning resources in https://github.com/disneystreaming/smithy4s/pull/965 * Http4s: pre- and post-error handling middleware in https://github.com/disneystreaming/smithy4s/pull/877 - # 0.17.6 This release is backward binary-compatible with the previous releases from the 0.17.x lineage. From a0186aefffa2f9702a69a17009e04494680a0b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Vil=C3=A1?= Date: Tue, 25 Jul 2023 23:30:47 +0100 Subject: [PATCH 3/8] Backport service interpreter (address #1111) (#1118) --- CHANGELOG.md | 4 + modules/core/src-2/kinds/package.scala | 5 ++ modules/core/src-3/kinds/package.scala | 6 ++ modules/core/src/smithy4s/Service.scala | 112 +++++++++++++++++++++++- 4 files changed, 125 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91150b1f..1e29ee391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.17.13 + +- Backports Service interpreter logic introduced in [#908](https://github.com/disneystreaming/smithy4s/pull/908). + # 0.17.12 * Remove reserved types in https://github.com/disneystreaming/smithy4s/pull/1052 diff --git a/modules/core/src-2/kinds/package.scala b/modules/core/src-2/kinds/package.scala index 83e9bbf90..2ab6e68a4 100644 --- a/modules/core/src-2/kinds/package.scala +++ b/modules/core/src-2/kinds/package.scala @@ -28,10 +28,15 @@ package object kinds { type Kind1[F[_]] = { type toKind2[E, O] = F[O] type toKind5[I, E, O, SI, SO] = F[O] + type handler[I, E, O, SI, SO] = I => F[O] } type Kind2[F[_, _]] = { type toKind5[I, E, O, SI, SO] = F[E, O] + type handler[I, E, O, SI, SO] = I => F[E, O] } + type Kind5[F[_, _, _, _, _]] = { + type handler[I, E, O, SI, SO] = I => F[I, E, O, SI, SO] + } } diff --git a/modules/core/src-3/kinds/package.scala b/modules/core/src-3/kinds/package.scala index 909aa41c3..651d86c19 100644 --- a/modules/core/src-3/kinds/package.scala +++ b/modules/core/src-3/kinds/package.scala @@ -29,10 +29,16 @@ package object kinds { type Kind1[F[_]] = { type toKind2[E, O] = F[O] type toKind5[I, E, O, SI, SO] = F[O] + type handler[I, E, O, SI, SO] = I => F[O] } type Kind2[F[_, _]] = { type toKind5[I, E, O, SI, SO] = F[E, O] + type handler[I, E, O, SI, SO] = I => F[E, O] + } + + type Kind5[F[_, _, _, _, _]] = { + type handler[I, E, O, SI, SO] = I => F[I, E, O, SI, SO] } } diff --git a/modules/core/src/smithy4s/Service.scala b/modules/core/src/smithy4s/Service.scala index 38788ad08..f993a60c7 100644 --- a/modules/core/src/smithy4s/Service.scala +++ b/modules/core/src/smithy4s/Service.scala @@ -35,12 +35,77 @@ import kinds._ * metaprogramming. */ trait Service[Alg[_[_, _, _, _, _]]] extends FunctorK5[Alg] with HasId { + /** + * A datatype (typically a sealed trait) that reifies an operation call within + * a service. It essentially captures the input and type indexes that the operation + * deals with. It also typically captures an input value. + * + * It is possible to think of Operation as an "applied [[Endpoint]]", + * or a "call to an [[Endpoint]]". + * + * @tparam I: the input of the operation + * @tparam E: the error type associated to the operation (typically represented as a sealed-trait) + * @tparam O: the output of the operation + * @tparam SI: the streamed input of an operation. Operations can have unary components and streamed components. + * For instance, an http call can send headers (unary `I`) and a stream of bytes (streamed `SI`) to the server. + * @tparam SO: the streamed output of the operation. + */ type Operation[I, E, O, SI, SO] + + /** + * An endpoint is the set of schemas tied to types associated with an [[Operation]]. + * It has a method to wrap the input in an operation instance I => Operation[I, E, O, SI, SO]. + * + * You can think of the endpoint as a "template for an [[Operation]]". It contains everything + * needed to decode/encode operation calls to/from low-level representations (like http requests). + */ type Endpoint[I, E, O, SI, SO] = smithy4s.Endpoint[Operation, I, E, O, SI, SO] + + /** + * This is a polymorphic function that runs an instance of an operation and produces an effect F. + */ type Interpreter[F[_, _, _, _, _]] = PolyFunction5[Operation, F] - type FunctorInterpreter[F[_]] = PolyFunction5[Operation, kinds.Kind1[F]#toKind5] - type BiFunctorInterpreter[F[_, _]] = PolyFunction5[Operation, kinds.Kind2[F]#toKind5] + + /** + * An interpreter specialised for effects of kind `* -> *`, like Try or monofunctor IO. + */ + type FunctorInterpreter[F[_]] = Interpreter[kinds.Kind1[F]#toKind5] + + /** + * An interpreter specialised for effects of kind `* -> (*, *)`, like Either or bifunctor IO. + */ + type BiFunctorInterpreter[F[_, _]] = Interpreter[kinds.Kind2[F]#toKind5] + + /** + * A polymorphic function that can take an Endpoint (associated to this service) and + * produces an handler for it, namely a function that takes the input type of the + * operation, and produces an effect. + */ + type EndpointCompiler[F[_, _, _, _, _]] = PolyFunction5[Endpoint, Kind5[F]#handler] + + /** + * A [[EndpointCompiler]] specialised for effects of kind `* -> *`, like Try or monofunctor IO + */ + type FunctorEndpointCompiler[F[_]] = EndpointCompiler[Kind1[F]#toKind5] + + /** + * A [[EndpointCompiler]] specialised for effects of kind `* -> (*, *)`, like Either or bifunctor IO + */ + type BiFunctorEndpointCompiler[F[_, _]] = EndpointCompiler[Kind2[F]#toKind5] + + /** + * A short-hand for algebras that are specialised for effects of kind `* -> *`. + * + * NB: this alias should be used in polymorphic implementations. When using the Smithy4s + * code generator, equivalent aliases that are named after the service are generated + * (e.g. `Weather` corresponding to `WeatherGen`). + */ type Impl[F[_]] = Alg[kinds.Kind1[F]#toKind5] + + /** + * A short-hand for algebras that are specialised for effects of kind `* -> (*, *)`. + * This is meant to be used in userland, e.g: {{{ val myService = MyService.ErrorAware[Either] }}} + */ type ErrorAware[F[_, _]] = Alg[kinds.Kind2[F]#toKind5] val service: Service[Alg] = this @@ -56,6 +121,49 @@ trait Service[Alg[_[_, _, _, _, _]]] extends FunctorK5[Alg] with HasId { def apply[I, E, O, SI, SO](op: Operation[I,E,O,SI,SO]): Endpoint[I,E,O,SI,SO] = endpoint(op)._2 } + /** + * Given a generic way to turn an endpoint into some handling function (like `I => F[I, E, O, SI, SO]`), this method + * takes care of the logic necessary to produce an interpreter that takes an Operation associated + * to the service and routes it to the correct function, returning the result. + */ + final def interpreter[F[_, _, _, _, _]](compiler: EndpointCompiler[F]) : Interpreter[F] = new Interpreter[F]{ + val cached = compiler.unsafeCacheBy(endpoints.map(Kind5.existential(_)), identity) + def apply[I, E, O, SI, SO](operation: Operation[I, E, O, SI, SO]): F[I, E, O, SI, SO] = { + val (input, ep) = endpoint(operation) + cached(ep).apply(input) + } + } + + /** + * A monofunctor-specialised version of [[interpreter]] + */ + final def functorInterpreter[F[_]](compiler: FunctorEndpointCompiler[F]): FunctorInterpreter[F] = interpreter[Kind1[F]#toKind5](compiler) + + /** + * A bifunctor-specialised version of [[interpreter]] + */ + final def bifunctorInterpreter[F[_, _]](compiler: BiFunctorEndpointCompiler[F]): BiFunctorInterpreter[F] = interpreter[Kind2[F]#toKind5](compiler) + + + /** + * A function that takes an endpoint compiler and produces an Algebra (typically an instance of the generated interfaces), + * backed by an interpreter. + * + * This is useful for writing generic functions that result in the instantiation of a client instance that abides by + * the service interface. + */ + final def algebra[F[_, _, _, _, _]](compiler: EndpointCompiler[F]) : Alg[F] = fromPolyFunction(interpreter(compiler)) + + /** + * A monofunctor-specialised version of [[algebra]] + */ + final def impl[F[_]](compiler: FunctorEndpointCompiler[F]) : Impl[F] = algebra[Kind1[F]#toKind5](compiler) + + /** + * A monofunctor-specialised version of [[algebra]] + */ + final def errorAware[F[_, _]](compiler: BiFunctorEndpointCompiler[F]) : ErrorAware[F] = algebra[Kind2[F]#toKind5](compiler) + } object Service { From 39940fb3825049a1e74e862e8c63b62e6f37cf91 Mon Sep 17 00:00:00 2001 From: Kasper Kondzielski Date: Wed, 26 Jul 2023 21:17:07 +0200 Subject: [PATCH 4/8] Fix rendering of deprecated annotation in mixins (#1123) Co-authored-by: ghostbuster91 Co-authored-by: David Francoeur --- .../smithy4s/codegen/internals/Renderer.scala | 16 ++++++++++++---- .../src/smithy4s/example/DeprecatedMixin.scala | 9 +++++++++ .../smithy4s/example/DeprecatedStructure.scala | 5 +++-- sampleSpecs/deprecations.smithy | 12 +++++++++--- 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 modules/example/src/smithy4s/example/DeprecatedMixin.scala diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index cef9c7eff..03bd16ed4 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -624,7 +624,13 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => } else Line.empty block(line"trait $name$ext") { lines( - fields.map(f => line"def ${fieldToRenderLine(f, noDefault = true)}") + fields.map { f => + lines( + deprecationAnnotation(f.hints), + Line.empty, + line"def ${fieldToRenderLine(f, noDefault = true)}" + ) + } ) } } @@ -884,12 +890,14 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => ) } - deprecationAnnotation(hints).appendIf(_.nonEmpty)(Line.space) + - line"$name: " + tpeAndDefault + line"$name: " + tpeAndDefault } } private def renderArgs(fields: List[Field]): Line = fields - .map(fieldToRenderLine(_)) + .map { f => + deprecationAnnotation(f.hints).appendIf(_.nonEmpty)(Line.space) + + fieldToRenderLine(f) + } .intercalate(Line.comma) private def renderEnum( diff --git a/modules/example/src/smithy4s/example/DeprecatedMixin.scala b/modules/example/src/smithy4s/example/DeprecatedMixin.scala new file mode 100644 index 000000000..3266bddb9 --- /dev/null +++ b/modules/example/src/smithy4s/example/DeprecatedMixin.scala @@ -0,0 +1,9 @@ +package smithy4s.example + + +@deprecated(message = "A compelling reason", since = "0.0.1") +trait DeprecatedMixin { + @deprecated(message = "N/A", since = "N/A") + def strings: Option[List[String]] + def other: Option[List[String]] +} \ No newline at end of file diff --git a/modules/example/src/smithy4s/example/DeprecatedStructure.scala b/modules/example/src/smithy4s/example/DeprecatedStructure.scala index 2063b35de..e2de59b0a 100644 --- a/modules/example/src/smithy4s/example/DeprecatedStructure.scala +++ b/modules/example/src/smithy4s/example/DeprecatedStructure.scala @@ -8,7 +8,7 @@ import smithy4s.schema.Schema.string import smithy4s.schema.Schema.struct @deprecated(message = "A compelling reason", since = "0.0.1") -case class DeprecatedStructure(@deprecated(message = "N/A", since = "N/A") name: Option[String] = None, nameV2: Option[String] = None, strings: Option[List[String]] = None) +case class DeprecatedStructure(@deprecated(message = "N/A", since = "N/A") strings: Option[List[String]] = None, other: Option[List[String]] = None, @deprecated(message = "N/A", since = "N/A") name: Option[String] = None, nameV2: Option[String] = None) extends DeprecatedMixin object DeprecatedStructure extends ShapeTag.Companion[DeprecatedStructure] { val id: ShapeId = ShapeId("smithy4s.example", "DeprecatedStructure") @@ -17,9 +17,10 @@ object DeprecatedStructure extends ShapeTag.Companion[DeprecatedStructure] { ) implicit val schema: Schema[DeprecatedStructure] = struct( + Strings.underlyingSchema.optional[DeprecatedStructure]("strings", _.strings).addHints(smithy.api.Deprecated(message = None, since = None)), + Strings.underlyingSchema.optional[DeprecatedStructure]("other", _.other), string.optional[DeprecatedStructure]("name", _.name).addHints(smithy.api.Deprecated(message = None, since = None)), string.optional[DeprecatedStructure]("nameV2", _.nameV2), - Strings.underlyingSchema.optional[DeprecatedStructure]("strings", _.strings), ){ DeprecatedStructure.apply }.withId(id).addHints(hints) diff --git a/sampleSpecs/deprecations.smithy b/sampleSpecs/deprecations.smithy index bf11740ad..5157d2598 100644 --- a/sampleSpecs/deprecations.smithy +++ b/sampleSpecs/deprecations.smithy @@ -3,10 +3,16 @@ namespace smithy4s.example use smithy4s.meta#adtMember @deprecated(message: "A compelling reason", since: "0.0.1") -structure DeprecatedStructure { +@mixin +structure DeprecatedMixin { + @deprecated strings: Strings, + other: Strings +} + +@deprecated(message: "A compelling reason", since: "0.0.1") +structure DeprecatedStructure with [DeprecatedMixin] { @deprecated name: String, - nameV2: String, - strings: Strings + nameV2: String } @deprecated From adc23f61350d83f4962106c86cea02e3c9d4f9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Thu, 27 Jul 2023 16:34:23 +0200 Subject: [PATCH 5/8] Only transform AWS shapes named after standard shapes (#1127) --- .../AwsStandardTypesTransformer.scala | 24 ++++++++++++- .../AwsStandardTypesTransformerSpec.scala | 34 ++++++++++++++----- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala index 63f4353cd..244686eab 100644 --- a/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala +++ b/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala @@ -84,7 +84,8 @@ class AwsStandardTypesTransformer extends ProjectionTransformer { private def canReplace(shape: Shape): Boolean = { shape.isInstanceOf[SimpleShape] && isAwsShape(shape) && - onlySupportedTraits(shape.getAllTraits) + onlySupportedTraits(shape.getAllTraits) && + hasStandardName(shape) } @annotation.nowarn @@ -96,12 +97,33 @@ class AwsStandardTypesTransformer extends ProjectionTransformer { t.isInstanceOf[DefaultTrait] || t.isInstanceOf[BoxTrait] }) + private def hasStandardName(shape: Shape): Boolean = + AwsStandardTypesTransformer.standardSimpleShapes.contains( + shape.getId().getName() + ) + } object AwsStandardTypesTransformer { val name: String = "AwsStandardTypesTransformer" + val standardSimpleShapes = Set( + "Integer", + "Short", + "Long", + "Float", + "Double", + "BigInteger", + "BigDecimal", + "Byte", + "Blob", + "Boolean", + "String", + "Document", + "Timestamp" + ) + private[transformers] final implicit class MemberShapeBuilderOps( val builder: MemberShape.Builder ) extends AnyVal { diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala index 1b4108417..2d4b72464 100644 --- a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala @@ -25,12 +25,20 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { import smithy4s.codegen.internals.TestUtils._ - test("Flattens member shapes targeting AWS new-types") { + test( + "Flattens member shapes targeting AWS new-types named after standard shapes" + ) { val kinesisNamespace = - """|namespace com.amazonaws.kinesis + """|$version: "2" + |namespace com.amazonaws.kinesis | + |// We expect this one to be removed in favour of smithy.api#Integer |integer Integer + |// This one should be kept because it has a different name |timestamp Date + |// This one should be kept because it has a trait + |@range(min: 1) + |long Long |""".stripMargin val testNamespace = @@ -38,8 +46,10 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { |namespace test | |structure TestStructure { + | @required | i: com.amazonaws.kinesis#Integer | d: com.amazonaws.kinesis#Date + | l: com.amazonaws.kinesis#Long, |} |""".stripMargin @@ -61,24 +71,25 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { structureCode, """package test | + |import com.amazonaws.kinesis.Date + |import com.amazonaws.kinesis.Long |import smithy4s.Hints |import smithy4s.Schema |import smithy4s.ShapeId |import smithy4s.ShapeTag - |import smithy4s.Timestamp |import smithy4s.schema.Schema.int |import smithy4s.schema.Schema.struct - |import smithy4s.schema.Schema.timestamp | - |case class TestStructure(i: Int = 0, d: Option[Timestamp] = None) + |case class TestStructure(i: Int, d: Option[Date] = None, l: Option[Long] = None) |object TestStructure extends ShapeTag.Companion[TestStructure] { | val id: ShapeId = ShapeId("test", "TestStructure") | | val hints: Hints = Hints.empty | | implicit val schema: Schema[TestStructure] = struct( - | int.required[TestStructure]("i", _.i).addHints(smithy.api.Default(smithy4s.Document.fromDouble(0.0d))), - | timestamp.optional[TestStructure]("d", _.d), + | int.required[TestStructure]("i", _.i).addHints(smithy.api.Required()), + | Date.schema.optional[TestStructure]("d", _.d), + | Long.schema.optional[TestStructure]("l", _.l), | ){ | TestStructure.apply | }.withId(id).addHints(hints) @@ -234,6 +245,7 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { """|namespace com.amazonaws.kinesis | |timestamp Date + |timestamp Timestamp |""".stripMargin val transformer = new AwsStandardTypesTransformer() @@ -246,10 +258,16 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { val transformedModel = transformer.transform(transformerContext) assert( - !transformedModel + transformedModel .getShape(ShapeId.from("com.amazonaws.kinesis#Date")) .isPresent ) + + assert( + !transformedModel + .getShape(ShapeId.from("com.amazonaws.kinesis#Timestamp")) + .isPresent + ) } test("Does not flatten types with traits") { From 0f4486d93c55ce59155f90bc94a3e0a3d804ede1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20M=C3=A9lois?= Date: Fri, 28 Jul 2023 15:13:51 +0200 Subject: [PATCH 6/8] Fixes AWS AwsStandardTypesTransformer bug Only structure members can carry the `@default` trait. --- .../AwsStandardTypesTransformer.scala | 15 ++++- .../AwsStandardTypesTransformerSpec.scala | 56 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala index 244686eab..a535547e8 100644 --- a/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala +++ b/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala @@ -65,16 +65,20 @@ class AwsStandardTypesTransformer extends ProjectionTransformer { .map[Boolean](canReplace) .orElse(false) } - .map[Shape](shape => { + .map[Shape] { shape => val target = model.expectShape(shape.getTarget) val replacementShape = replaceWith(shape.getTarget) + // Member shapes can only carry the `@default` trait IF + // their container is a structures + val canCarryDefault = + model.expectShape(shape.getContainer()).isStructureShape() shape .toBuilder() .target(replacementShape) - .copyDefaultTrait(target) + .when(canCarryDefault)(_.copyDefaultTrait(target)) .build() - }) + } .orElse(shape) } @@ -127,6 +131,11 @@ object AwsStandardTypesTransformer { private[transformers] final implicit class MemberShapeBuilderOps( val builder: MemberShape.Builder ) extends AnyVal { + def when(condition: Boolean)( + mutation: MemberShape.Builder => MemberShape.Builder + ): MemberShape.Builder = + if (condition)(mutation(builder)) else builder + def copyDefaultTrait(shape: Shape): MemberShape.Builder = { val defaultTraitOpt = toOption(shape.getTrait(classOf[DefaultTrait])) diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala index 2d4b72464..d6656912d 100644 --- a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala @@ -217,6 +217,62 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { ) } + test("Doesn't keep default traits on Lists") { + val kinesisNamespace = + """|$version: "2" + | + |namespace com.amazonaws.kinesis + | + |@default(5) + |integer Integer + |""".stripMargin + + val testNamespace = + """ + |$version: "2" + | + |namespace test + | + |list TestList { + | member: com.amazonaws.kinesis#Integer + |} + |""".stripMargin + + val rawModel = loadModel(kinesisNamespace, testNamespace) + + val transformer = new AwsStandardTypesTransformer() + val transformerContext = + TransformContext + .builder() + .model(rawModel) + .build() + + val transformedModel = transformer.transform(transformerContext) + + val listCode = + generateScalaCode(transformedModel)("test.TestList") + + assertEquals( + listCode, + """package test + | + |import smithy4s.Hints + |import smithy4s.Newtype + |import smithy4s.Schema + |import smithy4s.ShapeId + |import smithy4s.schema.Schema.bijection + |import smithy4s.schema.Schema.int + |import smithy4s.schema.Schema.list + | + |object TestList extends Newtype[List[Int]] { + | val id: ShapeId = ShapeId("test", "TestList") + | val hints: Hints = Hints.empty + | val underlyingSchema: Schema[List[Int]] = list(int).withId(id).addHints(hints) + | implicit val schema: Schema[TestList] = bijection(underlyingSchema, asBijection) + |}""".stripMargin + ) + } + test("Removes AWS new types after flattening") { val kinesisNamespace = """|namespace com.amazonaws.kinesis From ef3fbc350147a4be0e8f915c1736234c3a2d8c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Tue, 1 Aug 2023 17:50:44 +0200 Subject: [PATCH 7/8] Fix sandbox --- .../com/amazonaws/dynamodb/ErrorMessage.scala | 15 +++++++++++++++ .../amazonaws/dynamodb/InternalServerError.scala | 7 +++---- .../com/amazonaws/dynamodb/package.scala | 1 + modules/sandbox/src/Sandbox.scala | 2 -- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ErrorMessage.scala diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ErrorMessage.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ErrorMessage.scala new file mode 100644 index 000000000..915445726 --- /dev/null +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ErrorMessage.scala @@ -0,0 +1,15 @@ +package com.amazonaws.dynamodb + +import smithy4s.Hints +import smithy4s.Newtype +import smithy4s.Schema +import smithy4s.ShapeId +import smithy4s.schema.Schema.bijection +import smithy4s.schema.Schema.string + +object ErrorMessage extends Newtype[String] { + val id: ShapeId = ShapeId("com.amazonaws.dynamodb", "ErrorMessage") + val hints: Hints = Hints.empty + val underlyingSchema: Schema[String] = string.withId(id).addHints(hints) + implicit val schema: Schema[ErrorMessage] = bijection(underlyingSchema, asBijection) +} diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/InternalServerError.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/InternalServerError.scala index 2696a8909..8d8b7ff7d 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/InternalServerError.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/InternalServerError.scala @@ -4,15 +4,14 @@ import smithy4s.Hints import smithy4s.Schema import smithy4s.ShapeId import smithy4s.ShapeTag -import smithy4s.schema.Schema.string import smithy4s.schema.Schema.struct /**

An error occurred on the server side.

* @param message *

The server encountered an internal error trying to fulfill the request.

*/ -final case class InternalServerError(message: Option[String] = None) extends Throwable { - override def getMessage(): String = message.orNull +final case class InternalServerError(message: Option[ErrorMessage] = None) extends Throwable { + override def getMessage(): String = message.map(_.value).orNull } object InternalServerError extends ShapeTag.Companion[InternalServerError] { val id: ShapeId = ShapeId("com.amazonaws.dynamodb", "InternalServerError") @@ -23,7 +22,7 @@ object InternalServerError extends ShapeTag.Companion[InternalServerError] { ) implicit val schema: Schema[InternalServerError] = struct( - string.optional[InternalServerError]("message", _.message).addHints(smithy.api.Documentation("

The server encountered an internal error trying to fulfill the request.

")), + ErrorMessage.schema.optional[InternalServerError]("message", _.message).addHints(smithy.api.Documentation("

The server encountered an internal error trying to fulfill the request.

")), ){ InternalServerError.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/package.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/package.scala index b58b463ac..cc8ec5f7c 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/package.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/package.scala @@ -5,6 +5,7 @@ package object dynamodb { val DynamoDB = DynamoDBGen type ListTablesInputLimit = com.amazonaws.dynamodb.ListTablesInputLimit.Type + type ErrorMessage = com.amazonaws.dynamodb.ErrorMessage.Type type TableNameList = com.amazonaws.dynamodb.TableNameList.Type /** @param member *

An endpoint information details.

diff --git a/modules/sandbox/src/Sandbox.scala b/modules/sandbox/src/Sandbox.scala index 42be047d1..0febb1c58 100644 --- a/modules/sandbox/src/Sandbox.scala +++ b/modules/sandbox/src/Sandbox.scala @@ -23,8 +23,6 @@ import com.amazonaws.cloudwatch._ object Main extends IOApp.Simple { - type NextToken = String - override def run: IO[Unit] = cloudWatchClientResource.use(cloudWatchClient => listAll[ListMetricsOutput, Metric]( listF = maybeNextToken => From 8a84750eed503504597ac3beb8820cacfd3d8ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Tue, 1 Aug 2023 19:51:55 +0200 Subject: [PATCH 8/8] add missing newline --- .../codegen/transformers/AwsStandardTypesTransformerSpec.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala index 04c2a913b..31ba1dbde 100644 --- a/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/AwsStandardTypesTransformerSpec.scala @@ -269,7 +269,8 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { | val hints: Hints = Hints.empty | val underlyingSchema: Schema[List[Int]] = list(int).withId(id).addHints(hints) | implicit val schema: Schema[TestList] = bijection(underlyingSchema, asBijection) - |}""".stripMargin + |} + |""".stripMargin ) }