diff --git a/CHANGELOG.md b/CHANGELOG.md index a16f9e4f6..60b1eb67d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ See https://github.com/disneystreaming/smithy4s/pull/921 ### Structure Patterns Allows for marking string types with the `alloy#structurePattern` trait. This trait indicates that a given pattern, with parameters, applies to a given string and that this string should -actually be parsed into a structure where the members of the structure are derived from the parameters in the string pattern. +actually be parsed into a structure where the members of the structure are derived from the parameters in the string pattern. See https://github.com/disneystreaming/smithy4s/pull/942 @@ -42,6 +42,82 @@ When the smithy4sRenderOptics setting is enabled, Lenses and Prisms will be rend See https://github.com/disneystreaming/smithy4s/pull/1103 +# 0.17.14 + +* Only transform AWS shapes named after standard shapes in [#1127](https://github.com/disneystreaming/smithy4s/pull/1127) +* Fixes AWS AwsStandardTypesTransformer bug by in [1129](https://github.com/disneystreaming/smithy4s/pull/1129) + +# 0.17.13 + +* Backports Service interpreter logic introduced in [#908](https://github.com/disneystreaming/smithy4s/pull/908). +* Fixes rendering of deprecated annotations in mixins in [#1123](https://github.com/disneystreaming/smithy4s/pull/1123) + +# 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. 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/bootstrapped/src/generated/smithy4s/example/DeprecatedMixin.scala b/modules/bootstrapped/src/generated/smithy4s/example/DeprecatedMixin.scala new file mode 100644 index 000000000..d867965ee --- /dev/null +++ b/modules/bootstrapped/src/generated/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]] +} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/DeprecatedStructure.scala b/modules/bootstrapped/src/generated/smithy4s/example/DeprecatedStructure.scala index 479342bdb..b67a4763a 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/DeprecatedStructure.scala +++ b/modules/bootstrapped/src/generated/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") -final case class DeprecatedStructure(@deprecated(message = "N/A", since = "N/A") name: Option[String] = None, nameV2: Option[String] = None, strings: Option[List[String]] = None) +final 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/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 6f4cc1d81..9a323af13 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -761,7 +761,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)}" + ) + } ) } } @@ -1100,12 +1106,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/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/AwsStandardTypesTransformer.scala index 63f4353cd..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) } @@ -84,7 +88,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,15 +101,41 @@ 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 { + 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 f08f922a2..31ba1dbde 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 | - |final case class TestStructure(i: Int = 0, d: Option[Timestamp] = None) + |final 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) @@ -206,6 +217,63 @@ 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 @@ -234,6 +302,7 @@ final class AwsStandardTypesTransformerSpec extends munit.FunSuite { """|namespace com.amazonaws.kinesis | |timestamp Date + |timestamp Timestamp |""".stripMargin val transformer = new AwsStandardTypesTransformer() @@ -246,10 +315,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") { diff --git a/modules/core/src-2/kinds/package.scala b/modules/core/src-2/kinds/package.scala index 8af3b7b45..d1f4eee31 100644 --- a/modules/core/src-2/kinds/package.scala +++ b/modules/core/src-2/kinds/package.scala @@ -41,5 +41,4 @@ package object kinds { type Kind5[F[_, _, _, _, _]] = { type handler[I, E, O, SI, SO] = I => F[I, E, O, SI, SO] } - } 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 => diff --git a/sampleSpecs/deprecations.smithy b/sampleSpecs/deprecations.smithy index 40ea60a22..2c33857fb 100644 --- a/sampleSpecs/deprecations.smithy +++ b/sampleSpecs/deprecations.smithy @@ -4,10 +4,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