From dffd10d6f1ca203fd50a23bad358563cf4cd7748 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 6 Mar 2024 22:51:02 +0200 Subject: [PATCH 01/33] Add NewtypeValidated --- .../smithy4s/aws/AwsErrorTypeDecoder.scala | 2 +- .../com/amazonaws/dynamodb/DynamoDB.scala | 6 +- .../dynamodb/ListTablesInputLimit.scala | 12 +++- .../smithy4s/benchmark/BenchmarkService.scala | 4 +- .../smithy4s/example/AddMenuItemResult.scala | 2 +- .../smithy4s/example/BrandService.scala | 2 +- .../generated/smithy4s/example/CityId.scala | 12 +++- .../smithy4s/example/CitySummary.scala | 2 +- .../example/DiscriminatedService.scala | 2 +- .../smithy4s/example/DummyService.scala | 6 +- .../smithy4s/example/EchoInput.scala | 2 +- .../smithy4s/example/FooService.scala | 2 +- .../smithy4s/example/GetObjectOutput.scala | 2 +- .../smithy4s/example/HeadRequestOutput.scala | 2 +- .../smithy4s/example/HeaderEndpointData.scala | 8 +-- .../smithy4s/example/HeadersStruct.scala | 22 +++--- .../example/HeadersWithDefaults.scala | 2 +- .../smithy4s/example/HealthRequest.scala | 2 +- .../smithy4s/example/ObjectService.scala | 4 +- .../smithy4s/example/OperationInput.scala | 12 ++-- .../smithy4s/example/OperationOutput.scala | 6 +- .../smithy4s/example/PizzaAdminService.scala | 28 ++++---- .../smithy4s/example/PriceError.scala | 2 +- .../smithy4s/example/PutObjectInput.scala | 4 +- .../generated/smithy4s/example/Queries.scala | 22 +++--- .../example/QueriesWithDefaults.scala | 2 +- .../example/RecursiveInputService.scala | 2 +- .../smithy4s/example/ReservationInput.scala | 2 +- .../smithy4s/example/RoundTripData.scala | 4 +- .../example/ServiceWithNullsAndDefaults.scala | 2 +- .../smithy4s/example/TestInput.scala | 2 +- .../smithy4s/example/UnicodeRegexString.scala | 12 +++- .../example/UnwrappedValidatedFoo.scala | 21 ++++++ .../example/UnwrappedValidatedString.scala | 15 ++++ .../smithy4s/example/ValidatedFoo.scala | 21 ++++++ .../smithy4s/example/ValidatedString.scala | 21 ++++++ .../smithy4s/example/ValidationChecks.scala | 6 +- .../generated/smithy4s/example/Weather.scala | 4 +- .../collision/ReservedNameService.scala | 8 +-- .../guides/auth/HelloWorldAuthService.scala | 4 +- .../guides/hello/HelloWorldService.scala | 4 +- .../example/hello/HelloWorldService.scala | 2 +- .../smithy4s/example/hello/Person.scala | 2 +- .../smithy4s/example/imp/ImportService.scala | 2 +- .../generated/smithy4s/example/package.scala | 2 + .../ReservedNameOverrideService.scala | 2 +- .../smithy4s/example/test/HelloService.scala | 6 +- .../example/test/HelloWorldService.scala | 2 +- .../smithy4s/example/test/SayHelloInput.scala | 4 +- .../example/test/SayHelloOutput.scala | 2 +- .../generated/weather/WeatherService.scala | 2 +- .../internals/CollisionAvoidance.scala | 14 ++++ .../src/smithy4s/codegen/internals/IR.scala | 12 ++++ .../smithy4s/codegen/internals/Renderer.scala | 63 +++++++++++++++++ .../codegen/internals/SmithyToIR.scala | 29 +++++++- .../smithy4s/codegen/internals/ToLine.scala | 2 + .../ServerHttpComplianceTestCase.scala | 4 +- modules/core/src-2/NewtypeValidated.scala | 70 +++++++++++++++++++ modules/core/src-3/NewtypeValidated.scala | 66 +++++++++++++++++ modules/core/src/smithy4s/Hints.scala | 1 + .../UrlFormDataDecoderSchemaVisitor.scala | 2 +- .../UrlFormDataEncoderSchemaVisitor.scala | 2 +- sampleSpecs/validated-newtype.smithy | 22 ++++++ 63 files changed, 494 insertions(+), 119 deletions(-) create mode 100644 modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala create mode 100644 modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala create mode 100644 modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala create mode 100644 modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala create mode 100644 modules/core/src-2/NewtypeValidated.scala create mode 100644 modules/core/src-3/NewtypeValidated.scala create mode 100644 sampleSpecs/validated-newtype.smithy diff --git a/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala b/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala index 8150832c2..1e38b7bcc 100644 --- a/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala +++ b/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala @@ -90,7 +90,7 @@ object AwsErrorTypeDecoder { .optional[Body]("__type", _._1) val codeField = string .optional[Body]("code", _._2) - .addHints(smithy.api.XmlName("Code")) + .addHints(smithy.api.XmlName.unsafeApply("Code")) struct(__typeField, codeField)((_, _)) } } diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala index f41e79195..e82aebdff 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala @@ -62,8 +62,8 @@ object DynamoDBGen extends Service.Mixin[DynamoDBGen, DynamoDBOperation] { smithy.api.Title("Amazon DynamoDB"), aws.protocols.AwsJson1_0(http = None, eventStreamHttp = None), smithy.api.Documentation("Amazon DynamoDB\n\n\n

Amazon DynamoDB is a fully managed NoSQL database service that provides fast and\n predictable performance with seamless scalability. DynamoDB lets you offload the\n administrative burdens of operating and scaling a distributed database, so that you don\'t have\n to worry about hardware provisioning, setup and configuration, replication, software patching,\n or cluster scaling.

\n\n

With DynamoDB, you can create database tables that can store and retrieve any amount of\n data, and serve any level of request traffic. You can scale up or scale down your tables\'\n throughput capacity without downtime or performance degradation, and use the AWS Management\n Console to monitor resource utilization and performance metrics.

\n\n

DynamoDB automatically spreads the data and traffic for your tables over a sufficient\n number of servers to handle your throughput and storage requirements, while maintaining\n consistent and fast performance. All of your data is stored on solid state disks (SSDs) and\n automatically replicated across multiple Availability Zones in an AWS region, providing\n built-in high availability and data durability.

"), - aws.api.Service(sdkId = "DynamoDB", arnNamespace = Some(aws.api.ArnNamespace("dynamodb")), cloudFormationName = Some(aws.api.CloudFormationName("DynamoDB")), cloudTrailEventSource = Some("dynamodb.amazonaws.com"), docId = None, endpointPrefix = Some("dynamodb")), - smithy.api.XmlNamespace(uri = smithy.api.NonEmptyString("http://dynamodb.amazonaws.com/doc/2012-08-10/"), prefix = None), + aws.api.Service(sdkId = "DynamoDB", arnNamespace = Some(aws.api.ArnNamespace.unsafeApply("dynamodb")), cloudFormationName = Some(aws.api.CloudFormationName.unsafeApply("DynamoDB")), cloudTrailEventSource = Some("dynamodb.amazonaws.com"), docId = None, endpointPrefix = Some("dynamodb")), + smithy.api.XmlNamespace(uri = smithy.api.NonEmptyString.unsafeApply("http://dynamodb.amazonaws.com/doc/2012-08-10/"), prefix = None), aws.api.ClientEndpointDiscovery(operation = smithy4s.ShapeId(namespace = "com.amazonaws.dynamodb", name = "DescribeEndpoints"), error = Some(smithy4s.ShapeId(namespace = "com.amazonaws.dynamodb", name = "InvalidEndpointException"))), ).lazily @@ -136,7 +136,7 @@ object DynamoDBOperation { .withInput(ListTablesInput.schema) .withError(ListTablesError.errorSchema) .withOutput(ListTablesOutput.schema) - .withHints(aws.api.ClientDiscoveredEndpoint(required = false), smithy.api.Documentation("

Returns an array of table names associated with the current account and endpoint. The output\n from ListTables is paginated, with each page returning a maximum of 100 table\n names.

"), smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString("ExclusiveStartTableName")), outputToken = Some(smithy.api.NonEmptyString("LastEvaluatedTableName")), items = Some(smithy.api.NonEmptyString("TableNames")), pageSize = Some(smithy.api.NonEmptyString("Limit")))) + .withHints(aws.api.ClientDiscoveredEndpoint(required = false), smithy.api.Documentation("

Returns an array of table names associated with the current account and endpoint. The output\n from ListTables is paginated, with each page returning a maximum of 100 table\n names.

"), smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString.unsafeApply("ExclusiveStartTableName")), outputToken = Some(smithy.api.NonEmptyString.unsafeApply("LastEvaluatedTableName")), items = Some(smithy.api.NonEmptyString.unsafeApply("TableNames")), pageSize = Some(smithy.api.NonEmptyString.unsafeApply("Limit")))) def wrap(input: ListTablesInput): ListTables = ListTables(input) } sealed trait ListTablesError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala index 40e5e5e62..ea82e5127 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala @@ -1,17 +1,23 @@ package com.amazonaws.dynamodb import smithy4s.Hints -import smithy4s.Newtype +import smithy4s.NewtypeValidated import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.int -object ListTablesInputLimit extends Newtype[Int] { +object ListTablesInputLimit extends NewtypeValidated[Int] { val id: ShapeId = ShapeId("com.amazonaws.dynamodb", "ListTablesInputLimit") val hints: Hints = Hints( smithy.api.Box(), ).lazily val underlyingSchema: Schema[Int] = int.withId(id).addHints(hints).validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(100.0)))) - implicit val schema: Schema[ListTablesInputLimit] = bijection(underlyingSchema, asBijection) + implicit val schema: Schema[ListTablesInputLimit] = bijection(underlyingSchema, asBijectionUnsafe) + val validators: List[Int => Either[String, Int]] = List( + a => validateInternal(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(100.0))))(a) + ) + @inline def apply(a: Int): Either[String, ListTablesInputLimit] = validators + .foldLeft(Right(a): Either[String, Int])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) } diff --git a/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala b/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala index e1ef29d50..c57a75743 100644 --- a/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala @@ -81,7 +81,7 @@ object BenchmarkServiceOperation { val schema: OperationSchema[CreateObjectInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.benchmark", "CreateObject")) .withInput(CreateObjectInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/complex/{bucketName}/{key}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/complex/{bucketName}/{key}"), code = 200)) def wrap(input: CreateObjectInput): CreateObject = CreateObject(input) } final case class SendString(input: SendStringInput) extends BenchmarkServiceOperation[SendStringInput, Nothing, Unit, Nothing, Nothing] { @@ -93,7 +93,7 @@ object BenchmarkServiceOperation { val schema: OperationSchema[SendStringInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.benchmark", "SendString")) .withInput(SendStringInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/simple/{bucketName}/{key}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/simple/{bucketName}/{key}"), code = 200)) def wrap(input: SendStringInput): SendString = SendString(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala b/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala index 6cf0d7d8e..f226f4b46 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala @@ -18,7 +18,7 @@ object AddMenuItemResult extends ShapeTag.Companion[AddMenuItemResult] { implicit val schema: Schema[AddMenuItemResult] = struct( string.required[AddMenuItemResult]("itemId", _.itemId).addHints(smithy.api.HttpPayload()), - timestamp.required[AddMenuItemResult]("added", _.added).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader("X-ADDED-AT")), + timestamp.required[AddMenuItemResult]("added", _.added).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader.unsafeApply("X-ADDED-AT")), ){ AddMenuItemResult.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala b/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala index 5f9d88637..b7c3c347a 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala @@ -77,7 +77,7 @@ object BrandServiceOperation { val schema: OperationSchema[AddBrandsInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "AddBrands")) .withInput(AddBrandsInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/brands"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/brands"), code = 200)) def wrap(input: AddBrandsInput): AddBrands = AddBrands(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala b/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala index fa6d6b74d..808f17552 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala @@ -1,15 +1,21 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.Newtype +import smithy4s.NewtypeValidated import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object CityId extends Newtype[String] { +object CityId extends NewtypeValidated[String] { val id: ShapeId = ShapeId("smithy4s.example", "CityId") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Pattern(s"^[A-Za-z0-9 ]+$$")) - implicit val schema: Schema[CityId] = bijection(underlyingSchema, asBijection) + implicit val schema: Schema[CityId] = bijection(underlyingSchema, asBijectionUnsafe) + val validators: List[String => Either[String, String]] = List( + a => validateInternal(smithy.api.Pattern(s"^[A-Za-z0-9 ]+$$"))(a) + ) + @inline def apply(a: String): Either[String, CityId] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala b/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala index fac0553d8..9f699caa9 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala @@ -13,7 +13,7 @@ object CitySummary extends ShapeTag.Companion[CitySummary] { val id: ShapeId = ShapeId("smithy4s.example", "CitySummary") val hints: Hints = Hints( - smithy.api.References(List(smithy.api.Reference(resource = smithy.api.NonEmptyString("smithy4s.example#City"), ids = None, service = None, rel = None))), + smithy.api.References(List(smithy.api.Reference(resource = smithy.api.NonEmptyString.unsafeApply("smithy4s.example#City"), ids = None, service = None, rel = None))), ).lazily implicit val schema: Schema[CitySummary] = struct( diff --git a/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala b/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala index 8c7cb21d2..59eca7090 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala @@ -78,7 +78,7 @@ object DiscriminatedServiceOperation { val schema: OperationSchema[TestDiscriminatedInput, Nothing, TestDiscriminatedOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "TestDiscriminated")) .withInput(TestDiscriminatedInput.schema) .withOutput(TestDiscriminatedOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test/{key}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test/{key}"), code = 200), smithy.api.Readonly()) def wrap(input: TestDiscriminatedInput): TestDiscriminated = TestDiscriminated(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala b/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala index 80b0a8750..d5faa7ec2 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala @@ -91,7 +91,7 @@ object DummyServiceOperation { val schema: OperationSchema[Queries, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Dummy")) .withInput(Queries.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy"), code = 200), smithy.api.Readonly()) def wrap(input: Queries): Dummy = Dummy(input) } final case class DummyHostPrefix(input: HostLabelInput) extends DummyServiceOperation[HostLabelInput, Nothing, Unit, Nothing, Nothing] { @@ -103,7 +103,7 @@ object DummyServiceOperation { val schema: OperationSchema[HostLabelInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "DummyHostPrefix")) .withInput(HostLabelInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy"), code = 200), smithy.api.Endpoint(hostPrefix = smithy.api.NonEmptyString("foo.{label1}--abc{label2}.{label3}.secure."))) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy"), code = 200), smithy.api.Endpoint(hostPrefix = smithy.api.NonEmptyString.unsafeApply("foo.{label1}--abc{label2}.{label3}.secure."))) def wrap(input: HostLabelInput): DummyHostPrefix = DummyHostPrefix(input) } final case class DummyPath(input: PathParams) extends DummyServiceOperation[PathParams, Nothing, Unit, Nothing, Nothing] { @@ -115,7 +115,7 @@ object DummyServiceOperation { val schema: OperationSchema[PathParams, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "DummyPath")) .withInput(PathParams.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy-path/{str}/{int}/{ts1}/{ts2}/{ts3}/{ts4}/{b}/{ie}?value=foo&baz=bar"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy-path/{str}/{int}/{ts1}/{ts2}/{ts3}/{ts4}/{b}/{ie}?value=foo&baz=bar"), code = 200), smithy.api.Readonly()) def wrap(input: PathParams): DummyPath = DummyPath(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala index d0163a892..0c4e1faaf 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala @@ -17,7 +17,7 @@ object EchoInput extends ShapeTag.Companion[EchoInput] { implicit val schema: Schema[EchoInput] = struct( string.validated(smithy.api.Length(min = Some(10L), max = None)).required[EchoInput]("pathParam", _.pathParam).addHints(smithy.api.HttpLabel()), EchoBody.schema.required[EchoInput]("body", _.body).addHints(smithy.api.HttpPayload()), - string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[EchoInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery("queryParam")), + string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[EchoInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery.unsafeApply("queryParam")), ){ EchoInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala b/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala index 52ba798dc..45be65f5f 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala @@ -87,7 +87,7 @@ object FooServiceOperation { val schema: OperationSchema[Unit, Nothing, GetFooOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "GetFoo")) .withInput(unit) .withOutput(GetFooOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/foo"), code = 200), smithy.api.Documentation("Returns a useful Foo\nNo input necessary to find our Foo\nThe path for this operation is \"/foo\""), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/foo"), code = 200), smithy.api.Documentation("Returns a useful Foo\nNo input necessary to find our Foo\nThe path for this operation is \"/foo\""), smithy.api.Readonly()) def wrap(input: Unit): GetFoo = GetFoo() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala index 642ee7084..a22efed07 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala @@ -15,7 +15,7 @@ object GetObjectOutput extends ShapeTag.Companion[GetObjectOutput] { val hints: Hints = Hints.empty implicit val schema: Schema[GetObjectOutput] = struct( - ObjectSize.schema.required[GetObjectOutput]("size", _.size).addHints(smithy.api.HttpHeader("X-Size")), + ObjectSize.schema.required[GetObjectOutput]("size", _.size).addHints(smithy.api.HttpHeader.unsafeApply("X-Size")), string.optional[GetObjectOutput]("data", _.data).addHints(smithy.api.HttpPayload()), ){ GetObjectOutput.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala index 5787aeb46..e12f0e084 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala @@ -15,7 +15,7 @@ object HeadRequestOutput extends ShapeTag.Companion[HeadRequestOutput] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadRequestOutput] = struct( - string.required[HeadRequestOutput]("test", _.test).addHints(smithy.api.HttpHeader("Test")), + string.required[HeadRequestOutput]("test", _.test).addHints(smithy.api.HttpHeader.unsafeApply("Test")), ){ HeadRequestOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala index 7e4913cba..c4eded2a4 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala @@ -15,10 +15,10 @@ object HeaderEndpointData extends ShapeTag.Companion[HeaderEndpointData] { val hints: Hints = Hints.empty implicit val schema: Schema[HeaderEndpointData] = struct( - string.optional[HeaderEndpointData]("uppercaseHeader", _.uppercaseHeader).addHints(smithy.api.HttpHeader("X-UPPERCASE-HEADER")), - string.optional[HeaderEndpointData]("capitalizedHeader", _.capitalizedHeader).addHints(smithy.api.HttpHeader("X-Capitalized-Header")), - string.optional[HeaderEndpointData]("lowercaseHeader", _.lowercaseHeader).addHints(smithy.api.HttpHeader("x-lowercase-header")), - string.optional[HeaderEndpointData]("mixedHeader", _.mixedHeader).addHints(smithy.api.HttpHeader("x-MiXeD-hEaDEr")), + string.optional[HeaderEndpointData]("uppercaseHeader", _.uppercaseHeader).addHints(smithy.api.HttpHeader.unsafeApply("X-UPPERCASE-HEADER")), + string.optional[HeaderEndpointData]("capitalizedHeader", _.capitalizedHeader).addHints(smithy.api.HttpHeader.unsafeApply("X-Capitalized-Header")), + string.optional[HeaderEndpointData]("lowercaseHeader", _.lowercaseHeader).addHints(smithy.api.HttpHeader.unsafeApply("x-lowercase-header")), + string.optional[HeaderEndpointData]("mixedHeader", _.mixedHeader).addHints(smithy.api.HttpHeader.unsafeApply("x-MiXeD-hEaDEr")), ){ HeaderEndpointData.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala index 195351dfa..599b64e15 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala @@ -19,17 +19,17 @@ object HeadersStruct extends ShapeTag.Companion[HeadersStruct] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadersStruct] = struct( - string.optional[HeadersStruct]("str", _.str).addHints(smithy.api.HttpHeader("str")), - int.optional[HeadersStruct]("int", _.int).addHints(smithy.api.HttpHeader("int")), - timestamp.optional[HeadersStruct]("ts1", _.ts1).addHints(smithy.api.HttpHeader("ts1")), - timestamp.optional[HeadersStruct]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpHeader("ts2")), - timestamp.optional[HeadersStruct]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader("ts3")), - timestamp.optional[HeadersStruct]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpHeader("ts4")), - boolean.optional[HeadersStruct]("b", _.b).addHints(smithy.api.HttpHeader("b")), - StringList.underlyingSchema.optional[HeadersStruct]("sl", _.sl).addHints(smithy.api.HttpHeader("sl")), - Numbers.schema.optional[HeadersStruct]("ie", _.ie).addHints(smithy.api.HttpHeader("nums")), - OpenNums.schema.optional[HeadersStruct]("on", _.on).addHints(smithy.api.HttpHeader("openNums")), - OpenNumsStr.schema.optional[HeadersStruct]("ons", _.ons).addHints(smithy.api.HttpHeader("openNumsStr")), + string.optional[HeadersStruct]("str", _.str).addHints(smithy.api.HttpHeader.unsafeApply("str")), + int.optional[HeadersStruct]("int", _.int).addHints(smithy.api.HttpHeader.unsafeApply("int")), + timestamp.optional[HeadersStruct]("ts1", _.ts1).addHints(smithy.api.HttpHeader.unsafeApply("ts1")), + timestamp.optional[HeadersStruct]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpHeader.unsafeApply("ts2")), + timestamp.optional[HeadersStruct]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader.unsafeApply("ts3")), + timestamp.optional[HeadersStruct]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpHeader.unsafeApply("ts4")), + boolean.optional[HeadersStruct]("b", _.b).addHints(smithy.api.HttpHeader.unsafeApply("b")), + StringList.underlyingSchema.optional[HeadersStruct]("sl", _.sl).addHints(smithy.api.HttpHeader.unsafeApply("sl")), + Numbers.schema.optional[HeadersStruct]("ie", _.ie).addHints(smithy.api.HttpHeader.unsafeApply("nums")), + OpenNums.schema.optional[HeadersStruct]("on", _.on).addHints(smithy.api.HttpHeader.unsafeApply("openNums")), + OpenNumsStr.schema.optional[HeadersStruct]("ons", _.ons).addHints(smithy.api.HttpHeader.unsafeApply("openNumsStr")), StringMap.underlyingSchema.optional[HeadersStruct]("slm", _.slm).addHints(smithy.api.HttpPrefixHeaders("foo-")), ){ HeadersStruct.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala index da79656e5..1714cb7fd 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala @@ -15,7 +15,7 @@ object HeadersWithDefaults extends ShapeTag.Companion[HeadersWithDefaults] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadersWithDefaults] = struct( - string.field[HeadersWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpHeader("dflt")), + string.field[HeadersWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpHeader.unsafeApply("dflt")), ){ HeadersWithDefaults.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala b/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala index bdd5ff029..bd0d84c0c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala @@ -15,7 +15,7 @@ object HealthRequest extends ShapeTag.Companion[HealthRequest] { val hints: Hints = Hints.empty implicit val schema: Schema[HealthRequest] = struct( - string.validated(smithy.api.Length(min = Some(0L), max = Some(5L))).optional[HealthRequest]("query", _.query).addHints(smithy.api.HttpQuery("query")), + string.validated(smithy.api.Length(min = Some(0L), max = Some(5L))).optional[HealthRequest]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("query")), ){ HealthRequest.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala b/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala index eaadab04b..d2b55868e 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala @@ -98,7 +98,7 @@ object ObjectServiceOperation { .withInput(PutObjectInput.schema) .withError(PutObjectError.errorSchema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("PUT"), uri = smithy.api.NonEmptyString("/{bucketName}/{key}"), code = 200), smithy.api.Idempotent()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("PUT"), uri = smithy.api.NonEmptyString.unsafeApply("/{bucketName}/{key}"), code = 200), smithy.api.Idempotent()) def wrap(input: PutObjectInput): PutObject = PutObject(input) } sealed trait PutObjectError extends scala.Product with scala.Serializable { self => @@ -191,7 +191,7 @@ object ObjectServiceOperation { .withInput(GetObjectInput.schema) .withError(GetObjectError.errorSchema) .withOutput(GetObjectOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/{bucketName}/{key}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/{bucketName}/{key}"), code = 200), smithy.api.Readonly()) def wrap(input: GetObjectInput): GetObject = GetObject(input) } sealed trait GetObjectError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala index 6640a2556..e0637ced2 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala @@ -20,13 +20,13 @@ object OperationInput extends ShapeTag.Companion[OperationInput] { string.field[OperationInput]("optionalWithDefault", _.optionalWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-default"))), string.required[OperationInput]("requiredLabel", _.requiredLabel).addHints(smithy.api.Default(smithy4s.Document.fromString("required-label-with-default")), smithy.api.HttpLabel()), string.required[OperationInput]("requiredWithDefault", _.requiredWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-default"))), - string.field[OperationInput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader("optional-header-with-default")), - string.required[OperationInput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader("required-header-with-default")), - string.field[OperationInput]("optionalQueryWithDefault", _.optionalQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-query-with-default")), smithy.api.HttpQuery("optional-query-with-default")), - string.field[OperationInput]("requiredQueryWithDefault", _.requiredQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-query-with-default")), smithy.api.HttpQuery("required-query-with-default")), + string.field[OperationInput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader.unsafeApply("optional-header-with-default")), + string.required[OperationInput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader.unsafeApply("required-header-with-default")), + string.field[OperationInput]("optionalQueryWithDefault", _.optionalQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-query-with-default")), smithy.api.HttpQuery.unsafeApply("optional-query-with-default")), + string.field[OperationInput]("requiredQueryWithDefault", _.requiredQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-query-with-default")), smithy.api.HttpQuery.unsafeApply("required-query-with-default")), string.optional[OperationInput]("optional", _.optional), - string.optional[OperationInput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader("optional-header")), - string.optional[OperationInput]("optionalQuery", _.optionalQuery).addHints(smithy.api.HttpQuery("optional-query")), + string.optional[OperationInput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader.unsafeApply("optional-header")), + string.optional[OperationInput]("optionalQuery", _.optionalQuery).addHints(smithy.api.HttpQuery.unsafeApply("optional-query")), ){ OperationInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala index c059e1ac9..13d671ba1 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala @@ -19,10 +19,10 @@ object OperationOutput extends ShapeTag.Companion[OperationOutput] { implicit val schema: Schema[OperationOutput] = struct( string.field[OperationOutput]("optionalWithDefault", _.optionalWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-default"))), string.required[OperationOutput]("requiredWithDefault", _.requiredWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-default"))), - string.field[OperationOutput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader("optional-header-with-default")), - string.required[OperationOutput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader("required-header-with-default")), + string.field[OperationOutput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader.unsafeApply("optional-header-with-default")), + string.required[OperationOutput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader.unsafeApply("required-header-with-default")), string.optional[OperationOutput]("optional", _.optional), - string.optional[OperationOutput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader("optional-header")), + string.optional[OperationOutput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader.unsafeApply("optional-header")), ){ OperationOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala b/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala index 21a8c652e..839c9e764 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala @@ -147,7 +147,7 @@ object PizzaAdminServiceOperation { .withInput(AddMenuItemRequest.schema) .withError(AddMenuItemError.errorSchema) .withOutput(AddMenuItemResult.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/restaurant/{restaurant}/menu/item"), code = 201)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/restaurant/{restaurant}/menu/item"), code = 201)) def wrap(input: AddMenuItemRequest): AddMenuItem = AddMenuItem(input) } sealed trait AddMenuItemError extends scala.Product with scala.Serializable { self => @@ -240,7 +240,7 @@ object PizzaAdminServiceOperation { .withInput(GetMenuRequest.schema) .withError(GetMenuError.errorSchema) .withOutput(GetMenuResult.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/restaurant/{restaurant}/menu"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/restaurant/{restaurant}/menu"), code = 200), smithy.api.Readonly()) def wrap(input: GetMenuRequest): GetMenu = GetMenu(input) } sealed trait GetMenuError extends scala.Product with scala.Serializable { self => @@ -347,7 +347,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, VersionOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Version")) .withInput(unit) .withOutput(VersionOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/version"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/version"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): Version = Version() } final case class Health(input: HealthRequest) extends PizzaAdminServiceOperation[HealthRequest, PizzaAdminServiceOperation.HealthError, HealthResponse, Nothing, Nothing] { @@ -360,7 +360,7 @@ object PizzaAdminServiceOperation { .withInput(HealthRequest.schema) .withError(HealthError.errorSchema) .withOutput(HealthResponse.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/health"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/health"), code = 200), smithy.api.Readonly()) def wrap(input: HealthRequest): Health = Health(input) } sealed trait HealthError extends scala.Product with scala.Serializable { self => @@ -424,7 +424,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[HeaderEndpointData, Nothing, HeaderEndpointData, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "HeaderEndpoint")) .withInput(HeaderEndpointData.schema) .withOutput(HeaderEndpointData.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/headers/"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/headers/"), code = 200)) def wrap(input: HeaderEndpointData): HeaderEndpoint = HeaderEndpoint(input) } final case class RoundTrip(input: RoundTripData) extends PizzaAdminServiceOperation[RoundTripData, Nothing, RoundTripData, Nothing, Nothing] { @@ -436,7 +436,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[RoundTripData, Nothing, RoundTripData, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "RoundTrip")) .withInput(RoundTripData.schema) .withOutput(RoundTripData.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/roundTrip/{label}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/roundTrip/{label}"), code = 200)) def wrap(input: RoundTripData): RoundTrip = RoundTrip(input) } final case class GetEnum(input: GetEnumInput) extends PizzaAdminServiceOperation[GetEnumInput, PizzaAdminServiceOperation.GetEnumError, GetEnumOutput, Nothing, Nothing] { @@ -449,7 +449,7 @@ object PizzaAdminServiceOperation { .withInput(GetEnumInput.schema) .withError(GetEnumError.errorSchema) .withOutput(GetEnumOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/get-enum/{aa}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/get-enum/{aa}"), code = 200), smithy.api.Readonly()) def wrap(input: GetEnumInput): GetEnum = GetEnum(input) } sealed trait GetEnumError extends scala.Product with scala.Serializable { self => @@ -514,7 +514,7 @@ object PizzaAdminServiceOperation { .withInput(GetIntEnumInput.schema) .withError(GetIntEnumError.errorSchema) .withOutput(GetIntEnumOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/get-int-enum/{aa}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/get-int-enum/{aa}"), code = 200), smithy.api.Readonly()) def wrap(input: GetIntEnumInput): GetIntEnum = GetIntEnum(input) } sealed trait GetIntEnumError extends scala.Product with scala.Serializable { self => @@ -579,7 +579,7 @@ object PizzaAdminServiceOperation { .withInput(CustomCodeInput.schema) .withError(CustomCodeError.errorSchema) .withOutput(CustomCodeOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/custom-code/{code}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/custom-code/{code}"), code = 200), smithy.api.Readonly()) def wrap(input: CustomCodeInput): CustomCode = CustomCode(input) } sealed trait CustomCodeError extends scala.Product with scala.Serializable { self => @@ -643,7 +643,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[ReservationInput, Nothing, ReservationOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Reservation")) .withInput(ReservationInput.schema) .withOutput(ReservationOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/book/{name}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/book/{name}"), code = 200)) def wrap(input: ReservationInput): Reservation = Reservation(input) } final case class Echo(input: EchoInput) extends PizzaAdminServiceOperation[EchoInput, Nothing, Unit, Nothing, Nothing] { @@ -655,7 +655,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[EchoInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Echo")) .withInput(EchoInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/echo/{pathParam}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/echo/{pathParam}"), code = 200)) def wrap(input: EchoInput): Echo = Echo(input) } final case class OptionalOutput() extends PizzaAdminServiceOperation[Unit, Nothing, OptionalOutputOutput, Nothing, Nothing] { @@ -668,7 +668,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, OptionalOutputOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "OptionalOutput")) .withInput(unit) .withOutput(OptionalOutputOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/optional-output"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/optional-output"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): OptionalOutput = OptionalOutput() } final case class HeadRequest() extends PizzaAdminServiceOperation[Unit, Nothing, HeadRequestOutput, Nothing, Nothing] { @@ -681,7 +681,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, HeadRequestOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "HeadRequest")) .withInput(unit) .withOutput(HeadRequestOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("HEAD"), uri = smithy.api.NonEmptyString("/head-request"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("HEAD"), uri = smithy.api.NonEmptyString.unsafeApply("/head-request"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): HeadRequest = HeadRequest() } final case class NoContentRequest() extends PizzaAdminServiceOperation[Unit, Nothing, Unit, Nothing, Nothing] { @@ -694,7 +694,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "NoContentRequest")) .withInput(unit) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/no-content"), code = 204), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/no-content"), code = 204), smithy.api.Readonly()) def wrap(input: Unit): NoContentRequest = NoContentRequest() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala b/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala index 017b2d358..7ee3b2b21 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala @@ -22,7 +22,7 @@ object PriceError extends ShapeTag.Companion[PriceError] { implicit val schema: Schema[PriceError] = struct( string.required[PriceError]("message", _.message), - int.required[PriceError]("code", _.code).addHints(smithy.api.HttpHeader("X-CODE")), + int.required[PriceError]("code", _.code).addHints(smithy.api.HttpHeader.unsafeApply("X-CODE")), ){ PriceError.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala index e9b408d9e..2c6d7d980 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala @@ -21,8 +21,8 @@ object PutObjectInput extends ShapeTag.Companion[PutObjectInput] { ObjectKey.schema.required[PutObjectInput]("key", _.key).addHints(smithy.api.HttpLabel()), BucketName.schema.required[PutObjectInput]("bucketName", _.bucketName).addHints(smithy.api.HttpLabel()), string.required[PutObjectInput]("data", _.data).addHints(smithy.api.HttpPayload()), - LowHigh.schema.optional[PutObjectInput]("foo", _.foo).addHints(smithy.api.HttpHeader("X-Foo")), - SomeValue.schema.optional[PutObjectInput]("someValue", _.someValue).addHints(smithy.api.HttpQuery("paramName")), + LowHigh.schema.optional[PutObjectInput]("foo", _.foo).addHints(smithy.api.HttpHeader.unsafeApply("X-Foo")), + SomeValue.schema.optional[PutObjectInput]("someValue", _.someValue).addHints(smithy.api.HttpQuery.unsafeApply("paramName")), ){ PutObjectInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala b/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala index 602f663d6..95e49bbc6 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala @@ -19,17 +19,17 @@ object Queries extends ShapeTag.Companion[Queries] { val hints: Hints = Hints.empty implicit val schema: Schema[Queries] = struct( - string.optional[Queries]("str", _.str).addHints(smithy.api.HttpQuery("str")), - int.optional[Queries]("int", _.int).addHints(smithy.api.HttpQuery("int")), - timestamp.optional[Queries]("ts1", _.ts1).addHints(smithy.api.HttpQuery("ts1")), - timestamp.optional[Queries]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpQuery("ts2")), - timestamp.optional[Queries]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpQuery("ts3")), - timestamp.optional[Queries]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpQuery("ts4")), - boolean.optional[Queries]("b", _.b).addHints(smithy.api.HttpQuery("b")), - StringList.underlyingSchema.optional[Queries]("sl", _.sl).addHints(smithy.api.HttpQuery("sl")), - Numbers.schema.optional[Queries]("ie", _.ie).addHints(smithy.api.HttpQuery("nums")), - OpenNums.schema.optional[Queries]("on", _.on).addHints(smithy.api.HttpQuery("openNums")), - OpenNumsStr.schema.optional[Queries]("ons", _.ons).addHints(smithy.api.HttpQuery("openNumsStr")), + string.optional[Queries]("str", _.str).addHints(smithy.api.HttpQuery.unsafeApply("str")), + int.optional[Queries]("int", _.int).addHints(smithy.api.HttpQuery.unsafeApply("int")), + timestamp.optional[Queries]("ts1", _.ts1).addHints(smithy.api.HttpQuery.unsafeApply("ts1")), + timestamp.optional[Queries]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpQuery.unsafeApply("ts2")), + timestamp.optional[Queries]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpQuery.unsafeApply("ts3")), + timestamp.optional[Queries]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpQuery.unsafeApply("ts4")), + boolean.optional[Queries]("b", _.b).addHints(smithy.api.HttpQuery.unsafeApply("b")), + StringList.underlyingSchema.optional[Queries]("sl", _.sl).addHints(smithy.api.HttpQuery.unsafeApply("sl")), + Numbers.schema.optional[Queries]("ie", _.ie).addHints(smithy.api.HttpQuery.unsafeApply("nums")), + OpenNums.schema.optional[Queries]("on", _.on).addHints(smithy.api.HttpQuery.unsafeApply("openNums")), + OpenNumsStr.schema.optional[Queries]("ons", _.ons).addHints(smithy.api.HttpQuery.unsafeApply("openNumsStr")), StringMap.underlyingSchema.optional[Queries]("slm", _.slm).addHints(smithy.api.HttpQueryParams()), ){ Queries.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala index 6b7849756..3f38a00fc 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala @@ -15,7 +15,7 @@ object QueriesWithDefaults extends ShapeTag.Companion[QueriesWithDefaults] { val hints: Hints = Hints.empty implicit val schema: Schema[QueriesWithDefaults] = struct( - string.field[QueriesWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpQuery("dflt")), + string.field[QueriesWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpQuery.unsafeApply("dflt")), ){ QueriesWithDefaults.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala b/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala index 9c6dcc710..e4a9e1430 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala @@ -79,7 +79,7 @@ object RecursiveInputServiceOperation { val schema: OperationSchema[RecursiveInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "RecursiveInputOperation")) .withInput(RecursiveInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("PUT"), uri = smithy.api.NonEmptyString("/subscriptions"), code = 200), smithy.api.Idempotent()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("PUT"), uri = smithy.api.NonEmptyString.unsafeApply("/subscriptions"), code = 200), smithy.api.Idempotent()) def wrap(input: RecursiveInput): RecursiveInputOperation = RecursiveInputOperation(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala index d5bf33d33..ff452e074 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala @@ -18,7 +18,7 @@ object ReservationInput extends ShapeTag.Companion[ReservationInput] { implicit val schema: Schema[ReservationInput] = struct( string.required[ReservationInput]("name", _.name).addHints(smithy.api.HttpLabel()), - string.optional[ReservationInput]("town", _.town).addHints(smithy.api.HttpQuery("town")), + string.optional[ReservationInput]("town", _.town).addHints(smithy.api.HttpQuery.unsafeApply("town")), ){ ReservationInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala b/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala index 7c9082350..7e0a9df6d 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala @@ -16,8 +16,8 @@ object RoundTripData extends ShapeTag.Companion[RoundTripData] { implicit val schema: Schema[RoundTripData] = struct( string.required[RoundTripData]("label", _.label).addHints(smithy.api.HttpLabel()), - string.optional[RoundTripData]("header", _.header).addHints(smithy.api.HttpHeader("HEADER")), - string.optional[RoundTripData]("query", _.query).addHints(smithy.api.HttpQuery("query")), + string.optional[RoundTripData]("header", _.header).addHints(smithy.api.HttpHeader.unsafeApply("HEADER")), + string.optional[RoundTripData]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("query")), string.optional[RoundTripData]("body", _.body), ){ RoundTripData.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala index 61fc1b5e3..e1d3d1d4f 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala @@ -78,7 +78,7 @@ object ServiceWithNullsAndDefaultsOperation { val schema: OperationSchema[OperationInput, Nothing, OperationOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Operation")) .withInput(OperationInput.schema) .withOutput(OperationOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/operation/{requiredLabel}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/operation/{requiredLabel}"), code = 200)) def wrap(input: OperationInput): Operation = Operation(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala index a1b180044..16a3ee3b2 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala @@ -24,7 +24,7 @@ object TestInput extends ShapeTag.Companion[TestInput] { implicit val schema: Schema[TestInput] = struct( string.validated(smithy.api.Length(min = Some(10L), max = None)).required[TestInput]("pathParam", _.pathParam).addHints(smithy.api.HttpLabel()), TestBody.schema.required[TestInput]("body", _.body).addHints(smithy.api.HttpPayload()), - string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[TestInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery("queryParam")), + string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[TestInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery.unsafeApply("queryParam")), ){ TestInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala b/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala index 27f704393..cb5b2e27d 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala @@ -1,15 +1,21 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.Newtype +import smithy4s.NewtypeValidated import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object UnicodeRegexString extends Newtype[String] { +object UnicodeRegexString extends NewtypeValidated[String] { val id: ShapeId = ShapeId("smithy4s.example", "UnicodeRegexString") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Pattern(s"^\uD83D\uDE0E$$")) - implicit val schema: Schema[UnicodeRegexString] = bijection(underlyingSchema, asBijection) + implicit val schema: Schema[UnicodeRegexString] = bijection(underlyingSchema, asBijectionUnsafe) + val validators: List[String => Either[String, String]] = List( + a => validateInternal(smithy.api.Pattern(s"^\uD83D\uDE0E$$"))(a) + ) + @inline def apply(a: String): Either[String, UnicodeRegexString] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala b/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala new file mode 100644 index 000000000..6d2254e00 --- /dev/null +++ b/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala @@ -0,0 +1,21 @@ +package smithy4s.example + +import smithy4s.Hints +import smithy4s.Schema +import smithy4s.ShapeId +import smithy4s.ShapeTag +import smithy4s.schema.Schema.struct + +final case class UnwrappedValidatedFoo(name: Option[String] = None) + +object UnwrappedValidatedFoo extends ShapeTag.Companion[UnwrappedValidatedFoo] { + val id: ShapeId = ShapeId("smithy4s.example", "UnwrappedValidatedFoo") + + val hints: Hints = Hints.empty + + implicit val schema: Schema[UnwrappedValidatedFoo] = struct( + UnwrappedValidatedString.underlyingSchema.optional[UnwrappedValidatedFoo]("name", _.name), + ){ + UnwrappedValidatedFoo.apply + }.withId(id).addHints(hints) +} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala new file mode 100644 index 000000000..dc84551bf --- /dev/null +++ b/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala @@ -0,0 +1,15 @@ +package smithy4s.example + +import smithy4s.Hints +import smithy4s.Newtype +import smithy4s.Schema +import smithy4s.ShapeId +import smithy4s.schema.Schema.bijection +import smithy4s.schema.Schema.string + +object UnwrappedValidatedString extends Newtype[String] { + val id: ShapeId = ShapeId("smithy4s.example", "UnwrappedValidatedString") + val hints: Hints = Hints.empty + val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) + implicit val schema: Schema[UnwrappedValidatedString] = bijection(underlyingSchema, asBijection) +} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala new file mode 100644 index 000000000..d8a57727a --- /dev/null +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala @@ -0,0 +1,21 @@ +package smithy4s.example + +import smithy4s.Hints +import smithy4s.Schema +import smithy4s.ShapeId +import smithy4s.ShapeTag +import smithy4s.schema.Schema.struct + +final case class ValidatedFoo(name: Option[ValidatedString] = None) + +object ValidatedFoo extends ShapeTag.Companion[ValidatedFoo] { + val id: ShapeId = ShapeId("smithy4s.example", "ValidatedFoo") + + val hints: Hints = Hints.empty + + implicit val schema: Schema[ValidatedFoo] = struct( + ValidatedString.schema.optional[ValidatedFoo]("name", _.name), + ){ + ValidatedFoo.apply + }.withId(id).addHints(hints) +} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala new file mode 100644 index 000000000..e40ca23f6 --- /dev/null +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala @@ -0,0 +1,21 @@ +package smithy4s.example + +import smithy4s.Hints +import smithy4s.NewtypeValidated +import smithy4s.Schema +import smithy4s.ShapeId +import smithy4s.schema.Schema.bijection +import smithy4s.schema.Schema.string + +object ValidatedString extends NewtypeValidated[String] { + val id: ShapeId = ShapeId("smithy4s.example", "ValidatedString") + val hints: Hints = Hints.empty + val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) + implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijectionUnsafe) + val validators: List[String => Either[String, String]] = List( + a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) + ) + @inline def apply(a: String): Either[String, ValidatedString] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) +} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala index af58ae91c..3b8fff68d 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala @@ -16,9 +16,9 @@ object ValidationChecks extends ShapeTag.Companion[ValidationChecks] { val hints: Hints = Hints.empty implicit val schema: Schema[ValidationChecks] = struct( - string.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("str", _.str).addHints(smithy.api.HttpQuery("str")), - StringList.underlyingSchema.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("lst", _.lst).addHints(smithy.api.HttpQuery("lst")), - int.validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(10.0)))).optional[ValidationChecks]("int", _.int).addHints(smithy.api.HttpQuery("int")), + string.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("str", _.str).addHints(smithy.api.HttpQuery.unsafeApply("str")), + StringList.underlyingSchema.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("lst", _.lst).addHints(smithy.api.HttpQuery.unsafeApply("lst")), + int.validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(10.0)))).optional[ValidationChecks]("int", _.int).addHints(smithy.api.HttpQuery.unsafeApply("int")), ){ ValidationChecks.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala b/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala index 4015fa5f1..3a4354cbe 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala @@ -33,7 +33,7 @@ object WeatherGen extends Service.Mixin[WeatherGen, WeatherOperation] { val hints: Hints = Hints( smithy.api.Documentation("Provides weather forecasts."), - smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString("nextToken")), outputToken = Some(smithy.api.NonEmptyString("nextToken")), items = None, pageSize = Some(smithy.api.NonEmptyString("pageSize"))), + smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString.unsafeApply("nextToken")), outputToken = Some(smithy.api.NonEmptyString.unsafeApply("nextToken")), items = None, pageSize = Some(smithy.api.NonEmptyString.unsafeApply("pageSize"))), ).lazily def apply[F[_]](implicit F: Impl[F]): F.type = F @@ -188,7 +188,7 @@ object WeatherOperation { val schema: OperationSchema[ListCitiesInput, Nothing, ListCitiesOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "ListCities")) .withInput(ListCitiesInput.schema) .withOutput(ListCitiesOutput.schema) - .withHints(smithy.api.Paginated(inputToken = None, outputToken = None, items = Some(smithy.api.NonEmptyString("items")), pageSize = None), smithy.api.Readonly()) + .withHints(smithy.api.Paginated(inputToken = None, outputToken = None, items = Some(smithy.api.NonEmptyString.unsafeApply("items")), pageSize = None), smithy.api.Readonly()) def wrap(input: ListCitiesInput): ListCities = ListCities(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala b/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala index eb4652255..8184f45e5 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala @@ -91,7 +91,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[SetInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Set")) .withInput(SetInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/set/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/set/"), code = 204)) def wrap(input: SetInput): Set = Set(input) } final case class List(input: ListInput) extends ReservedNameServiceOperation[ListInput, Nothing, Unit, Nothing, Nothing] { @@ -103,7 +103,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[ListInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "List")) .withInput(ListInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/list/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/list/"), code = 204)) def wrap(input: ListInput): List = List(input) } final case class Map(input: MapInput) extends ReservedNameServiceOperation[MapInput, Nothing, Unit, Nothing, Nothing] { @@ -115,7 +115,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[MapInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Map")) .withInput(MapInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/map/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/map/"), code = 204)) def wrap(input: MapInput): Map = Map(input) } final case class Option(input: OptionInput) extends ReservedNameServiceOperation[OptionInput, Nothing, Unit, Nothing, Nothing] { @@ -127,7 +127,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[OptionInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Option")) .withInput(OptionInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/option/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/option/"), code = 204)) def wrap(input: OptionInput): Option = Option(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala b/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala index a678023e3..c8d51acbb 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala @@ -93,7 +93,7 @@ object HelloWorldAuthServiceOperation { .withInput(unit) .withError(SayWorldError.errorSchema) .withOutput(World.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/hello"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/hello"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): SayWorld = SayWorld() } sealed trait SayWorldError extends scala.Product with scala.Serializable { self => @@ -159,7 +159,7 @@ object HelloWorldAuthServiceOperation { .withInput(unit) .withError(HealthCheckError.errorSchema) .withOutput(HealthCheckOutput.schema) - .withHints(smithy.api.Auth(Set()), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/health"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Auth(Set()), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/health"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): HealthCheck = HealthCheck() } sealed trait HealthCheckError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala index 32fdf7701..149a8ffba 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala @@ -26,7 +26,7 @@ object HelloWorldServiceGen extends Service.Mixin[HelloWorldServiceGen, HelloWor val hints: Hints = Hints( alloy.SimpleRestJson(), - smithy.api.Cors(origin = smithy.api.NonEmptyString("http://mysite.com"), maxAge = 600, additionalAllowedHeaders = Some(List(smithy.api.NonEmptyString("Authorization"))), additionalExposedHeaders = Some(List(smithy.api.NonEmptyString("X-Smithy4s")))), + smithy.api.Cors(origin = smithy.api.NonEmptyString.unsafeApply("http://mysite.com"), maxAge = 600, additionalAllowedHeaders = Some(List(smithy.api.NonEmptyString.unsafeApply("Authorization"))), additionalExposedHeaders = Some(List(smithy.api.NonEmptyString.unsafeApply("X-Smithy4s")))), ).lazily def apply[F[_]](implicit F: Impl[F]): F.type = F @@ -81,7 +81,7 @@ object HelloWorldServiceOperation { val schema: OperationSchema[Unit, Nothing, World, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.guides.hello", "SayWorld")) .withInput(unit) .withOutput(World.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/hello"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/hello"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): SayWorld = SayWorld() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala index 1ef9acb62..85d1fd76c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala @@ -85,7 +85,7 @@ object HelloWorldServiceOperation { .withInput(Person.schema) .withError(HelloError.errorSchema) .withOutput(Greeting.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/{name}"), code = 200), smithy.api.Tags(List("testOperationTag"))) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/{name}"), code = 200), smithy.api.Tags(List("testOperationTag"))) def wrap(input: Person): Hello = Hello(input) } sealed trait HelloError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala b/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala index 9f3790325..04d806779 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala @@ -16,7 +16,7 @@ object Person extends ShapeTag.Companion[Person] { implicit val schema: Schema[Person] = struct( string.required[Person]("name", _.name).addHints(smithy.api.HttpLabel()), - string.optional[Person]("town", _.town).addHints(smithy.api.HttpQuery("town")), + string.optional[Person]("town", _.town).addHints(smithy.api.HttpQuery.unsafeApply("town")), ){ Person.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala b/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala index 3c6d39aa5..eaf48b081 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala @@ -88,7 +88,7 @@ object ImportServiceOperation { .withInput(unit) .withError(ImportOperationError.errorSchema) .withOutput(OpOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test"), code = 200)) def wrap(input: Unit): ImportOperation = ImportOperation() } sealed trait ImportOperationError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/package.scala b/modules/bootstrapped/src/generated/smithy4s/example/package.scala index 900b37050..21864caef 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/package.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/package.scala @@ -41,6 +41,7 @@ package object example { type Weather[F[_]] = smithy4s.kinds.FunctorAlgebra[WeatherGen, F] val Weather = WeatherGen + type UnwrappedValidatedString = smithy4s.example.UnwrappedValidatedString.Type type PublishersList = smithy4s.example.PublishersList.Type type TestString = smithy4s.example.TestString.Type type ArbitraryData = smithy4s.example.ArbitraryData.Type @@ -60,6 +61,7 @@ package object example { type MapWithMemberHints = smithy4s.example.MapWithMemberHints.Type type TestIdRefList = smithy4s.example.TestIdRefList.Type type CitySummaries = smithy4s.example.CitySummaries.Type + type ValidatedString = smithy4s.example.ValidatedString.Type /** This is a simple example of a "quoted string" */ type AString = smithy4s.example.AString.Type type NonEmptyMapNumbers = smithy4s.example.NonEmptyMapNumbers.Type diff --git a/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala b/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala index 5f67990e6..f84525a75 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala @@ -79,7 +79,7 @@ object ReservedNameOverrideServiceOperation { val schema: OperationSchema[SetOpInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.reservedNameOverride", "SetOp")) .withInput(SetOpInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/set/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/set/"), code = 204)) def wrap(input: SetOpInput): SetOp = SetOp(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala index 518557b0a..f51ba2c10 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala @@ -93,7 +93,7 @@ object HelloServiceOperation { .withInput(SayHelloInput.schema) .withError(SayHelloError.errorSchema) .withOutput(SayHelloOutput.schema) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/", host = None, resolvedHost = None, authScheme = None, queryParams = Some(List("Hi=Hello%20there")), forbidQueryParams = None, requireQueryParams = None, headers = Some(Map("X-Greeting" -> "Hi")), forbidHeaders = None, requireHeaders = None, body = Some("{\"name\":\"Teddy\"}"), bodyMediaType = Some("application/json"), params = Some(smithy4s.Document.obj("greeting" -> smithy4s.Document.fromString("Hi"), "name" -> smithy4s.Document.fromString("Teddy"), "query" -> smithy4s.Document.fromString("Hello there"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/"), code = 200), smithy.test.HttpResponseTests(List(smithy.test.HttpResponseTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), code = 200, authScheme = None, headers = Some(Map("X-H1" -> "V1")), forbidHeaders = None, requireHeaders = None, body = Some("{\"result\":\"Hello!\"}"), bodyMediaType = None, params = Some(smithy4s.Document.obj("payload" -> smithy4s.Document.obj("result" -> smithy4s.Document.fromString("Hello!")), "header1" -> smithy4s.Document.fromString("V1"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None)))) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/", host = None, resolvedHost = None, authScheme = None, queryParams = Some(List("Hi=Hello%20there")), forbidQueryParams = None, requireQueryParams = None, headers = Some(Map("X-Greeting" -> "Hi")), forbidHeaders = None, requireHeaders = None, body = Some("{\"name\":\"Teddy\"}"), bodyMediaType = Some("application/json"), params = Some(smithy4s.Document.obj("greeting" -> smithy4s.Document.fromString("Hi"), "name" -> smithy4s.Document.fromString("Teddy"), "query" -> smithy4s.Document.fromString("Hello there"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/"), code = 200), smithy.test.HttpResponseTests(List(smithy.test.HttpResponseTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), code = 200, authScheme = None, headers = Some(Map("X-H1" -> "V1")), forbidHeaders = None, requireHeaders = None, body = Some("{\"result\":\"Hello!\"}"), bodyMediaType = None, params = Some(smithy4s.Document.obj("payload" -> smithy4s.Document.obj("result" -> smithy4s.Document.fromString("Hello!")), "header1" -> smithy4s.Document.fromString("V1"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None)))) def wrap(input: SayHelloInput): SayHello = SayHello(input) } sealed trait SayHelloError extends scala.Product with scala.Serializable { self => @@ -172,7 +172,7 @@ object HelloServiceOperation { val schema: OperationSchema[Unit, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "Listen")) .withInput(unit) .withOutput(unit) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "listen", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/listen", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = None, vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/listen"), code = 200), smithy.api.Readonly()) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "listen", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/listen", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = None, vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/listen"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): Listen = Listen() } final case class TestPath(input: TestPathInput) extends HelloServiceOperation[TestPathInput, Nothing, Unit, Nothing, Nothing] { @@ -184,7 +184,7 @@ object HelloServiceOperation { val schema: OperationSchema[TestPathInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "TestPath")) .withInput(TestPathInput.schema) .withOutput(unit) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "TestPath", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/test-path/sameValue", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("path" -> smithy4s.Document.fromString("sameValue"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test-path/{path}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "TestPath", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/test-path/sameValue", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("path" -> smithy4s.Document.fromString("sameValue"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test-path/{path}"), code = 200), smithy.api.Readonly()) def wrap(input: TestPathInput): TestPath = TestPath(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala index e1135ef1c..485170eb0 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala @@ -78,7 +78,7 @@ object HelloWorldServiceOperation { val schema: OperationSchema[HelloInput, Nothing, HelloOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "Hello")) .withInput(HelloInput.schema) .withOutput(HelloOutput.schema) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "helloSuccess", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/World", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None), smithy.test.HttpRequestTestCase(id = "helloFails", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/fail", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/{name}"), code = 200)) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "helloSuccess", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/World", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None), smithy.test.HttpRequestTestCase(id = "helloFails", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/fail", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/{name}"), code = 200)) def wrap(input: HelloInput): Hello = Hello(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala index df5ff96c1..6189355d4 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala @@ -17,8 +17,8 @@ object SayHelloInput extends ShapeTag.Companion[SayHelloInput] { ).lazily implicit val schema: Schema[SayHelloInput] = struct( - string.optional[SayHelloInput]("greeting", _.greeting).addHints(smithy.api.HttpHeader("X-Greeting")), - string.optional[SayHelloInput]("query", _.query).addHints(smithy.api.HttpQuery("Hi")), + string.optional[SayHelloInput]("greeting", _.greeting).addHints(smithy.api.HttpHeader.unsafeApply("X-Greeting")), + string.optional[SayHelloInput]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("Hi")), string.optional[SayHelloInput]("name", _.name), ){ SayHelloInput.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala index ff1f22f0c..aead36af7 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala @@ -16,7 +16,7 @@ object SayHelloOutput extends ShapeTag.Companion[SayHelloOutput] { implicit val schema: Schema[SayHelloOutput] = struct( SayHelloPayload.schema.required[SayHelloOutput]("payload", _.payload).addHints(smithy.api.HttpPayload()), - string.required[SayHelloOutput]("header1", _.header1).addHints(smithy.api.HttpHeader("X-H1")), + string.required[SayHelloOutput]("header1", _.header1).addHints(smithy.api.HttpHeader.unsafeApply("X-H1")), ){ SayHelloOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/weather/WeatherService.scala b/modules/bootstrapped/src/generated/weather/WeatherService.scala index 3258c14e9..8d35d4fcf 100644 --- a/modules/bootstrapped/src/generated/weather/WeatherService.scala +++ b/modules/bootstrapped/src/generated/weather/WeatherService.scala @@ -78,7 +78,7 @@ object WeatherServiceOperation { val schema: OperationSchema[GetWeatherInput, Nothing, GetWeatherOutput, Nothing, Nothing] = Schema.operation(ShapeId("weather", "GetWeather")) .withInput(GetWeatherInput.schema) .withOutput(GetWeatherOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/weather/{city}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/weather/{city}"), code = 200)) def wrap(input: GetWeatherInput): GetWeather = GetWeather(input) } } diff --git a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala index b370ffd86..7a670c4c5 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala @@ -24,6 +24,7 @@ import TypedNode._ import Type.ExternalType import LineSegment._ import smithy4s.codegen.internals.Type.Nullable +import smithy4s.codegen.internals.Type.ValidatedAlias private[internals] object CollisionAvoidance { def apply(compilationUnit: CompilationUnit): CompilationUnit = { @@ -86,6 +87,14 @@ private[internals] object CollisionAvoidance { rec, hints.map(modHint) ) + case ValidatedTypeAlias(shapeId, name, tpe, recursive, hints) => + ValidatedTypeAlias( + shapeId, + protectKeyword(name.capitalize), + modType(tpe), + recursive, + hints.map(modHint) + ) case Enumeration(shapeId, name, tag, values, hints) => val newValues = values.map { case EnumValue(value, intValue, name, realName, hints) => @@ -128,6 +137,8 @@ private[internals] object CollisionAvoidance { val protectedName = protectKeyword(name.capitalize) val unwrapped = isUnwrapped | (protectedName != name.capitalize) Alias(namespace, protectKeyword(name.capitalize), modType(tpe), unwrapped) + case ValidatedAlias(namespace, name, tpe) => + ValidatedAlias(namespace, protectKeyword(name.capitalize), modType(tpe)) case PrimitiveType(prim) => PrimitiveType(prim) case ExternalType(name, fqn, typeParams, pFqn, under, refinementHint) => ExternalType( @@ -215,6 +226,8 @@ private[internals] object CollisionAvoidance { ) case NewTypeTN(ref, target) => NewTypeTN(modRef(ref), target) + case ValidatedNewTypeTN(ref, target) => + ValidatedNewTypeTN(modRef(ref), target) case AltTN(ref, altName, alt) => AltTN(modRef(ref), altName, alt) case MapTN(values) => @@ -301,6 +314,7 @@ private[internals] object CollisionAvoidance { val EnumValue_ = NameRef("smithy4s.schema", "EnumValue") val EnumTag_ = NameRef("smithy4s.schema", "EnumTag") val Newtype_ = NameRef("smithy4s", "Newtype") + val NewtypeValidated_ = NameRef("smithy4s", "NewtypeValidated") val Hints_ = NameRef("smithy4s", "Hints") val ShapeTag_ = NameRef("smithy4s", "ShapeTag") val ErrorSchema_ = NameRef("smithy4s.schema", "ErrorSchema") diff --git a/modules/codegen/src/smithy4s/codegen/internals/IR.scala b/modules/codegen/src/smithy4s/codegen/internals/IR.scala index bbf465330..3f5575ebb 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/IR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/IR.scala @@ -102,6 +102,14 @@ private[internals] case class TypeAlias( hints: List[Hint] = Nil ) extends Decl +private[internals] case class ValidatedTypeAlias( + shapeId: ShapeId, + name: String, + tpe: Type, + recursive: Boolean = false, + hints: List[Hint] = Nil +) extends Decl + private[internals] case class Enumeration( shapeId: ShapeId, name: String, @@ -279,6 +287,7 @@ private[internals] object Type { tpe: Type, isUnwrapped: Boolean ) extends Type + case class ValidatedAlias(namespace: String, name: String, tpe: Type) extends Type case class PrimitiveType(prim: Primitive) extends Type case class ExternalType( name: String, @@ -422,6 +431,8 @@ private[internals] object TypedNode { fields.traverse(_.traverse(_.traverse(f))).map(StructureTN(ref, _)) case NewTypeTN(ref, target) => f(target).map(NewTypeTN(ref, _)) + case ValidatedNewTypeTN(ref, target) => + f(target).map(ValidatedNewTypeTN(ref, _)) case AltTN(ref, altName, alt) => alt.traverse(f).map(AltTN(ref, altName, _)) case MapTN(values) => @@ -452,6 +463,7 @@ private[internals] object TypedNode { fields: List[(String, FieldTN[A])] ) extends TypedNode[A] case class NewTypeTN[A](ref: Type.Ref, target: A) extends TypedNode[A] + case class ValidatedNewTypeTN[A](ref: Type.Ref, target: A) extends TypedNode[A] case class AltTN[A](ref: Type.Ref, altName: String, alt: AltValueTN[A]) extends TypedNode[A] case class MapTN[A](values: List[(A, A)]) extends TypedNode[A] diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 291f941ae..84c277bd1 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -185,6 +185,8 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => renderUnion(shapeId, union.nameRef, alts, mixins, recursive, hints) case ta @ TypeAlias(shapeId, _, tpe, _, recursive, hints) => renderNewtype(shapeId, ta.nameRef, tpe, recursive, hints) + case vta @ ValidatedTypeAlias(shapeId, _, tpe, recursive, hints) => + renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) case enumeration @ Enumeration(shapeId, _, tag, values, hints) => renderEnum(shapeId, enumeration.nameRef, tag, values, hints) } @@ -265,6 +267,12 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => deprecationAnnotation(hints), line"type $name = ${compilationUnit.namespace}.${name}.Type" ) + case ValidatedTypeAlias(_, name, _, _, hints) => + lines( + documentationAnnotation(hints), + deprecationAnnotation(hints), + line"type $name = ${compilationUnit.namespace}.${name}.Type" + ) } val blk = @@ -1323,6 +1331,51 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => ) ) } + + private def renderValidatedNewtype( + shapeId: ShapeId, + name: NameRef, + tpe: Type, + recursive: Boolean, + hints: List[Hint] + ): Lines = { + val validators = { + val tags = hints.collect { case t: Hint.Constraint => t } + tags.map{ tag => + line"a => validateInternal(${renderNativeHint(tag.native)})(a)" + }.intercalate(Line.comma) + } + + val definition = + if (recursive) line"$recursive_(" + else Line.empty + val trailingCalls = + line".withId(id).addHints(hints)${renderConstraintValidation(hints)}" + val closing = if (recursive) ")" else "" + lines( + documentationAnnotation(hints), + deprecationAnnotation(hints), + obj(name, line"$NewtypeValidated_[$tpe]")( + renderId(shapeId), + renderHintsVal(hints), + line"val underlyingSchema: $Schema_[$tpe] = ${tpe.schemaRef}$trailingCalls", + lines( + line"implicit val schema: $Schema_[$name] = $definition$bijection_(underlyingSchema, asBijectionUnsafe)$closing" + ), + lines( + line"val validators: List[$tpe => Either[String, $tpe]] = List(", + indent(validators), + line")" + ), + lines( + line"@inline def apply(a: $tpe): Either[String, $name] = validators", + indent(line".foldLeft(Right(a): Either[String, $tpe])((acc, v) => acc.flatMap(v))"), + indent(line".map(unsafeApply)") + ), + renderTypeclasses(hints, name) + ) + ) + } private implicit class OperationExt(op: Operation) { def renderArgs = @@ -1387,6 +1440,8 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => false ) => NameRef(ns, s"$name.schema").toLine + case Type.ValidatedAlias(ns, name, _) => + NameRef(ns, s"$name.schema").toLine case Type.Alias(ns, name, _, _) => NameRef(ns, s"$name.underlyingSchema").toLine case Type.Ref(ns, name) => NameRef(ns, s"$name.schema").toLine @@ -1518,6 +1573,14 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => else false -> line"${ref.show}($text)" }) + case ValidatedNewTypeTN(ref, target) => + Reader(topLevel => { + val (wroteCollection, text) = target.run(topLevel) + if (wroteCollection && !topLevel) + false -> text + else + false -> line"${ref.show}.unsafeApply($text)" + }) case AltTN(ref, altName, AltValueTN.TypeAltTN(alt)) => line"${ref.show}.${altName.capitalize}Case(${alt.runDefault}).widen".write diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index da87da417..e67796546 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -158,6 +158,14 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { recursive, hints ).some + case Type.ValidatedAlias(_, name, tpe) => + ValidatedTypeAlias( + shape.getId(), + name, + tpe, + recursive, + hints + ).some case Type.PrimitiveType(_) => None case other => TypeAlias( @@ -592,6 +600,13 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { shape.hasTrait(classOf[smithy4s.meta.UnwrapTrait]) } + private def hasConstraints(shape: Shape): Boolean = { + shape.getAllTraits().asScala.values.exists { + case ConstraintTrait(_) => true + case _ => false + } + } + def primitive( shape: Shape, primitiveId: String, @@ -603,7 +618,9 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { shape.getId() != ShapeId.from(primitiveId) && !isUnboxedPrimitive(shape.getId()) ) { - Type + val isUnwrapped = isUnwrappedShape(shape) + if(isUnwrapped || !hasConstraints(shape)) { + Type .Alias( shape.getId().getNamespace(), shape.getId().getName(), @@ -611,6 +628,14 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { isUnwrappedShape(shape) ) .some + } else { + Type.ValidatedAlias( + shape.getId().getNamespace(), + shape.getId().getName(), + externalOrBase + ).some + } + } else externalOrBase.some } @@ -1269,6 +1294,8 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { // Alias case (node, Type.Alias(ns, name, tpe, _)) => TypedNode.NewTypeTN(Type.Ref(ns, name), NodeAndType(node, tpe)) + case (node, Type.ValidatedAlias(ns, name, tpe)) => + TypedNode.ValidatedNewTypeTN(Type.Ref(ns, name), NodeAndType(node, tpe)) // Enumeration (Enum Trait) case (N.StringNode(str), UnRef(shape @ T.enumeration(e))) => val (enumDef, index) = diff --git a/modules/codegen/src/smithy4s/codegen/internals/ToLine.scala b/modules/codegen/src/smithy4s/codegen/internals/ToLine.scala index 0398c78d4..431227dc8 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/ToLine.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/ToLine.scala @@ -66,6 +66,8 @@ private[internals] object ToLine { NameRef(ns, name) case Type.Alias(_, _, aliased, _) => typeToNameRef(aliased) + case Type.ValidatedAlias(ns, name, _) => + NameRef(ns, name) case Type.Ref(namespace, name) => NameRef(namespace, name) case Type.PrimitiveType(prim) => primitiveLine(prim) case e: Type.ExternalType => diff --git a/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala b/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala index 47482ae5e..adb020cf9 100644 --- a/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala +++ b/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala @@ -245,8 +245,8 @@ private[compliancetests] class ServerHttpComplianceTestCase[ ): (Service.Reflective[NoInputOp], Request[F]) = { val newHints = { val newHttp = smithy.api.Http( - method = smithy.api.NonEmptyString("GET"), - uri = smithy.api.NonEmptyString("/") + method = smithy.api.NonEmptyString.unsafeApply("GET"), + uri = smithy.api.NonEmptyString.unsafeApply("/") ) val code = endpoint.hints.get[smithy.api.Http].map(_.code).getOrElse(newHttp.code) diff --git a/modules/core/src-2/NewtypeValidated.scala b/modules/core/src-2/NewtypeValidated.scala new file mode 100644 index 000000000..358d88576 --- /dev/null +++ b/modules/core/src-2/NewtypeValidated.scala @@ -0,0 +1,70 @@ +/* + * Copyright 2021-2024 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 + +abstract class NewtypeValidated[A] extends HasId { self => + // This encoding originally comes from this library: + // https://github.com/alexknvl/newtypes#what-does-it-do + type Base + trait _Tag extends Any + type Type <: Base with _Tag + + @inline def apply(a: A): Either[String, Type] + + @inline final def unsafeApply(a: A): Type = a.asInstanceOf[Type] + + @inline final def value(x: Type): A = + x.asInstanceOf[A] + + implicit final class Ops(val self: Type) { + @inline final def value: A = NewtypeValidated.this.value(self) + } + + def schema: Schema[Type] + + implicit val tag: ShapeTag[Type] = new ShapeTag[Type] { + def id: ShapeId = self.id + def schema: Schema[Type] = self.schema + } + + def unapply(t: Type): Some[A] = Some(t.value) + + implicit def refinementProvider[C](implicit ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] = + new RefinementProvider.Simple[C, Type] { + val tag: ShapeTag[C] = ev.tag + override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) + } + + protected val validators: List[A => Either[String, A]] + + protected def validateInternal[C](c: C)(value: A)(implicit ev: RefinementProvider.Simple[C, A]): Either[String, A] = + ev.make(c).apply(value) + + protected val asBijectionUnsafe: Bijection[A, Type] = + new NewtypeValidated.Make[A, Type] { + def to(a: A): Type = self.unsafeApply(a) + def from(t: Type): A = value(t) + } + + object hint { + def unapply(h: Hints): Option[Type] = h.get(tag) + } +} + +object NewtypeValidated { + private[smithy4s] trait Make[A, B] extends Bijection[A, B] +} \ No newline at end of file diff --git a/modules/core/src-3/NewtypeValidated.scala b/modules/core/src-3/NewtypeValidated.scala new file mode 100644 index 000000000..49878bb30 --- /dev/null +++ b/modules/core/src-3/NewtypeValidated.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2021-2024 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 + +abstract class NewtypeValidated[A] extends HasId { self => + opaque type Type = A + + def apply(a: A): Either[String, Type] + + def unsafeApply(a: A): Type = a + + extension (orig: Type) def value: A = orig + + def unapply(orig: Type): Some[A] = Some(orig.value) + + def schema: Schema[Type] + + implicit val tag: ShapeTag[Type] = new ShapeTag[Type] { + def id: ShapeId = self.id + def schema: Schema[Type] = self.schema + } + + given refinementProvider[C](using ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] with { + // new RefinementProvider.Simple[C, Type] { + val tag: ShapeTag[C] = ev.tag + override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) + } + +// implicit def refinementProvider[C](given ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] = +// new RefinementProvider.Simple[C, Type] { +// val tag: ShapeTag[C] = ev.tag +// override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) +// } + + protected val validators: List[A => Either[String, A]] + + protected def validateInternal[C](c: C)(value: A)(using ev: RefinementProvider.Simple[C, A]): Either[String, A] = + ev.make(c).apply(value) + + protected val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { + def to(a: A): Type = self.unsafeApply(a) + def from(t: Type): A = value(t) + } + + object hint { + def unapply(h: Hints): Option[Type] = h.get(tag) + } +} + +object NewtypeValidated { + private[smithy4s] trait Make[A, B] extends Bijection[A, B] +} diff --git a/modules/core/src/smithy4s/Hints.scala b/modules/core/src/smithy4s/Hints.scala index c4d35ba38..f24e0ddc8 100644 --- a/modules/core/src/smithy4s/Hints.scala +++ b/modules/core/src/smithy4s/Hints.scala @@ -47,6 +47,7 @@ trait Hints { final def has[A](implicit key: ShapeTag[A]): Boolean = this.get[A].isDefined final def get[A](key: ShapeTag.Has[A]): Option[A] = get(key.getTag) final def get[T](nt: Newtype[T]): Option[nt.Type] = get(nt.tag) + final def get[T](vnt: NewtypeValidated[T]): Option[vnt.Type] = get(vnt.tag) final def filter(predicate: Hint => Boolean): Hints = Hints.fromSeq(all.filter(predicate).toSeq) final def filterNot(predicate: Hint => Boolean): Hints = diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala index 56fd88820..aaeda0c4e 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala @@ -110,7 +110,7 @@ private[http] class UrlFormDataDecoderSchemaVisitor( val kvSchema: Schema[(K, V)] = { val kField = key.required[KV]("key", _._1) val vField = value.required[KV]("value", _._2) - Schema.struct(kField, vField)((_, _)).addHints(UrlFormName("entry")) + Schema.struct(kField, vField)((_, _)).addHints(UrlFormName.unsafeApply("entry")) } compile(Schema.vector(kvSchema).addHints(hints)) .map(_.toMap) diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala index a68e964f0..0f1d27ff5 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala @@ -94,7 +94,7 @@ private[http] class UrlFormDataEncoderSchemaVisitor( val kvSchema: Schema[(K, V)] = { val kField = key.required[KV]("key", _._1) val vField = value.required[KV]("value", _._2) - Schema.struct(kField, vField)((_, _)).addHints(UrlFormName("entry")) + Schema.struct(kField, vField)((_, _)).addHints(UrlFormName.unsafeApply("entry")) } // Avoid serialising empty maps, see comment in collection case and // https://github.com/smithy-lang/smithy/issues/1868. diff --git a/sampleSpecs/validated-newtype.smithy b/sampleSpecs/validated-newtype.smithy new file mode 100644 index 000000000..b6fc3a239 --- /dev/null +++ b/sampleSpecs/validated-newtype.smithy @@ -0,0 +1,22 @@ +$version: "2" + +namespace smithy4s.example + +use smithy4s.meta#unwrap + +@length(min: 1) +@pattern("[a-zA-Z0-9]+") +string ValidatedString + +structure ValidatedFoo { + name: ValidatedString +} + +@length(min: 1) +@pattern("[a-zA-Z0-9]+") +@unwrap +string UnwrappedValidatedString + +structure UnwrappedValidatedFoo { + name: UnwrappedValidatedString +} \ No newline at end of file From c4775415077fe47bfca8d248bdec4702488c9e71 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 19 Mar 2024 13:54:17 +0200 Subject: [PATCH 02/33] Fix formatting --- .../internals/CollisionAvoidance.scala | 6 ++-- .../src/smithy4s/codegen/internals/IR.scala | 8 +++-- .../smithy4s/codegen/internals/Renderer.scala | 14 ++++---- .../codegen/internals/SmithyToIR.scala | 32 +++++++++-------- modules/core/src-2/NewtypeValidated.scala | 19 ++++++---- modules/core/src-3/NewtypeValidated.scala | 35 ++++++++++--------- .../UrlFormDataDecoderSchemaVisitor.scala | 4 ++- .../UrlFormDataEncoderSchemaVisitor.scala | 4 ++- 8 files changed, 69 insertions(+), 53 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala index 7a670c4c5..f64cdc5f0 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala @@ -87,7 +87,7 @@ private[internals] object CollisionAvoidance { rec, hints.map(modHint) ) - case ValidatedTypeAlias(shapeId, name, tpe, recursive, hints) => + case ValidatedTypeAlias(shapeId, name, tpe, recursive, hints) => ValidatedTypeAlias( shapeId, protectKeyword(name.capitalize), @@ -137,7 +137,7 @@ private[internals] object CollisionAvoidance { val protectedName = protectKeyword(name.capitalize) val unwrapped = isUnwrapped | (protectedName != name.capitalize) Alias(namespace, protectKeyword(name.capitalize), modType(tpe), unwrapped) - case ValidatedAlias(namespace, name, tpe) => + case ValidatedAlias(namespace, name, tpe) => ValidatedAlias(namespace, protectKeyword(name.capitalize), modType(tpe)) case PrimitiveType(prim) => PrimitiveType(prim) case ExternalType(name, fqn, typeParams, pFqn, under, refinementHint) => @@ -226,7 +226,7 @@ private[internals] object CollisionAvoidance { ) case NewTypeTN(ref, target) => NewTypeTN(modRef(ref), target) - case ValidatedNewTypeTN(ref, target) => + case ValidatedNewTypeTN(ref, target) => ValidatedNewTypeTN(modRef(ref), target) case AltTN(ref, altName, alt) => AltTN(modRef(ref), altName, alt) diff --git a/modules/codegen/src/smithy4s/codegen/internals/IR.scala b/modules/codegen/src/smithy4s/codegen/internals/IR.scala index 3f5575ebb..bdb7a4f38 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/IR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/IR.scala @@ -287,7 +287,8 @@ private[internals] object Type { tpe: Type, isUnwrapped: Boolean ) extends Type - case class ValidatedAlias(namespace: String, name: String, tpe: Type) extends Type + case class ValidatedAlias(namespace: String, name: String, tpe: Type) + extends Type case class PrimitiveType(prim: Primitive) extends Type case class ExternalType( name: String, @@ -431,7 +432,7 @@ private[internals] object TypedNode { fields.traverse(_.traverse(_.traverse(f))).map(StructureTN(ref, _)) case NewTypeTN(ref, target) => f(target).map(NewTypeTN(ref, _)) - case ValidatedNewTypeTN(ref, target) => + case ValidatedNewTypeTN(ref, target) => f(target).map(ValidatedNewTypeTN(ref, _)) case AltTN(ref, altName, alt) => alt.traverse(f).map(AltTN(ref, altName, _)) @@ -463,7 +464,8 @@ private[internals] object TypedNode { fields: List[(String, FieldTN[A])] ) extends TypedNode[A] case class NewTypeTN[A](ref: Type.Ref, target: A) extends TypedNode[A] - case class ValidatedNewTypeTN[A](ref: Type.Ref, target: A) extends TypedNode[A] + case class ValidatedNewTypeTN[A](ref: Type.Ref, target: A) + extends TypedNode[A] case class AltTN[A](ref: Type.Ref, altName: String, alt: AltValueTN[A]) extends TypedNode[A] case class MapTN[A](values: List[(A, A)]) extends TypedNode[A] diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 84c277bd1..bbac2f041 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -1331,8 +1331,8 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => ) ) } - - private def renderValidatedNewtype( + + private def renderValidatedNewtype( shapeId: ShapeId, name: NameRef, tpe: Type, @@ -1341,9 +1341,11 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => ): Lines = { val validators = { val tags = hints.collect { case t: Hint.Constraint => t } - tags.map{ tag => - line"a => validateInternal(${renderNativeHint(tag.native)})(a)" - }.intercalate(Line.comma) + tags + .map { tag => + line"a => validateInternal(${renderNativeHint(tag.native)})(a)" + } + .intercalate(Line.comma) } val definition = @@ -1573,7 +1575,7 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => else false -> line"${ref.show}($text)" }) - case ValidatedNewTypeTN(ref, target) => + case ValidatedNewTypeTN(ref, target) => Reader(topLevel => { val (wroteCollection, text) = target.run(topLevel) if (wroteCollection && !topLevel) diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index e67796546..53874cf43 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -603,7 +603,7 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { private def hasConstraints(shape: Shape): Boolean = { shape.getAllTraits().asScala.values.exists { case ConstraintTrait(_) => true - case _ => false + case _ => false } } @@ -619,23 +619,25 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { !isUnboxedPrimitive(shape.getId()) ) { val isUnwrapped = isUnwrappedShape(shape) - if(isUnwrapped || !hasConstraints(shape)) { + if (isUnwrapped || !hasConstraints(shape)) { Type - .Alias( - shape.getId().getNamespace(), - shape.getId().getName(), - externalOrBase, - isUnwrappedShape(shape) - ) - .some + .Alias( + shape.getId().getNamespace(), + shape.getId().getName(), + externalOrBase, + isUnwrappedShape(shape) + ) + .some } else { - Type.ValidatedAlias( - shape.getId().getNamespace(), - shape.getId().getName(), - externalOrBase - ).some + Type + .ValidatedAlias( + shape.getId().getNamespace(), + shape.getId().getName(), + externalOrBase + ) + .some } - + } else externalOrBase.some } diff --git a/modules/core/src-2/NewtypeValidated.scala b/modules/core/src-2/NewtypeValidated.scala index 358d88576..a246f1bb1 100644 --- a/modules/core/src-2/NewtypeValidated.scala +++ b/modules/core/src-2/NewtypeValidated.scala @@ -17,7 +17,7 @@ package smithy4s abstract class NewtypeValidated[A] extends HasId { self => - // This encoding originally comes from this library: + // This encoding originally comes from this library: // https://github.com/alexknvl/newtypes#what-does-it-do type Base trait _Tag extends Any @@ -43,15 +43,20 @@ abstract class NewtypeValidated[A] extends HasId { self => def unapply(t: Type): Some[A] = Some(t.value) - implicit def refinementProvider[C](implicit ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] = + implicit def refinementProvider[C](implicit + ev: RefinementProvider.Simple[C, A] + ): RefinementProvider.Simple[C, Type] = new RefinementProvider.Simple[C, Type] { val tag: ShapeTag[C] = ev.tag - override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) + override def make(c: C): Refinement.Aux[C, Type, Type] = + ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) } - + protected val validators: List[A => Either[String, A]] - - protected def validateInternal[C](c: C)(value: A)(implicit ev: RefinementProvider.Simple[C, A]): Either[String, A] = + + protected def validateInternal[C](c: C)(value: A)(implicit + ev: RefinementProvider.Simple[C, A] + ): Either[String, A] = ev.make(c).apply(value) protected val asBijectionUnsafe: Bijection[A, Type] = @@ -67,4 +72,4 @@ abstract class NewtypeValidated[A] extends HasId { self => object NewtypeValidated { private[smithy4s] trait Make[A, B] extends Bijection[A, B] -} \ No newline at end of file +} diff --git a/modules/core/src-3/NewtypeValidated.scala b/modules/core/src-3/NewtypeValidated.scala index 49878bb30..0c34b3c64 100644 --- a/modules/core/src-3/NewtypeValidated.scala +++ b/modules/core/src-3/NewtypeValidated.scala @@ -34,27 +34,28 @@ abstract class NewtypeValidated[A] extends HasId { self => def schema: Schema[Type] = self.schema } - given refinementProvider[C](using ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] with { - // new RefinementProvider.Simple[C, Type] { - val tag: ShapeTag[C] = ev.tag - override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) + given refinementProvider[C](using + ev: RefinementProvider.Simple[C, A] + ): RefinementProvider.Simple[C, Type] with { + + val tag: ShapeTag[C] = ev.tag + + override def make(c: C): Refinement.Aux[C, Type, Type] = + ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) } - -// implicit def refinementProvider[C](given ev: RefinementProvider.Simple[C, A]): RefinementProvider.Simple[C, Type] = -// new RefinementProvider.Simple[C, Type] { -// val tag: ShapeTag[C] = ev.tag -// override def make(c: C): Refinement.Aux[C, Type, Type] = ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) -// } - + protected val validators: List[A => Either[String, A]] - - protected def validateInternal[C](c: C)(value: A)(using ev: RefinementProvider.Simple[C, A]): Either[String, A] = + + protected def validateInternal[C](c: C)(value: A)(using + ev: RefinementProvider.Simple[C, A] + ): Either[String, A] = ev.make(c).apply(value) - protected val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { - def to(a: A): Type = self.unsafeApply(a) - def from(t: Type): A = value(t) - } + protected val asBijectionUnsafe: Bijection[A, Type] = + new NewtypeValidated.Make[A, Type] { + def to(a: A): Type = self.unsafeApply(a) + def from(t: Type): A = value(t) + } object hint { def unapply(h: Hints): Option[Type] = h.get(tag) diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala index aaeda0c4e..0efb6066f 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala @@ -110,7 +110,9 @@ private[http] class UrlFormDataDecoderSchemaVisitor( val kvSchema: Schema[(K, V)] = { val kField = key.required[KV]("key", _._1) val vField = value.required[KV]("value", _._2) - Schema.struct(kField, vField)((_, _)).addHints(UrlFormName.unsafeApply("entry")) + Schema + .struct(kField, vField)((_, _)) + .addHints(UrlFormName.unsafeApply("entry")) } compile(Schema.vector(kvSchema).addHints(hints)) .map(_.toMap) diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala index 0f1d27ff5..1332828b4 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala @@ -94,7 +94,9 @@ private[http] class UrlFormDataEncoderSchemaVisitor( val kvSchema: Schema[(K, V)] = { val kField = key.required[KV]("key", _._1) val vField = value.required[KV]("value", _._2) - Schema.struct(kField, vField)((_, _)).addHints(UrlFormName.unsafeApply("entry")) + Schema + .struct(kField, vField)((_, _)) + .addHints(UrlFormName.unsafeApply("entry")) } // Avoid serialising empty maps, see comment in collection case and // https://github.com/smithy-lang/smithy/issues/1868. From 468d54c2c6aa9256de8589fcb89bc273bb30bb7d Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 19 Mar 2024 14:09:50 +0200 Subject: [PATCH 03/33] Add simple renderer tests --- .../codegen/internals/RendererSpec.scala | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index d2462b550..64243fd51 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -484,4 +484,65 @@ final class RendererSpec extends munit.FunSuite { ) ) } + + test("newtype with constraint") { + val smithy = """ + |$version: "2" + | + |namespace smithy4s + | + |@length(min: 1, max: 10) + |string MyValidatedString + | + |structure ValidatedFoo { + | mvs: MyValidatedString + |} + |""".stripMargin + + val contents = generateScalaCode(smithy).values + + assert( + contents.exists( + _.contains("object MyValidatedString extends NewtypeValidated[String]") + ) + ) + assert( + contents.exists( + _.contains( + "final case class ValidatedFoo(mvs: Option[MyValidatedString] = None)" + ) + ) + ) + } + + test("unwrapped newtype with constraint") { + val smithy = """ + |$version: "2" + | + |namespace smithy4s + | + |use smithy4s.meta#unwrap + | + |@unwrap + |@length(min: 1, max: 10) + |string MyValidatedString + | + |structure ValidatedFoo { + | mvs: MyValidatedString + |} + |""".stripMargin + + val contents = generateScalaCode(smithy).values + + assert( + contents.exists( + _.contains("object MyValidatedString extends Newtype[String]") + ) + ) + assert( + contents.exists( + _.contains("final case class ValidatedFoo(mvs: Option[String] = None)") + ) + ) + } } From c600b1fcaea0b3607cce1394697ed165e6494d31 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 19 Mar 2024 15:37:16 +0200 Subject: [PATCH 04/33] Fix validated newtype usage --- .../test/src/smithy4s/HintsSpec.scala | 36 +++++++++---------- .../test/src/smithy4s/PatternSpec.scala | 2 +- .../src/smithy4s/http/HttpRequestSpec.scala | 2 +- .../smithy4s/http/MetadataDecoderSpec.scala | 4 +-- .../smithy4s/http/internals/PathSpec.scala | 12 +++---- ...mDataEncoderDecoderSchemaVisitorSpec.scala | 16 ++++----- .../src/smithy4s/optics/CompositionSpec.scala | 2 +- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala b/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala index f8d436284..a58afbed0 100644 --- a/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala @@ -24,27 +24,27 @@ import smithy.api.Required class HintsSpec() extends FunSuite { test("hints work as expected with newtypes") { - val hints = Hints(HttpHeader("X-Foobar")) - expect(hints.get(HttpHeader) == Some(HttpHeader("X-Foobar"))) + val hints = Hints(HttpHeader.unsafeApply("X-Foobar")) + expect(hints.get(HttpHeader) == Some(HttpHeader.unsafeApply("X-Foobar"))) } test("hints work as expected with newtypes (using implicits)") { - val hints = Hints(HttpHeader("X-Foobar")) - expect.same(hints.get[HttpHeader], Some(HttpHeader("X-Foobar"))) + val hints = Hints(HttpHeader.unsafeApply("X-Foobar")) + expect.same(hints.get[HttpHeader], Some(HttpHeader.unsafeApply("X-Foobar"))) } test("hints can be stored as member hints") { - val hints = Hints(HttpLabel()).addMemberHints(HttpHeader("X-Foobar")) + val hints = Hints(HttpLabel()).addMemberHints(HttpHeader.unsafeApply("X-Foobar")) // Member and target hints are both looked at when searching for a hint. - expect.same(hints.get(HttpHeader), Some(HttpHeader("X-Foobar"))) + expect.same(hints.get(HttpHeader), Some(HttpHeader.unsafeApply("X-Foobar"))) expect.same(hints.get(HttpLabel), Some(HttpLabel())) } test("Member hints are stored separately from target hints") { val hints = - Hints(HttpHeader("X-Target")).addMemberHints(HttpHeader("X-Member")) - expect.same(hints.memberHints, Hints.member(HttpHeader("X-Member"))) - expect.same(hints.targetHints, Hints(HttpHeader("X-Target"))) + Hints(HttpHeader.unsafeApply("X-Target")).addMemberHints(HttpHeader.unsafeApply("X-Member")) + expect.same(hints.memberHints, Hints.member(HttpHeader.unsafeApply("X-Member"))) + expect.same(hints.targetHints, Hints(HttpHeader.unsafeApply("X-Target"))) } test( @@ -52,25 +52,25 @@ class HintsSpec() extends FunSuite { ) { val hints = Hints.empty - .addMemberHints(HttpHeader("X-Member")) - .addTargetHints(HttpHeader("X-Foobar")) - expect.same(hints.get(HttpHeader), Some(HttpHeader("X-Member"))) + .addMemberHints(HttpHeader.unsafeApply("X-Member")) + .addTargetHints(HttpHeader.unsafeApply("X-Foobar")) + expect.same(hints.get(HttpHeader), Some(HttpHeader.unsafeApply("X-Member"))) } test("Hints concatenation respect hint level") { - val concat = Hints.member(HttpHeader("X-Member")) ++ Hints(HttpLabel()) - expect.same(concat.memberHints, Hints.member(HttpHeader("X-Member"))) + val concat = Hints.member(HttpHeader.unsafeApply("X-Member")) ++ Hints(HttpLabel()) + expect.same(concat.memberHints, Hints.member(HttpHeader.unsafeApply("X-Member"))) expect.same(concat.targetHints, Hints(HttpLabel())) } test("Hints#add adds to the member layer") { - val concat = Hints.empty.add(HttpHeader("X-Member")) - expect.same(concat, Hints.member(HttpHeader("X-Member"))) + val concat = Hints.empty.add(HttpHeader.unsafeApply("X-Member")) + expect.same(concat, Hints.member(HttpHeader.unsafeApply("X-Member"))) } test("Hints#addTargetHints adds to the target layer") { - val concat = Hints.empty.addTargetHints(HttpHeader("X-Member")) - expect.same(concat, Hints(HttpHeader("X-Member"))) + val concat = Hints.empty.addTargetHints(HttpHeader.unsafeApply("X-Member")) + expect.same(concat, Hints(HttpHeader.unsafeApply("X-Member"))) } test("Hints#expand allows to derive a hint from another hint") { diff --git a/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala b/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala index fdb6ba05f..43eead517 100644 --- a/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala @@ -28,7 +28,7 @@ class PatternSpec extends munit.FunSuite { .fromSchema(UnicodeRegexString.schema) .decode(Document.fromString(s)) - assertEquals(result, Right(UnicodeRegexString(s))) + assertEquals(result, Right(UnicodeRegexString.unsafeApply(s))) } } diff --git a/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala index 0e64c803e..72ffad3a3 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala @@ -13,7 +13,7 @@ final class HttpRequestSpec extends FunSuite { val schema = Schema.struct(Schema.string.required[Foo]("foo", _.foo))(Foo(_)) val endpointHint = - api.Endpoint(hostPrefix = api.NonEmptyString("test.{foo}-other.")) + api.Endpoint(hostPrefix = api.NonEmptyString.unsafeApply("test.{foo}-other.")) val opSchema = OperationSchema( ShapeId("test", "Test"), Hints(endpointHint), diff --git a/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala index a859e9997..e93687a37 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala @@ -13,7 +13,7 @@ final class MetadataDecoderSpec extends FunSuite { .struct( Schema.string .optional[Foo]("deviceType", _.deviceType) - .addHints(api.HttpHeader("x-device-type")) + .addHints(api.HttpHeader.unsafeApply("x-device-type")) .addHints(api.Input()) )(Foo(_)) .addHints(smithy4s.internals.InputOutput.Input.widen) @@ -30,7 +30,7 @@ final class MetadataDecoderSpec extends FunSuite { val field = Schema.string.option .biject[Option[String]](identity[Option[String]](_))(identity(_)) .required[Foo]("name", _.name) - .addHints(smithy.api.HttpHeader("X-Name")) + .addHints(smithy.api.HttpHeader.unsafeApply("X-Name")) Schema .struct(field)(Foo(_)) .addHints(smithy4s.internals.InputOutput.Input.widen) diff --git a/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala index 280deda91..4f635f15d 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala @@ -34,8 +34,8 @@ class PathSpec() extends munit.FunSuite { val schemaA = schema .addHints( Http( - method = NonEmptyString("GET"), - uri = NonEmptyString("/{label}"), + method = NonEmptyString.unsafeApply("GET"), + uri = NonEmptyString.unsafeApply("/{label}"), code = 200 ) ) @@ -132,8 +132,8 @@ class PathSpec() extends munit.FunSuite { )(_ => ()) .addHints( Http( - method = NonEmptyString("GET"), - uri = NonEmptyString("/{label}/const/{secondLabel}"), + method = NonEmptyString.unsafeApply("GET"), + uri = NonEmptyString.unsafeApply("/{label}/const/{secondLabel}"), code = 200 ) ) @@ -154,8 +154,8 @@ class PathSpec() extends munit.FunSuite { )(_ => ()) .addHints( Http( - method = NonEmptyString("GET"), - uri = NonEmptyString("/{label}/const/{greedyLabel+}"), + method = NonEmptyString.unsafeApply("GET"), + uri = NonEmptyString.unsafeApply("/{label}/const/{greedyLabel+}"), code = 200 ) ) diff --git a/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala index be26656fe..d718c6188 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala @@ -164,8 +164,8 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { case class Foo(x: String, y: Option[String]) object Foo { implicit val schema: Schema[Foo] = { - val x = string.required[Foo]("x", _.x).addHints(UrlFormName("xx")) - val y = string.optional[Foo]("y", _.y).addHints(UrlFormName("y:y")) + val x = string.required[Foo]("x", _.x).addHints(UrlFormName.unsafeApply("xx")) + val y = string.optional[Foo]("y", _.y).addHints(UrlFormName.unsafeApply("y:y")) struct(x, y)(Foo.apply) } } @@ -276,7 +276,7 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { case class Foo(foos: List[Int]) object Foo { implicit val schema: Schema[Foo] = { - val foos = list(int.addHints(UrlFormName("x"))) + val foos = list(int.addHints(UrlFormName.unsafeApply("x"))) .required[Foo]("foos", _.foos) struct(foos)(Foo.apply) } @@ -309,7 +309,7 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { implicit val schema: Schema[Foo] = { val foos = list(int) .required[Foo]("foos", _.foos) - .addHints(UrlFormFlattened(), UrlFormName("x")) + .addHints(UrlFormFlattened(), UrlFormName.unsafeApply("x")) struct(foos)(Foo.apply) } } @@ -349,8 +349,8 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { pureTest("union: custom names") { type Foo = Either[Int, String] implicit val schema: Schema[Foo] = Schema.either( - int.addMemberHints(UrlFormName("foo")), - string.addMemberHints(UrlFormName("bar")) + int.addMemberHints(UrlFormName.unsafeApply("foo")), + string.addMemberHints(UrlFormName.unsafeApply("bar")) ) checkEncodingAndDecoding[Foo]( value = Left(1), @@ -407,9 +407,9 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { object Foo { implicit val schema: Schema[Foo] = { val foos = - map(string.addHints(UrlFormName("k")), int.addHints(UrlFormName("v"))) + map(string.addHints(UrlFormName.unsafeApply("k")), int.addHints(UrlFormName.unsafeApply("v"))) .required[Foo]("foos", _.foos) - .addHints(UrlFormName("entries")) + .addHints(UrlFormName.unsafeApply("entries")) struct(foos)(Foo.apply) } } diff --git a/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala b/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala index 61d481f74..15120a5f4 100644 --- a/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala @@ -67,7 +67,7 @@ final class CompositionSpec extends FunSuite { } test("lens composition newtypes") { - val input = GetCityInput(CityId("test")) + val input = GetCityInput(CityId.unsafeApply("test")) val cityName: Lens[GetCityInput, String] = GetCityInput.optics.cityId.value From ba9b81b25a6fe9e2293ad7ed11e7e43e0c062b7a Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 19 Mar 2024 16:51:38 +0200 Subject: [PATCH 05/33] Render validated newtypes only if smithy4sRenderValidatedNewtypes=true --- .../smithy4s/aws/AwsErrorTypeDecoder.scala | 2 +- .../com/amazonaws/dynamodb/DynamoDB.scala | 6 +-- .../dynamodb/ListTablesInputLimit.scala | 12 ++---- .../smithy4s/benchmark/BenchmarkService.scala | 4 +- .../smithy4s/example/AddMenuItemResult.scala | 2 +- .../smithy4s/example/BrandService.scala | 2 +- .../generated/smithy4s/example/CityId.scala | 12 ++---- .../smithy4s/example/CitySummary.scala | 2 +- .../example/DiscriminatedService.scala | 2 +- .../smithy4s/example/DummyService.scala | 6 +-- .../smithy4s/example/EchoInput.scala | 2 +- .../smithy4s/example/FooService.scala | 2 +- .../smithy4s/example/GetObjectOutput.scala | 2 +- .../smithy4s/example/HeadRequestOutput.scala | 2 +- .../smithy4s/example/HeaderEndpointData.scala | 8 ++-- .../smithy4s/example/HeadersStruct.scala | 22 +++++----- .../example/HeadersWithDefaults.scala | 2 +- .../smithy4s/example/HealthRequest.scala | 2 +- .../smithy4s/example/ObjectService.scala | 4 +- .../smithy4s/example/OperationInput.scala | 12 +++--- .../smithy4s/example/OperationOutput.scala | 6 +-- .../smithy4s/example/PizzaAdminService.scala | 28 ++++++------- .../smithy4s/example/PriceError.scala | 2 +- .../smithy4s/example/PutObjectInput.scala | 4 +- .../generated/smithy4s/example/Queries.scala | 22 +++++----- .../example/QueriesWithDefaults.scala | 2 +- .../example/RecursiveInputService.scala | 2 +- .../smithy4s/example/ReservationInput.scala | 2 +- .../smithy4s/example/RoundTripData.scala | 4 +- .../example/ServiceWithNullsAndDefaults.scala | 2 +- .../smithy4s/example/TestInput.scala | 2 +- .../smithy4s/example/UnicodeRegexString.scala | 12 ++---- .../smithy4s/example/ValidatedString.scala | 12 ++---- .../smithy4s/example/ValidationChecks.scala | 6 +-- .../generated/smithy4s/example/Weather.scala | 4 +- .../collision/ReservedNameService.scala | 8 ++-- .../guides/auth/HelloWorldAuthService.scala | 4 +- .../guides/hello/HelloWorldService.scala | 4 +- .../example/hello/HelloWorldService.scala | 2 +- .../smithy4s/example/hello/Person.scala | 2 +- .../smithy4s/example/imp/ImportService.scala | 2 +- .../ReservedNameOverrideService.scala | 2 +- .../smithy4s/example/test/HelloService.scala | 6 +-- .../example/test/HelloWorldService.scala | 2 +- .../smithy4s/example/test/SayHelloInput.scala | 4 +- .../example/test/SayHelloOutput.scala | 2 +- .../generated/weather/WeatherService.scala | 2 +- .../test/src/smithy4s/HintsSpec.scala | 36 ++++++++--------- .../test/src/smithy4s/PatternSpec.scala | 2 +- .../src/smithy4s/http/HttpRequestSpec.scala | 2 +- .../smithy4s/http/MetadataDecoderSpec.scala | 4 +- .../smithy4s/http/internals/PathSpec.scala | 12 +++--- ...mDataEncoderDecoderSchemaVisitorSpec.scala | 16 ++++---- .../src/smithy4s/optics/CompositionSpec.scala | 2 +- .../render-validated-newtypes/build.sbt | 10 +++++ .../project/build.properties | 1 + .../project/plugins.sbt | 9 +++++ .../src/main/scala/Main.scala | 35 ++++++++++++++++ .../src/main/smithy/validated-newtypes.smithy | 15 +++++++ .../render-validated-newtypes/test | 2 + .../codegen/Smithy4sCodegenPlugin.scala | 14 +++++-- .../smithy4s/codegen/internals/Renderer.scala | 25 ++++++++++-- .../codegen/internals/RendererSpec.scala | 40 ++++++++++++++++++- .../ServerHttpComplianceTestCase.scala | 4 +- .../UrlFormDataDecoderSchemaVisitor.scala | 2 +- .../UrlFormDataEncoderSchemaVisitor.scala | 2 +- 66 files changed, 300 insertions(+), 191 deletions(-) create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/build.properties create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/plugins.sbt create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy create mode 100644 modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/test diff --git a/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala b/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala index 1e38b7bcc..8150832c2 100644 --- a/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala +++ b/modules/aws-kernel/src/smithy4s/aws/AwsErrorTypeDecoder.scala @@ -90,7 +90,7 @@ object AwsErrorTypeDecoder { .optional[Body]("__type", _._1) val codeField = string .optional[Body]("code", _._2) - .addHints(smithy.api.XmlName.unsafeApply("Code")) + .addHints(smithy.api.XmlName("Code")) struct(__typeField, codeField)((_, _)) } } diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala index e82aebdff..f41e79195 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/DynamoDB.scala @@ -62,8 +62,8 @@ object DynamoDBGen extends Service.Mixin[DynamoDBGen, DynamoDBOperation] { smithy.api.Title("Amazon DynamoDB"), aws.protocols.AwsJson1_0(http = None, eventStreamHttp = None), smithy.api.Documentation("Amazon DynamoDB\n\n\n

Amazon DynamoDB is a fully managed NoSQL database service that provides fast and\n predictable performance with seamless scalability. DynamoDB lets you offload the\n administrative burdens of operating and scaling a distributed database, so that you don\'t have\n to worry about hardware provisioning, setup and configuration, replication, software patching,\n or cluster scaling.

\n\n

With DynamoDB, you can create database tables that can store and retrieve any amount of\n data, and serve any level of request traffic. You can scale up or scale down your tables\'\n throughput capacity without downtime or performance degradation, and use the AWS Management\n Console to monitor resource utilization and performance metrics.

\n\n

DynamoDB automatically spreads the data and traffic for your tables over a sufficient\n number of servers to handle your throughput and storage requirements, while maintaining\n consistent and fast performance. All of your data is stored on solid state disks (SSDs) and\n automatically replicated across multiple Availability Zones in an AWS region, providing\n built-in high availability and data durability.

"), - aws.api.Service(sdkId = "DynamoDB", arnNamespace = Some(aws.api.ArnNamespace.unsafeApply("dynamodb")), cloudFormationName = Some(aws.api.CloudFormationName.unsafeApply("DynamoDB")), cloudTrailEventSource = Some("dynamodb.amazonaws.com"), docId = None, endpointPrefix = Some("dynamodb")), - smithy.api.XmlNamespace(uri = smithy.api.NonEmptyString.unsafeApply("http://dynamodb.amazonaws.com/doc/2012-08-10/"), prefix = None), + aws.api.Service(sdkId = "DynamoDB", arnNamespace = Some(aws.api.ArnNamespace("dynamodb")), cloudFormationName = Some(aws.api.CloudFormationName("DynamoDB")), cloudTrailEventSource = Some("dynamodb.amazonaws.com"), docId = None, endpointPrefix = Some("dynamodb")), + smithy.api.XmlNamespace(uri = smithy.api.NonEmptyString("http://dynamodb.amazonaws.com/doc/2012-08-10/"), prefix = None), aws.api.ClientEndpointDiscovery(operation = smithy4s.ShapeId(namespace = "com.amazonaws.dynamodb", name = "DescribeEndpoints"), error = Some(smithy4s.ShapeId(namespace = "com.amazonaws.dynamodb", name = "InvalidEndpointException"))), ).lazily @@ -136,7 +136,7 @@ object DynamoDBOperation { .withInput(ListTablesInput.schema) .withError(ListTablesError.errorSchema) .withOutput(ListTablesOutput.schema) - .withHints(aws.api.ClientDiscoveredEndpoint(required = false), smithy.api.Documentation("

Returns an array of table names associated with the current account and endpoint. The output\n from ListTables is paginated, with each page returning a maximum of 100 table\n names.

"), smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString.unsafeApply("ExclusiveStartTableName")), outputToken = Some(smithy.api.NonEmptyString.unsafeApply("LastEvaluatedTableName")), items = Some(smithy.api.NonEmptyString.unsafeApply("TableNames")), pageSize = Some(smithy.api.NonEmptyString.unsafeApply("Limit")))) + .withHints(aws.api.ClientDiscoveredEndpoint(required = false), smithy.api.Documentation("

Returns an array of table names associated with the current account and endpoint. The output\n from ListTables is paginated, with each page returning a maximum of 100 table\n names.

"), smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString("ExclusiveStartTableName")), outputToken = Some(smithy.api.NonEmptyString("LastEvaluatedTableName")), items = Some(smithy.api.NonEmptyString("TableNames")), pageSize = Some(smithy.api.NonEmptyString("Limit")))) def wrap(input: ListTablesInput): ListTables = ListTables(input) } sealed trait ListTablesError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala index ea82e5127..40e5e5e62 100644 --- a/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala +++ b/modules/bootstrapped/src/generated/com/amazonaws/dynamodb/ListTablesInputLimit.scala @@ -1,23 +1,17 @@ package com.amazonaws.dynamodb import smithy4s.Hints -import smithy4s.NewtypeValidated +import smithy4s.Newtype import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.int -object ListTablesInputLimit extends NewtypeValidated[Int] { +object ListTablesInputLimit extends Newtype[Int] { val id: ShapeId = ShapeId("com.amazonaws.dynamodb", "ListTablesInputLimit") val hints: Hints = Hints( smithy.api.Box(), ).lazily val underlyingSchema: Schema[Int] = int.withId(id).addHints(hints).validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(100.0)))) - implicit val schema: Schema[ListTablesInputLimit] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[Int => Either[String, Int]] = List( - a => validateInternal(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(100.0))))(a) - ) - @inline def apply(a: Int): Either[String, ListTablesInputLimit] = validators - .foldLeft(Right(a): Either[String, Int])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + implicit val schema: Schema[ListTablesInputLimit] = bijection(underlyingSchema, asBijection) } diff --git a/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala b/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala index c57a75743..e1ef29d50 100644 --- a/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/benchmark/BenchmarkService.scala @@ -81,7 +81,7 @@ object BenchmarkServiceOperation { val schema: OperationSchema[CreateObjectInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.benchmark", "CreateObject")) .withInput(CreateObjectInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/complex/{bucketName}/{key}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/complex/{bucketName}/{key}"), code = 200)) def wrap(input: CreateObjectInput): CreateObject = CreateObject(input) } final case class SendString(input: SendStringInput) extends BenchmarkServiceOperation[SendStringInput, Nothing, Unit, Nothing, Nothing] { @@ -93,7 +93,7 @@ object BenchmarkServiceOperation { val schema: OperationSchema[SendStringInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.benchmark", "SendString")) .withInput(SendStringInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/simple/{bucketName}/{key}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/simple/{bucketName}/{key}"), code = 200)) def wrap(input: SendStringInput): SendString = SendString(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala b/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala index f226f4b46..6cf0d7d8e 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/AddMenuItemResult.scala @@ -18,7 +18,7 @@ object AddMenuItemResult extends ShapeTag.Companion[AddMenuItemResult] { implicit val schema: Schema[AddMenuItemResult] = struct( string.required[AddMenuItemResult]("itemId", _.itemId).addHints(smithy.api.HttpPayload()), - timestamp.required[AddMenuItemResult]("added", _.added).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader.unsafeApply("X-ADDED-AT")), + timestamp.required[AddMenuItemResult]("added", _.added).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader("X-ADDED-AT")), ){ AddMenuItemResult.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala b/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala index b7c3c347a..5f9d88637 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/BrandService.scala @@ -77,7 +77,7 @@ object BrandServiceOperation { val schema: OperationSchema[AddBrandsInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "AddBrands")) .withInput(AddBrandsInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/brands"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/brands"), code = 200)) def wrap(input: AddBrandsInput): AddBrands = AddBrands(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala b/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala index 808f17552..fa6d6b74d 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/CityId.scala @@ -1,21 +1,15 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.NewtypeValidated +import smithy4s.Newtype import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object CityId extends NewtypeValidated[String] { +object CityId extends Newtype[String] { val id: ShapeId = ShapeId("smithy4s.example", "CityId") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Pattern(s"^[A-Za-z0-9 ]+$$")) - implicit val schema: Schema[CityId] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List( - a => validateInternal(smithy.api.Pattern(s"^[A-Za-z0-9 ]+$$"))(a) - ) - @inline def apply(a: String): Either[String, CityId] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + implicit val schema: Schema[CityId] = bijection(underlyingSchema, asBijection) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala b/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala index 9f699caa9..fac0553d8 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/CitySummary.scala @@ -13,7 +13,7 @@ object CitySummary extends ShapeTag.Companion[CitySummary] { val id: ShapeId = ShapeId("smithy4s.example", "CitySummary") val hints: Hints = Hints( - smithy.api.References(List(smithy.api.Reference(resource = smithy.api.NonEmptyString.unsafeApply("smithy4s.example#City"), ids = None, service = None, rel = None))), + smithy.api.References(List(smithy.api.Reference(resource = smithy.api.NonEmptyString("smithy4s.example#City"), ids = None, service = None, rel = None))), ).lazily implicit val schema: Schema[CitySummary] = struct( diff --git a/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala b/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala index 59eca7090..8c7cb21d2 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/DiscriminatedService.scala @@ -78,7 +78,7 @@ object DiscriminatedServiceOperation { val schema: OperationSchema[TestDiscriminatedInput, Nothing, TestDiscriminatedOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "TestDiscriminated")) .withInput(TestDiscriminatedInput.schema) .withOutput(TestDiscriminatedOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test/{key}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test/{key}"), code = 200), smithy.api.Readonly()) def wrap(input: TestDiscriminatedInput): TestDiscriminated = TestDiscriminated(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala b/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala index d5faa7ec2..80b0a8750 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/DummyService.scala @@ -91,7 +91,7 @@ object DummyServiceOperation { val schema: OperationSchema[Queries, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Dummy")) .withInput(Queries.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy"), code = 200), smithy.api.Readonly()) def wrap(input: Queries): Dummy = Dummy(input) } final case class DummyHostPrefix(input: HostLabelInput) extends DummyServiceOperation[HostLabelInput, Nothing, Unit, Nothing, Nothing] { @@ -103,7 +103,7 @@ object DummyServiceOperation { val schema: OperationSchema[HostLabelInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "DummyHostPrefix")) .withInput(HostLabelInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy"), code = 200), smithy.api.Endpoint(hostPrefix = smithy.api.NonEmptyString.unsafeApply("foo.{label1}--abc{label2}.{label3}.secure."))) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy"), code = 200), smithy.api.Endpoint(hostPrefix = smithy.api.NonEmptyString("foo.{label1}--abc{label2}.{label3}.secure."))) def wrap(input: HostLabelInput): DummyHostPrefix = DummyHostPrefix(input) } final case class DummyPath(input: PathParams) extends DummyServiceOperation[PathParams, Nothing, Unit, Nothing, Nothing] { @@ -115,7 +115,7 @@ object DummyServiceOperation { val schema: OperationSchema[PathParams, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "DummyPath")) .withInput(PathParams.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/dummy-path/{str}/{int}/{ts1}/{ts2}/{ts3}/{ts4}/{b}/{ie}?value=foo&baz=bar"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/dummy-path/{str}/{int}/{ts1}/{ts2}/{ts3}/{ts4}/{b}/{ie}?value=foo&baz=bar"), code = 200), smithy.api.Readonly()) def wrap(input: PathParams): DummyPath = DummyPath(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala index 0c4e1faaf..d0163a892 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/EchoInput.scala @@ -17,7 +17,7 @@ object EchoInput extends ShapeTag.Companion[EchoInput] { implicit val schema: Schema[EchoInput] = struct( string.validated(smithy.api.Length(min = Some(10L), max = None)).required[EchoInput]("pathParam", _.pathParam).addHints(smithy.api.HttpLabel()), EchoBody.schema.required[EchoInput]("body", _.body).addHints(smithy.api.HttpPayload()), - string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[EchoInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery.unsafeApply("queryParam")), + string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[EchoInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery("queryParam")), ){ EchoInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala b/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala index 45be65f5f..52ba798dc 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/FooService.scala @@ -87,7 +87,7 @@ object FooServiceOperation { val schema: OperationSchema[Unit, Nothing, GetFooOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "GetFoo")) .withInput(unit) .withOutput(GetFooOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/foo"), code = 200), smithy.api.Documentation("Returns a useful Foo\nNo input necessary to find our Foo\nThe path for this operation is \"/foo\""), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/foo"), code = 200), smithy.api.Documentation("Returns a useful Foo\nNo input necessary to find our Foo\nThe path for this operation is \"/foo\""), smithy.api.Readonly()) def wrap(input: Unit): GetFoo = GetFoo() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala index a22efed07..642ee7084 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/GetObjectOutput.scala @@ -15,7 +15,7 @@ object GetObjectOutput extends ShapeTag.Companion[GetObjectOutput] { val hints: Hints = Hints.empty implicit val schema: Schema[GetObjectOutput] = struct( - ObjectSize.schema.required[GetObjectOutput]("size", _.size).addHints(smithy.api.HttpHeader.unsafeApply("X-Size")), + ObjectSize.schema.required[GetObjectOutput]("size", _.size).addHints(smithy.api.HttpHeader("X-Size")), string.optional[GetObjectOutput]("data", _.data).addHints(smithy.api.HttpPayload()), ){ GetObjectOutput.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala index e12f0e084..5787aeb46 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadRequestOutput.scala @@ -15,7 +15,7 @@ object HeadRequestOutput extends ShapeTag.Companion[HeadRequestOutput] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadRequestOutput] = struct( - string.required[HeadRequestOutput]("test", _.test).addHints(smithy.api.HttpHeader.unsafeApply("Test")), + string.required[HeadRequestOutput]("test", _.test).addHints(smithy.api.HttpHeader("Test")), ){ HeadRequestOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala index c4eded2a4..7e4913cba 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeaderEndpointData.scala @@ -15,10 +15,10 @@ object HeaderEndpointData extends ShapeTag.Companion[HeaderEndpointData] { val hints: Hints = Hints.empty implicit val schema: Schema[HeaderEndpointData] = struct( - string.optional[HeaderEndpointData]("uppercaseHeader", _.uppercaseHeader).addHints(smithy.api.HttpHeader.unsafeApply("X-UPPERCASE-HEADER")), - string.optional[HeaderEndpointData]("capitalizedHeader", _.capitalizedHeader).addHints(smithy.api.HttpHeader.unsafeApply("X-Capitalized-Header")), - string.optional[HeaderEndpointData]("lowercaseHeader", _.lowercaseHeader).addHints(smithy.api.HttpHeader.unsafeApply("x-lowercase-header")), - string.optional[HeaderEndpointData]("mixedHeader", _.mixedHeader).addHints(smithy.api.HttpHeader.unsafeApply("x-MiXeD-hEaDEr")), + string.optional[HeaderEndpointData]("uppercaseHeader", _.uppercaseHeader).addHints(smithy.api.HttpHeader("X-UPPERCASE-HEADER")), + string.optional[HeaderEndpointData]("capitalizedHeader", _.capitalizedHeader).addHints(smithy.api.HttpHeader("X-Capitalized-Header")), + string.optional[HeaderEndpointData]("lowercaseHeader", _.lowercaseHeader).addHints(smithy.api.HttpHeader("x-lowercase-header")), + string.optional[HeaderEndpointData]("mixedHeader", _.mixedHeader).addHints(smithy.api.HttpHeader("x-MiXeD-hEaDEr")), ){ HeaderEndpointData.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala index 599b64e15..195351dfa 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadersStruct.scala @@ -19,17 +19,17 @@ object HeadersStruct extends ShapeTag.Companion[HeadersStruct] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadersStruct] = struct( - string.optional[HeadersStruct]("str", _.str).addHints(smithy.api.HttpHeader.unsafeApply("str")), - int.optional[HeadersStruct]("int", _.int).addHints(smithy.api.HttpHeader.unsafeApply("int")), - timestamp.optional[HeadersStruct]("ts1", _.ts1).addHints(smithy.api.HttpHeader.unsafeApply("ts1")), - timestamp.optional[HeadersStruct]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpHeader.unsafeApply("ts2")), - timestamp.optional[HeadersStruct]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader.unsafeApply("ts3")), - timestamp.optional[HeadersStruct]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpHeader.unsafeApply("ts4")), - boolean.optional[HeadersStruct]("b", _.b).addHints(smithy.api.HttpHeader.unsafeApply("b")), - StringList.underlyingSchema.optional[HeadersStruct]("sl", _.sl).addHints(smithy.api.HttpHeader.unsafeApply("sl")), - Numbers.schema.optional[HeadersStruct]("ie", _.ie).addHints(smithy.api.HttpHeader.unsafeApply("nums")), - OpenNums.schema.optional[HeadersStruct]("on", _.on).addHints(smithy.api.HttpHeader.unsafeApply("openNums")), - OpenNumsStr.schema.optional[HeadersStruct]("ons", _.ons).addHints(smithy.api.HttpHeader.unsafeApply("openNumsStr")), + string.optional[HeadersStruct]("str", _.str).addHints(smithy.api.HttpHeader("str")), + int.optional[HeadersStruct]("int", _.int).addHints(smithy.api.HttpHeader("int")), + timestamp.optional[HeadersStruct]("ts1", _.ts1).addHints(smithy.api.HttpHeader("ts1")), + timestamp.optional[HeadersStruct]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpHeader("ts2")), + timestamp.optional[HeadersStruct]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpHeader("ts3")), + timestamp.optional[HeadersStruct]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpHeader("ts4")), + boolean.optional[HeadersStruct]("b", _.b).addHints(smithy.api.HttpHeader("b")), + StringList.underlyingSchema.optional[HeadersStruct]("sl", _.sl).addHints(smithy.api.HttpHeader("sl")), + Numbers.schema.optional[HeadersStruct]("ie", _.ie).addHints(smithy.api.HttpHeader("nums")), + OpenNums.schema.optional[HeadersStruct]("on", _.on).addHints(smithy.api.HttpHeader("openNums")), + OpenNumsStr.schema.optional[HeadersStruct]("ons", _.ons).addHints(smithy.api.HttpHeader("openNumsStr")), StringMap.underlyingSchema.optional[HeadersStruct]("slm", _.slm).addHints(smithy.api.HttpPrefixHeaders("foo-")), ){ HeadersStruct.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala index 1714cb7fd..da79656e5 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HeadersWithDefaults.scala @@ -15,7 +15,7 @@ object HeadersWithDefaults extends ShapeTag.Companion[HeadersWithDefaults] { val hints: Hints = Hints.empty implicit val schema: Schema[HeadersWithDefaults] = struct( - string.field[HeadersWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpHeader.unsafeApply("dflt")), + string.field[HeadersWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpHeader("dflt")), ){ HeadersWithDefaults.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala b/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala index bd0d84c0c..bdd5ff029 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/HealthRequest.scala @@ -15,7 +15,7 @@ object HealthRequest extends ShapeTag.Companion[HealthRequest] { val hints: Hints = Hints.empty implicit val schema: Schema[HealthRequest] = struct( - string.validated(smithy.api.Length(min = Some(0L), max = Some(5L))).optional[HealthRequest]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("query")), + string.validated(smithy.api.Length(min = Some(0L), max = Some(5L))).optional[HealthRequest]("query", _.query).addHints(smithy.api.HttpQuery("query")), ){ HealthRequest.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala b/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala index d2b55868e..eaadab04b 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ObjectService.scala @@ -98,7 +98,7 @@ object ObjectServiceOperation { .withInput(PutObjectInput.schema) .withError(PutObjectError.errorSchema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("PUT"), uri = smithy.api.NonEmptyString.unsafeApply("/{bucketName}/{key}"), code = 200), smithy.api.Idempotent()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("PUT"), uri = smithy.api.NonEmptyString("/{bucketName}/{key}"), code = 200), smithy.api.Idempotent()) def wrap(input: PutObjectInput): PutObject = PutObject(input) } sealed trait PutObjectError extends scala.Product with scala.Serializable { self => @@ -191,7 +191,7 @@ object ObjectServiceOperation { .withInput(GetObjectInput.schema) .withError(GetObjectError.errorSchema) .withOutput(GetObjectOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/{bucketName}/{key}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/{bucketName}/{key}"), code = 200), smithy.api.Readonly()) def wrap(input: GetObjectInput): GetObject = GetObject(input) } sealed trait GetObjectError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala index e0637ced2..6640a2556 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/OperationInput.scala @@ -20,13 +20,13 @@ object OperationInput extends ShapeTag.Companion[OperationInput] { string.field[OperationInput]("optionalWithDefault", _.optionalWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-default"))), string.required[OperationInput]("requiredLabel", _.requiredLabel).addHints(smithy.api.Default(smithy4s.Document.fromString("required-label-with-default")), smithy.api.HttpLabel()), string.required[OperationInput]("requiredWithDefault", _.requiredWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-default"))), - string.field[OperationInput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader.unsafeApply("optional-header-with-default")), - string.required[OperationInput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader.unsafeApply("required-header-with-default")), - string.field[OperationInput]("optionalQueryWithDefault", _.optionalQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-query-with-default")), smithy.api.HttpQuery.unsafeApply("optional-query-with-default")), - string.field[OperationInput]("requiredQueryWithDefault", _.requiredQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-query-with-default")), smithy.api.HttpQuery.unsafeApply("required-query-with-default")), + string.field[OperationInput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader("optional-header-with-default")), + string.required[OperationInput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader("required-header-with-default")), + string.field[OperationInput]("optionalQueryWithDefault", _.optionalQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-query-with-default")), smithy.api.HttpQuery("optional-query-with-default")), + string.field[OperationInput]("requiredQueryWithDefault", _.requiredQueryWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-query-with-default")), smithy.api.HttpQuery("required-query-with-default")), string.optional[OperationInput]("optional", _.optional), - string.optional[OperationInput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader.unsafeApply("optional-header")), - string.optional[OperationInput]("optionalQuery", _.optionalQuery).addHints(smithy.api.HttpQuery.unsafeApply("optional-query")), + string.optional[OperationInput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader("optional-header")), + string.optional[OperationInput]("optionalQuery", _.optionalQuery).addHints(smithy.api.HttpQuery("optional-query")), ){ OperationInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala index 13d671ba1..c059e1ac9 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/OperationOutput.scala @@ -19,10 +19,10 @@ object OperationOutput extends ShapeTag.Companion[OperationOutput] { implicit val schema: Schema[OperationOutput] = struct( string.field[OperationOutput]("optionalWithDefault", _.optionalWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-default"))), string.required[OperationOutput]("requiredWithDefault", _.requiredWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-default"))), - string.field[OperationOutput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader.unsafeApply("optional-header-with-default")), - string.required[OperationOutput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader.unsafeApply("required-header-with-default")), + string.field[OperationOutput]("optionalHeaderWithDefault", _.optionalHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("optional-header-with-default")), smithy.api.HttpHeader("optional-header-with-default")), + string.required[OperationOutput]("requiredHeaderWithDefault", _.requiredHeaderWithDefault).addHints(smithy.api.Default(smithy4s.Document.fromString("required-header-with-default")), smithy.api.HttpHeader("required-header-with-default")), string.optional[OperationOutput]("optional", _.optional), - string.optional[OperationOutput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader.unsafeApply("optional-header")), + string.optional[OperationOutput]("optionalHeader", _.optionalHeader).addHints(smithy.api.HttpHeader("optional-header")), ){ OperationOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala b/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala index 839c9e764..21a8c652e 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PizzaAdminService.scala @@ -147,7 +147,7 @@ object PizzaAdminServiceOperation { .withInput(AddMenuItemRequest.schema) .withError(AddMenuItemError.errorSchema) .withOutput(AddMenuItemResult.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/restaurant/{restaurant}/menu/item"), code = 201)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/restaurant/{restaurant}/menu/item"), code = 201)) def wrap(input: AddMenuItemRequest): AddMenuItem = AddMenuItem(input) } sealed trait AddMenuItemError extends scala.Product with scala.Serializable { self => @@ -240,7 +240,7 @@ object PizzaAdminServiceOperation { .withInput(GetMenuRequest.schema) .withError(GetMenuError.errorSchema) .withOutput(GetMenuResult.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/restaurant/{restaurant}/menu"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/restaurant/{restaurant}/menu"), code = 200), smithy.api.Readonly()) def wrap(input: GetMenuRequest): GetMenu = GetMenu(input) } sealed trait GetMenuError extends scala.Product with scala.Serializable { self => @@ -347,7 +347,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, VersionOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Version")) .withInput(unit) .withOutput(VersionOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/version"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/version"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): Version = Version() } final case class Health(input: HealthRequest) extends PizzaAdminServiceOperation[HealthRequest, PizzaAdminServiceOperation.HealthError, HealthResponse, Nothing, Nothing] { @@ -360,7 +360,7 @@ object PizzaAdminServiceOperation { .withInput(HealthRequest.schema) .withError(HealthError.errorSchema) .withOutput(HealthResponse.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/health"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/health"), code = 200), smithy.api.Readonly()) def wrap(input: HealthRequest): Health = Health(input) } sealed trait HealthError extends scala.Product with scala.Serializable { self => @@ -424,7 +424,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[HeaderEndpointData, Nothing, HeaderEndpointData, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "HeaderEndpoint")) .withInput(HeaderEndpointData.schema) .withOutput(HeaderEndpointData.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/headers/"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/headers/"), code = 200)) def wrap(input: HeaderEndpointData): HeaderEndpoint = HeaderEndpoint(input) } final case class RoundTrip(input: RoundTripData) extends PizzaAdminServiceOperation[RoundTripData, Nothing, RoundTripData, Nothing, Nothing] { @@ -436,7 +436,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[RoundTripData, Nothing, RoundTripData, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "RoundTrip")) .withInput(RoundTripData.schema) .withOutput(RoundTripData.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/roundTrip/{label}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/roundTrip/{label}"), code = 200)) def wrap(input: RoundTripData): RoundTrip = RoundTrip(input) } final case class GetEnum(input: GetEnumInput) extends PizzaAdminServiceOperation[GetEnumInput, PizzaAdminServiceOperation.GetEnumError, GetEnumOutput, Nothing, Nothing] { @@ -449,7 +449,7 @@ object PizzaAdminServiceOperation { .withInput(GetEnumInput.schema) .withError(GetEnumError.errorSchema) .withOutput(GetEnumOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/get-enum/{aa}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/get-enum/{aa}"), code = 200), smithy.api.Readonly()) def wrap(input: GetEnumInput): GetEnum = GetEnum(input) } sealed trait GetEnumError extends scala.Product with scala.Serializable { self => @@ -514,7 +514,7 @@ object PizzaAdminServiceOperation { .withInput(GetIntEnumInput.schema) .withError(GetIntEnumError.errorSchema) .withOutput(GetIntEnumOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/get-int-enum/{aa}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/get-int-enum/{aa}"), code = 200), smithy.api.Readonly()) def wrap(input: GetIntEnumInput): GetIntEnum = GetIntEnum(input) } sealed trait GetIntEnumError extends scala.Product with scala.Serializable { self => @@ -579,7 +579,7 @@ object PizzaAdminServiceOperation { .withInput(CustomCodeInput.schema) .withError(CustomCodeError.errorSchema) .withOutput(CustomCodeOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/custom-code/{code}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/custom-code/{code}"), code = 200), smithy.api.Readonly()) def wrap(input: CustomCodeInput): CustomCode = CustomCode(input) } sealed trait CustomCodeError extends scala.Product with scala.Serializable { self => @@ -643,7 +643,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[ReservationInput, Nothing, ReservationOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Reservation")) .withInput(ReservationInput.schema) .withOutput(ReservationOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/book/{name}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/book/{name}"), code = 200)) def wrap(input: ReservationInput): Reservation = Reservation(input) } final case class Echo(input: EchoInput) extends PizzaAdminServiceOperation[EchoInput, Nothing, Unit, Nothing, Nothing] { @@ -655,7 +655,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[EchoInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Echo")) .withInput(EchoInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/echo/{pathParam}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/echo/{pathParam}"), code = 200)) def wrap(input: EchoInput): Echo = Echo(input) } final case class OptionalOutput() extends PizzaAdminServiceOperation[Unit, Nothing, OptionalOutputOutput, Nothing, Nothing] { @@ -668,7 +668,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, OptionalOutputOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "OptionalOutput")) .withInput(unit) .withOutput(OptionalOutputOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/optional-output"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/optional-output"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): OptionalOutput = OptionalOutput() } final case class HeadRequest() extends PizzaAdminServiceOperation[Unit, Nothing, HeadRequestOutput, Nothing, Nothing] { @@ -681,7 +681,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, HeadRequestOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "HeadRequest")) .withInput(unit) .withOutput(HeadRequestOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("HEAD"), uri = smithy.api.NonEmptyString.unsafeApply("/head-request"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("HEAD"), uri = smithy.api.NonEmptyString("/head-request"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): HeadRequest = HeadRequest() } final case class NoContentRequest() extends PizzaAdminServiceOperation[Unit, Nothing, Unit, Nothing, Nothing] { @@ -694,7 +694,7 @@ object PizzaAdminServiceOperation { val schema: OperationSchema[Unit, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "NoContentRequest")) .withInput(unit) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/no-content"), code = 204), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/no-content"), code = 204), smithy.api.Readonly()) def wrap(input: Unit): NoContentRequest = NoContentRequest() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala b/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala index 7ee3b2b21..017b2d358 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PriceError.scala @@ -22,7 +22,7 @@ object PriceError extends ShapeTag.Companion[PriceError] { implicit val schema: Schema[PriceError] = struct( string.required[PriceError]("message", _.message), - int.required[PriceError]("code", _.code).addHints(smithy.api.HttpHeader.unsafeApply("X-CODE")), + int.required[PriceError]("code", _.code).addHints(smithy.api.HttpHeader("X-CODE")), ){ PriceError.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala index 2c6d7d980..e9b408d9e 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/PutObjectInput.scala @@ -21,8 +21,8 @@ object PutObjectInput extends ShapeTag.Companion[PutObjectInput] { ObjectKey.schema.required[PutObjectInput]("key", _.key).addHints(smithy.api.HttpLabel()), BucketName.schema.required[PutObjectInput]("bucketName", _.bucketName).addHints(smithy.api.HttpLabel()), string.required[PutObjectInput]("data", _.data).addHints(smithy.api.HttpPayload()), - LowHigh.schema.optional[PutObjectInput]("foo", _.foo).addHints(smithy.api.HttpHeader.unsafeApply("X-Foo")), - SomeValue.schema.optional[PutObjectInput]("someValue", _.someValue).addHints(smithy.api.HttpQuery.unsafeApply("paramName")), + LowHigh.schema.optional[PutObjectInput]("foo", _.foo).addHints(smithy.api.HttpHeader("X-Foo")), + SomeValue.schema.optional[PutObjectInput]("someValue", _.someValue).addHints(smithy.api.HttpQuery("paramName")), ){ PutObjectInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala b/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala index 95e49bbc6..602f663d6 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/Queries.scala @@ -19,17 +19,17 @@ object Queries extends ShapeTag.Companion[Queries] { val hints: Hints = Hints.empty implicit val schema: Schema[Queries] = struct( - string.optional[Queries]("str", _.str).addHints(smithy.api.HttpQuery.unsafeApply("str")), - int.optional[Queries]("int", _.int).addHints(smithy.api.HttpQuery.unsafeApply("int")), - timestamp.optional[Queries]("ts1", _.ts1).addHints(smithy.api.HttpQuery.unsafeApply("ts1")), - timestamp.optional[Queries]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpQuery.unsafeApply("ts2")), - timestamp.optional[Queries]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpQuery.unsafeApply("ts3")), - timestamp.optional[Queries]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpQuery.unsafeApply("ts4")), - boolean.optional[Queries]("b", _.b).addHints(smithy.api.HttpQuery.unsafeApply("b")), - StringList.underlyingSchema.optional[Queries]("sl", _.sl).addHints(smithy.api.HttpQuery.unsafeApply("sl")), - Numbers.schema.optional[Queries]("ie", _.ie).addHints(smithy.api.HttpQuery.unsafeApply("nums")), - OpenNums.schema.optional[Queries]("on", _.on).addHints(smithy.api.HttpQuery.unsafeApply("openNums")), - OpenNumsStr.schema.optional[Queries]("ons", _.ons).addHints(smithy.api.HttpQuery.unsafeApply("openNumsStr")), + string.optional[Queries]("str", _.str).addHints(smithy.api.HttpQuery("str")), + int.optional[Queries]("int", _.int).addHints(smithy.api.HttpQuery("int")), + timestamp.optional[Queries]("ts1", _.ts1).addHints(smithy.api.HttpQuery("ts1")), + timestamp.optional[Queries]("ts2", _.ts2).addHints(smithy.api.TimestampFormat.DATE_TIME.widen, smithy.api.HttpQuery("ts2")), + timestamp.optional[Queries]("ts3", _.ts3).addHints(smithy.api.TimestampFormat.EPOCH_SECONDS.widen, smithy.api.HttpQuery("ts3")), + timestamp.optional[Queries]("ts4", _.ts4).addHints(smithy.api.TimestampFormat.HTTP_DATE.widen, smithy.api.HttpQuery("ts4")), + boolean.optional[Queries]("b", _.b).addHints(smithy.api.HttpQuery("b")), + StringList.underlyingSchema.optional[Queries]("sl", _.sl).addHints(smithy.api.HttpQuery("sl")), + Numbers.schema.optional[Queries]("ie", _.ie).addHints(smithy.api.HttpQuery("nums")), + OpenNums.schema.optional[Queries]("on", _.on).addHints(smithy.api.HttpQuery("openNums")), + OpenNumsStr.schema.optional[Queries]("ons", _.ons).addHints(smithy.api.HttpQuery("openNumsStr")), StringMap.underlyingSchema.optional[Queries]("slm", _.slm).addHints(smithy.api.HttpQueryParams()), ){ Queries.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala index 3f38a00fc..6b7849756 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/QueriesWithDefaults.scala @@ -15,7 +15,7 @@ object QueriesWithDefaults extends ShapeTag.Companion[QueriesWithDefaults] { val hints: Hints = Hints.empty implicit val schema: Schema[QueriesWithDefaults] = struct( - string.field[QueriesWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpQuery.unsafeApply("dflt")), + string.field[QueriesWithDefaults]("dflt", _.dflt).addHints(smithy.api.Default(smithy4s.Document.fromString("test")), smithy.api.HttpQuery("dflt")), ){ QueriesWithDefaults.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala b/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala index e4a9e1430..9c6dcc710 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/RecursiveInputService.scala @@ -79,7 +79,7 @@ object RecursiveInputServiceOperation { val schema: OperationSchema[RecursiveInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "RecursiveInputOperation")) .withInput(RecursiveInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("PUT"), uri = smithy.api.NonEmptyString.unsafeApply("/subscriptions"), code = 200), smithy.api.Idempotent()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("PUT"), uri = smithy.api.NonEmptyString("/subscriptions"), code = 200), smithy.api.Idempotent()) def wrap(input: RecursiveInput): RecursiveInputOperation = RecursiveInputOperation(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala index ff452e074..d5bf33d33 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ReservationInput.scala @@ -18,7 +18,7 @@ object ReservationInput extends ShapeTag.Companion[ReservationInput] { implicit val schema: Schema[ReservationInput] = struct( string.required[ReservationInput]("name", _.name).addHints(smithy.api.HttpLabel()), - string.optional[ReservationInput]("town", _.town).addHints(smithy.api.HttpQuery.unsafeApply("town")), + string.optional[ReservationInput]("town", _.town).addHints(smithy.api.HttpQuery("town")), ){ ReservationInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala b/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala index 7e0a9df6d..7c9082350 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/RoundTripData.scala @@ -16,8 +16,8 @@ object RoundTripData extends ShapeTag.Companion[RoundTripData] { implicit val schema: Schema[RoundTripData] = struct( string.required[RoundTripData]("label", _.label).addHints(smithy.api.HttpLabel()), - string.optional[RoundTripData]("header", _.header).addHints(smithy.api.HttpHeader.unsafeApply("HEADER")), - string.optional[RoundTripData]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("query")), + string.optional[RoundTripData]("header", _.header).addHints(smithy.api.HttpHeader("HEADER")), + string.optional[RoundTripData]("query", _.query).addHints(smithy.api.HttpQuery("query")), string.optional[RoundTripData]("body", _.body), ){ RoundTripData.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala b/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala index e1d3d1d4f..61fc1b5e3 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ServiceWithNullsAndDefaults.scala @@ -78,7 +78,7 @@ object ServiceWithNullsAndDefaultsOperation { val schema: OperationSchema[OperationInput, Nothing, OperationOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "Operation")) .withInput(OperationInput.schema) .withOutput(OperationOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/operation/{requiredLabel}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/operation/{requiredLabel}"), code = 200)) def wrap(input: OperationInput): Operation = Operation(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala index 16a3ee3b2..a1b180044 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/TestInput.scala @@ -24,7 +24,7 @@ object TestInput extends ShapeTag.Companion[TestInput] { implicit val schema: Schema[TestInput] = struct( string.validated(smithy.api.Length(min = Some(10L), max = None)).required[TestInput]("pathParam", _.pathParam).addHints(smithy.api.HttpLabel()), TestBody.schema.required[TestInput]("body", _.body).addHints(smithy.api.HttpPayload()), - string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[TestInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery.unsafeApply("queryParam")), + string.validated(smithy.api.Length(min = Some(10L), max = None)).optional[TestInput]("queryParam", _.queryParam).addHints(smithy.api.HttpQuery("queryParam")), ){ TestInput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala b/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala index cb5b2e27d..27f704393 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/UnicodeRegexString.scala @@ -1,21 +1,15 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.NewtypeValidated +import smithy4s.Newtype import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object UnicodeRegexString extends NewtypeValidated[String] { +object UnicodeRegexString extends Newtype[String] { val id: ShapeId = ShapeId("smithy4s.example", "UnicodeRegexString") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Pattern(s"^\uD83D\uDE0E$$")) - implicit val schema: Schema[UnicodeRegexString] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List( - a => validateInternal(smithy.api.Pattern(s"^\uD83D\uDE0E$$"))(a) - ) - @inline def apply(a: String): Either[String, UnicodeRegexString] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + implicit val schema: Schema[UnicodeRegexString] = bijection(underlyingSchema, asBijection) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala index e40ca23f6..18c331f8e 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala @@ -1,21 +1,15 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.NewtypeValidated +import smithy4s.Newtype import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object ValidatedString extends NewtypeValidated[String] { +object ValidatedString extends Newtype[String] { val id: ShapeId = ShapeId("smithy4s.example", "ValidatedString") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) - implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List( - a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) - ) - @inline def apply(a: String): Either[String, ValidatedString] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijection) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala index 3b8fff68d..af58ae91c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidationChecks.scala @@ -16,9 +16,9 @@ object ValidationChecks extends ShapeTag.Companion[ValidationChecks] { val hints: Hints = Hints.empty implicit val schema: Schema[ValidationChecks] = struct( - string.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("str", _.str).addHints(smithy.api.HttpQuery.unsafeApply("str")), - StringList.underlyingSchema.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("lst", _.lst).addHints(smithy.api.HttpQuery.unsafeApply("lst")), - int.validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(10.0)))).optional[ValidationChecks]("int", _.int).addHints(smithy.api.HttpQuery.unsafeApply("int")), + string.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("str", _.str).addHints(smithy.api.HttpQuery("str")), + StringList.underlyingSchema.validated(smithy.api.Length(min = Some(1L), max = Some(10L))).optional[ValidationChecks]("lst", _.lst).addHints(smithy.api.HttpQuery("lst")), + int.validated(smithy.api.Range(min = Some(scala.math.BigDecimal(1.0)), max = Some(scala.math.BigDecimal(10.0)))).optional[ValidationChecks]("int", _.int).addHints(smithy.api.HttpQuery("int")), ){ ValidationChecks.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala b/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala index 3a4354cbe..4015fa5f1 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/Weather.scala @@ -33,7 +33,7 @@ object WeatherGen extends Service.Mixin[WeatherGen, WeatherOperation] { val hints: Hints = Hints( smithy.api.Documentation("Provides weather forecasts."), - smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString.unsafeApply("nextToken")), outputToken = Some(smithy.api.NonEmptyString.unsafeApply("nextToken")), items = None, pageSize = Some(smithy.api.NonEmptyString.unsafeApply("pageSize"))), + smithy.api.Paginated(inputToken = Some(smithy.api.NonEmptyString("nextToken")), outputToken = Some(smithy.api.NonEmptyString("nextToken")), items = None, pageSize = Some(smithy.api.NonEmptyString("pageSize"))), ).lazily def apply[F[_]](implicit F: Impl[F]): F.type = F @@ -188,7 +188,7 @@ object WeatherOperation { val schema: OperationSchema[ListCitiesInput, Nothing, ListCitiesOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example", "ListCities")) .withInput(ListCitiesInput.schema) .withOutput(ListCitiesOutput.schema) - .withHints(smithy.api.Paginated(inputToken = None, outputToken = None, items = Some(smithy.api.NonEmptyString.unsafeApply("items")), pageSize = None), smithy.api.Readonly()) + .withHints(smithy.api.Paginated(inputToken = None, outputToken = None, items = Some(smithy.api.NonEmptyString("items")), pageSize = None), smithy.api.Readonly()) def wrap(input: ListCitiesInput): ListCities = ListCities(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala b/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala index 8184f45e5..eb4652255 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/collision/ReservedNameService.scala @@ -91,7 +91,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[SetInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Set")) .withInput(SetInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/set/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/set/"), code = 204)) def wrap(input: SetInput): Set = Set(input) } final case class List(input: ListInput) extends ReservedNameServiceOperation[ListInput, Nothing, Unit, Nothing, Nothing] { @@ -103,7 +103,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[ListInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "List")) .withInput(ListInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/list/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/list/"), code = 204)) def wrap(input: ListInput): List = List(input) } final case class Map(input: MapInput) extends ReservedNameServiceOperation[MapInput, Nothing, Unit, Nothing, Nothing] { @@ -115,7 +115,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[MapInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Map")) .withInput(MapInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/map/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/map/"), code = 204)) def wrap(input: MapInput): Map = Map(input) } final case class Option(input: OptionInput) extends ReservedNameServiceOperation[OptionInput, Nothing, Unit, Nothing, Nothing] { @@ -127,7 +127,7 @@ object ReservedNameServiceOperation { val schema: OperationSchema[OptionInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.collision", "Option")) .withInput(OptionInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/option/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/option/"), code = 204)) def wrap(input: OptionInput): Option = Option(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala b/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala index c8d51acbb..a678023e3 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/guides/auth/HelloWorldAuthService.scala @@ -93,7 +93,7 @@ object HelloWorldAuthServiceOperation { .withInput(unit) .withError(SayWorldError.errorSchema) .withOutput(World.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/hello"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/hello"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): SayWorld = SayWorld() } sealed trait SayWorldError extends scala.Product with scala.Serializable { self => @@ -159,7 +159,7 @@ object HelloWorldAuthServiceOperation { .withInput(unit) .withError(HealthCheckError.errorSchema) .withOutput(HealthCheckOutput.schema) - .withHints(smithy.api.Auth(Set()), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/health"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Auth(Set()), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/health"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): HealthCheck = HealthCheck() } sealed trait HealthCheckError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala index 149a8ffba..32fdf7701 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/guides/hello/HelloWorldService.scala @@ -26,7 +26,7 @@ object HelloWorldServiceGen extends Service.Mixin[HelloWorldServiceGen, HelloWor val hints: Hints = Hints( alloy.SimpleRestJson(), - smithy.api.Cors(origin = smithy.api.NonEmptyString.unsafeApply("http://mysite.com"), maxAge = 600, additionalAllowedHeaders = Some(List(smithy.api.NonEmptyString.unsafeApply("Authorization"))), additionalExposedHeaders = Some(List(smithy.api.NonEmptyString.unsafeApply("X-Smithy4s")))), + smithy.api.Cors(origin = smithy.api.NonEmptyString("http://mysite.com"), maxAge = 600, additionalAllowedHeaders = Some(List(smithy.api.NonEmptyString("Authorization"))), additionalExposedHeaders = Some(List(smithy.api.NonEmptyString("X-Smithy4s")))), ).lazily def apply[F[_]](implicit F: Impl[F]): F.type = F @@ -81,7 +81,7 @@ object HelloWorldServiceOperation { val schema: OperationSchema[Unit, Nothing, World, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.guides.hello", "SayWorld")) .withInput(unit) .withOutput(World.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/hello"), code = 200), smithy.api.Readonly()) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/hello"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): SayWorld = SayWorld() } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala index 85d1fd76c..1ef9acb62 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/hello/HelloWorldService.scala @@ -85,7 +85,7 @@ object HelloWorldServiceOperation { .withInput(Person.schema) .withError(HelloError.errorSchema) .withOutput(Greeting.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/{name}"), code = 200), smithy.api.Tags(List("testOperationTag"))) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/{name}"), code = 200), smithy.api.Tags(List("testOperationTag"))) def wrap(input: Person): Hello = Hello(input) } sealed trait HelloError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala b/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala index 04d806779..9f3790325 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/hello/Person.scala @@ -16,7 +16,7 @@ object Person extends ShapeTag.Companion[Person] { implicit val schema: Schema[Person] = struct( string.required[Person]("name", _.name).addHints(smithy.api.HttpLabel()), - string.optional[Person]("town", _.town).addHints(smithy.api.HttpQuery.unsafeApply("town")), + string.optional[Person]("town", _.town).addHints(smithy.api.HttpQuery("town")), ){ Person.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala b/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala index eaf48b081..3c6d39aa5 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/imp/ImportService.scala @@ -88,7 +88,7 @@ object ImportServiceOperation { .withInput(unit) .withError(ImportOperationError.errorSchema) .withOutput(OpOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test"), code = 200)) def wrap(input: Unit): ImportOperation = ImportOperation() } sealed trait ImportOperationError extends scala.Product with scala.Serializable { self => diff --git a/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala b/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala index f84525a75..5f67990e6 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/reservedNameOverride/ReservedNameOverrideService.scala @@ -79,7 +79,7 @@ object ReservedNameOverrideServiceOperation { val schema: OperationSchema[SetOpInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.reservedNameOverride", "SetOp")) .withInput(SetOpInput.schema) .withOutput(unit) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/api/set/"), code = 204)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/api/set/"), code = 204)) def wrap(input: SetOpInput): SetOp = SetOp(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala index f51ba2c10..518557b0a 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloService.scala @@ -93,7 +93,7 @@ object HelloServiceOperation { .withInput(SayHelloInput.schema) .withError(SayHelloError.errorSchema) .withOutput(SayHelloOutput.schema) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/", host = None, resolvedHost = None, authScheme = None, queryParams = Some(List("Hi=Hello%20there")), forbidQueryParams = None, requireQueryParams = None, headers = Some(Map("X-Greeting" -> "Hi")), forbidHeaders = None, requireHeaders = None, body = Some("{\"name\":\"Teddy\"}"), bodyMediaType = Some("application/json"), params = Some(smithy4s.Document.obj("greeting" -> smithy4s.Document.fromString("Hi"), "name" -> smithy4s.Document.fromString("Teddy"), "query" -> smithy4s.Document.fromString("Hello there"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/"), code = 200), smithy.test.HttpResponseTests(List(smithy.test.HttpResponseTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), code = 200, authScheme = None, headers = Some(Map("X-H1" -> "V1")), forbidHeaders = None, requireHeaders = None, body = Some("{\"result\":\"Hello!\"}"), bodyMediaType = None, params = Some(smithy4s.Document.obj("payload" -> smithy4s.Document.obj("result" -> smithy4s.Document.fromString("Hello!")), "header1" -> smithy4s.Document.fromString("V1"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None)))) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/", host = None, resolvedHost = None, authScheme = None, queryParams = Some(List("Hi=Hello%20there")), forbidQueryParams = None, requireQueryParams = None, headers = Some(Map("X-Greeting" -> "Hi")), forbidHeaders = None, requireHeaders = None, body = Some("{\"name\":\"Teddy\"}"), bodyMediaType = Some("application/json"), params = Some(smithy4s.Document.obj("greeting" -> smithy4s.Document.fromString("Hi"), "name" -> smithy4s.Document.fromString("Teddy"), "query" -> smithy4s.Document.fromString("Hello there"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/"), code = 200), smithy.test.HttpResponseTests(List(smithy.test.HttpResponseTestCase(id = "say_hello", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), code = 200, authScheme = None, headers = Some(Map("X-H1" -> "V1")), forbidHeaders = None, requireHeaders = None, body = Some("{\"result\":\"Hello!\"}"), bodyMediaType = None, params = Some(smithy4s.Document.obj("payload" -> smithy4s.Document.obj("result" -> smithy4s.Document.fromString("Hello!")), "header1" -> smithy4s.Document.fromString("V1"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None)))) def wrap(input: SayHelloInput): SayHello = SayHello(input) } sealed trait SayHelloError extends scala.Product with scala.Serializable { self => @@ -172,7 +172,7 @@ object HelloServiceOperation { val schema: OperationSchema[Unit, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "Listen")) .withInput(unit) .withOutput(unit) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "listen", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/listen", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = None, vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/listen"), code = 200), smithy.api.Readonly()) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "listen", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/listen", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = None, vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/listen"), code = 200), smithy.api.Readonly()) def wrap(input: Unit): Listen = Listen() } final case class TestPath(input: TestPathInput) extends HelloServiceOperation[TestPathInput, Nothing, Unit, Nothing, Nothing] { @@ -184,7 +184,7 @@ object HelloServiceOperation { val schema: OperationSchema[TestPathInput, Nothing, Unit, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "TestPath")) .withInput(TestPathInput.schema) .withOutput(unit) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "TestPath", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/test-path/sameValue", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("path" -> smithy4s.Document.fromString("sameValue"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/test-path/{path}"), code = 200), smithy.api.Readonly()) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "TestPath", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "GET", uri = "/test-path/sameValue", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("path" -> smithy4s.Document.fromString("sameValue"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/test-path/{path}"), code = 200), smithy.api.Readonly()) def wrap(input: TestPathInput): TestPath = TestPath(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala index 485170eb0..e1135ef1c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/HelloWorldService.scala @@ -78,7 +78,7 @@ object HelloWorldServiceOperation { val schema: OperationSchema[HelloInput, Nothing, HelloOutput, Nothing, Nothing] = Schema.operation(ShapeId("smithy4s.example.test", "Hello")) .withInput(HelloInput.schema) .withOutput(HelloOutput.schema) - .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "helloSuccess", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/World", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None), smithy.test.HttpRequestTestCase(id = "helloFails", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/fail", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("POST"), uri = smithy.api.NonEmptyString.unsafeApply("/{name}"), code = 200)) + .withHints(smithy.test.HttpRequestTests(List(smithy.test.HttpRequestTestCase(id = "helloSuccess", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/World", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None), smithy.test.HttpRequestTestCase(id = "helloFails", protocol = smithy4s.ShapeId(namespace = "alloy", name = "simpleRestJson"), method = "POST", uri = "/fail", host = None, resolvedHost = None, authScheme = None, queryParams = None, forbidQueryParams = None, requireQueryParams = None, headers = None, forbidHeaders = None, requireHeaders = None, body = None, bodyMediaType = None, params = Some(smithy4s.Document.obj("name" -> smithy4s.Document.fromString("World"))), vendorParams = None, vendorParamsShape = None, documentation = None, tags = None, appliesTo = None))), smithy.api.Http(method = smithy.api.NonEmptyString("POST"), uri = smithy.api.NonEmptyString("/{name}"), code = 200)) def wrap(input: HelloInput): Hello = Hello(input) } } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala index 6189355d4..df5ff96c1 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloInput.scala @@ -17,8 +17,8 @@ object SayHelloInput extends ShapeTag.Companion[SayHelloInput] { ).lazily implicit val schema: Schema[SayHelloInput] = struct( - string.optional[SayHelloInput]("greeting", _.greeting).addHints(smithy.api.HttpHeader.unsafeApply("X-Greeting")), - string.optional[SayHelloInput]("query", _.query).addHints(smithy.api.HttpQuery.unsafeApply("Hi")), + string.optional[SayHelloInput]("greeting", _.greeting).addHints(smithy.api.HttpHeader("X-Greeting")), + string.optional[SayHelloInput]("query", _.query).addHints(smithy.api.HttpQuery("Hi")), string.optional[SayHelloInput]("name", _.name), ){ SayHelloInput.apply diff --git a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala index aead36af7..ff1f22f0c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/test/SayHelloOutput.scala @@ -16,7 +16,7 @@ object SayHelloOutput extends ShapeTag.Companion[SayHelloOutput] { implicit val schema: Schema[SayHelloOutput] = struct( SayHelloPayload.schema.required[SayHelloOutput]("payload", _.payload).addHints(smithy.api.HttpPayload()), - string.required[SayHelloOutput]("header1", _.header1).addHints(smithy.api.HttpHeader.unsafeApply("X-H1")), + string.required[SayHelloOutput]("header1", _.header1).addHints(smithy.api.HttpHeader("X-H1")), ){ SayHelloOutput.apply }.withId(id).addHints(hints) diff --git a/modules/bootstrapped/src/generated/weather/WeatherService.scala b/modules/bootstrapped/src/generated/weather/WeatherService.scala index 8d35d4fcf..3258c14e9 100644 --- a/modules/bootstrapped/src/generated/weather/WeatherService.scala +++ b/modules/bootstrapped/src/generated/weather/WeatherService.scala @@ -78,7 +78,7 @@ object WeatherServiceOperation { val schema: OperationSchema[GetWeatherInput, Nothing, GetWeatherOutput, Nothing, Nothing] = Schema.operation(ShapeId("weather", "GetWeather")) .withInput(GetWeatherInput.schema) .withOutput(GetWeatherOutput.schema) - .withHints(smithy.api.Http(method = smithy.api.NonEmptyString.unsafeApply("GET"), uri = smithy.api.NonEmptyString.unsafeApply("/weather/{city}"), code = 200)) + .withHints(smithy.api.Http(method = smithy.api.NonEmptyString("GET"), uri = smithy.api.NonEmptyString("/weather/{city}"), code = 200)) def wrap(input: GetWeatherInput): GetWeather = GetWeather(input) } } diff --git a/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala b/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala index a58afbed0..f8d436284 100644 --- a/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/HintsSpec.scala @@ -24,27 +24,27 @@ import smithy.api.Required class HintsSpec() extends FunSuite { test("hints work as expected with newtypes") { - val hints = Hints(HttpHeader.unsafeApply("X-Foobar")) - expect(hints.get(HttpHeader) == Some(HttpHeader.unsafeApply("X-Foobar"))) + val hints = Hints(HttpHeader("X-Foobar")) + expect(hints.get(HttpHeader) == Some(HttpHeader("X-Foobar"))) } test("hints work as expected with newtypes (using implicits)") { - val hints = Hints(HttpHeader.unsafeApply("X-Foobar")) - expect.same(hints.get[HttpHeader], Some(HttpHeader.unsafeApply("X-Foobar"))) + val hints = Hints(HttpHeader("X-Foobar")) + expect.same(hints.get[HttpHeader], Some(HttpHeader("X-Foobar"))) } test("hints can be stored as member hints") { - val hints = Hints(HttpLabel()).addMemberHints(HttpHeader.unsafeApply("X-Foobar")) + val hints = Hints(HttpLabel()).addMemberHints(HttpHeader("X-Foobar")) // Member and target hints are both looked at when searching for a hint. - expect.same(hints.get(HttpHeader), Some(HttpHeader.unsafeApply("X-Foobar"))) + expect.same(hints.get(HttpHeader), Some(HttpHeader("X-Foobar"))) expect.same(hints.get(HttpLabel), Some(HttpLabel())) } test("Member hints are stored separately from target hints") { val hints = - Hints(HttpHeader.unsafeApply("X-Target")).addMemberHints(HttpHeader.unsafeApply("X-Member")) - expect.same(hints.memberHints, Hints.member(HttpHeader.unsafeApply("X-Member"))) - expect.same(hints.targetHints, Hints(HttpHeader.unsafeApply("X-Target"))) + Hints(HttpHeader("X-Target")).addMemberHints(HttpHeader("X-Member")) + expect.same(hints.memberHints, Hints.member(HttpHeader("X-Member"))) + expect.same(hints.targetHints, Hints(HttpHeader("X-Target"))) } test( @@ -52,25 +52,25 @@ class HintsSpec() extends FunSuite { ) { val hints = Hints.empty - .addMemberHints(HttpHeader.unsafeApply("X-Member")) - .addTargetHints(HttpHeader.unsafeApply("X-Foobar")) - expect.same(hints.get(HttpHeader), Some(HttpHeader.unsafeApply("X-Member"))) + .addMemberHints(HttpHeader("X-Member")) + .addTargetHints(HttpHeader("X-Foobar")) + expect.same(hints.get(HttpHeader), Some(HttpHeader("X-Member"))) } test("Hints concatenation respect hint level") { - val concat = Hints.member(HttpHeader.unsafeApply("X-Member")) ++ Hints(HttpLabel()) - expect.same(concat.memberHints, Hints.member(HttpHeader.unsafeApply("X-Member"))) + val concat = Hints.member(HttpHeader("X-Member")) ++ Hints(HttpLabel()) + expect.same(concat.memberHints, Hints.member(HttpHeader("X-Member"))) expect.same(concat.targetHints, Hints(HttpLabel())) } test("Hints#add adds to the member layer") { - val concat = Hints.empty.add(HttpHeader.unsafeApply("X-Member")) - expect.same(concat, Hints.member(HttpHeader.unsafeApply("X-Member"))) + val concat = Hints.empty.add(HttpHeader("X-Member")) + expect.same(concat, Hints.member(HttpHeader("X-Member"))) } test("Hints#addTargetHints adds to the target layer") { - val concat = Hints.empty.addTargetHints(HttpHeader.unsafeApply("X-Member")) - expect.same(concat, Hints(HttpHeader.unsafeApply("X-Member"))) + val concat = Hints.empty.addTargetHints(HttpHeader("X-Member")) + expect.same(concat, Hints(HttpHeader("X-Member"))) } test("Hints#expand allows to derive a hint from another hint") { diff --git a/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala b/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala index 43eead517..fdb6ba05f 100644 --- a/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/PatternSpec.scala @@ -28,7 +28,7 @@ class PatternSpec extends munit.FunSuite { .fromSchema(UnicodeRegexString.schema) .decode(Document.fromString(s)) - assertEquals(result, Right(UnicodeRegexString.unsafeApply(s))) + assertEquals(result, Right(UnicodeRegexString(s))) } } diff --git a/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala index 72ffad3a3..0e64c803e 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/HttpRequestSpec.scala @@ -13,7 +13,7 @@ final class HttpRequestSpec extends FunSuite { val schema = Schema.struct(Schema.string.required[Foo]("foo", _.foo))(Foo(_)) val endpointHint = - api.Endpoint(hostPrefix = api.NonEmptyString.unsafeApply("test.{foo}-other.")) + api.Endpoint(hostPrefix = api.NonEmptyString("test.{foo}-other.")) val opSchema = OperationSchema( ShapeId("test", "Test"), Hints(endpointHint), diff --git a/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala index e93687a37..a859e9997 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/MetadataDecoderSpec.scala @@ -13,7 +13,7 @@ final class MetadataDecoderSpec extends FunSuite { .struct( Schema.string .optional[Foo]("deviceType", _.deviceType) - .addHints(api.HttpHeader.unsafeApply("x-device-type")) + .addHints(api.HttpHeader("x-device-type")) .addHints(api.Input()) )(Foo(_)) .addHints(smithy4s.internals.InputOutput.Input.widen) @@ -30,7 +30,7 @@ final class MetadataDecoderSpec extends FunSuite { val field = Schema.string.option .biject[Option[String]](identity[Option[String]](_))(identity(_)) .required[Foo]("name", _.name) - .addHints(smithy.api.HttpHeader.unsafeApply("X-Name")) + .addHints(smithy.api.HttpHeader("X-Name")) Schema .struct(field)(Foo(_)) .addHints(smithy4s.internals.InputOutput.Input.widen) diff --git a/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala index 4f635f15d..280deda91 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/internals/PathSpec.scala @@ -34,8 +34,8 @@ class PathSpec() extends munit.FunSuite { val schemaA = schema .addHints( Http( - method = NonEmptyString.unsafeApply("GET"), - uri = NonEmptyString.unsafeApply("/{label}"), + method = NonEmptyString("GET"), + uri = NonEmptyString("/{label}"), code = 200 ) ) @@ -132,8 +132,8 @@ class PathSpec() extends munit.FunSuite { )(_ => ()) .addHints( Http( - method = NonEmptyString.unsafeApply("GET"), - uri = NonEmptyString.unsafeApply("/{label}/const/{secondLabel}"), + method = NonEmptyString("GET"), + uri = NonEmptyString("/{label}/const/{secondLabel}"), code = 200 ) ) @@ -154,8 +154,8 @@ class PathSpec() extends munit.FunSuite { )(_ => ()) .addHints( Http( - method = NonEmptyString.unsafeApply("GET"), - uri = NonEmptyString.unsafeApply("/{label}/const/{greedyLabel+}"), + method = NonEmptyString("GET"), + uri = NonEmptyString("/{label}/const/{greedyLabel+}"), code = 200 ) ) diff --git a/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala b/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala index d718c6188..be26656fe 100644 --- a/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/http/internals/UrlFormDataEncoderDecoderSchemaVisitorSpec.scala @@ -164,8 +164,8 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { case class Foo(x: String, y: Option[String]) object Foo { implicit val schema: Schema[Foo] = { - val x = string.required[Foo]("x", _.x).addHints(UrlFormName.unsafeApply("xx")) - val y = string.optional[Foo]("y", _.y).addHints(UrlFormName.unsafeApply("y:y")) + val x = string.required[Foo]("x", _.x).addHints(UrlFormName("xx")) + val y = string.optional[Foo]("y", _.y).addHints(UrlFormName("y:y")) struct(x, y)(Foo.apply) } } @@ -276,7 +276,7 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { case class Foo(foos: List[Int]) object Foo { implicit val schema: Schema[Foo] = { - val foos = list(int.addHints(UrlFormName.unsafeApply("x"))) + val foos = list(int.addHints(UrlFormName("x"))) .required[Foo]("foos", _.foos) struct(foos)(Foo.apply) } @@ -309,7 +309,7 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { implicit val schema: Schema[Foo] = { val foos = list(int) .required[Foo]("foos", _.foos) - .addHints(UrlFormFlattened(), UrlFormName.unsafeApply("x")) + .addHints(UrlFormFlattened(), UrlFormName("x")) struct(foos)(Foo.apply) } } @@ -349,8 +349,8 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { pureTest("union: custom names") { type Foo = Either[Int, String] implicit val schema: Schema[Foo] = Schema.either( - int.addMemberHints(UrlFormName.unsafeApply("foo")), - string.addMemberHints(UrlFormName.unsafeApply("bar")) + int.addMemberHints(UrlFormName("foo")), + string.addMemberHints(UrlFormName("bar")) ) checkEncodingAndDecoding[Foo]( value = Left(1), @@ -407,9 +407,9 @@ object UrlFormDataEncoderDecoderSchemaVisitorSpec extends SimpleIOSuite { object Foo { implicit val schema: Schema[Foo] = { val foos = - map(string.addHints(UrlFormName.unsafeApply("k")), int.addHints(UrlFormName.unsafeApply("v"))) + map(string.addHints(UrlFormName("k")), int.addHints(UrlFormName("v"))) .required[Foo]("foos", _.foos) - .addHints(UrlFormName.unsafeApply("entries")) + .addHints(UrlFormName("entries")) struct(foos)(Foo.apply) } } diff --git a/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala b/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala index 15120a5f4..61d481f74 100644 --- a/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/optics/CompositionSpec.scala @@ -67,7 +67,7 @@ final class CompositionSpec extends FunSuite { } test("lens composition newtypes") { - val input = GetCityInput(CityId.unsafeApply("test")) + val input = GetCityInput(CityId("test")) val cityName: Lens[GetCityInput, String] = GetCityInput.optics.cityId.value diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt new file mode 100644 index 000000000..1dd2fe9bc --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt @@ -0,0 +1,10 @@ +lazy val root = (project in file(".")) + .enablePlugins(Smithy4sCodegenPlugin) + .settings( + scalaVersion := "2.13.10", + Compile / smithy4sRenderValidatedNewtypes := true, + libraryDependencies ++= Seq( + "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value, + "com.disneystreaming.smithy4s" %% "smithy4s-dynamic" % smithy4sVersion.value + ) + ) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/build.properties b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/build.properties new file mode 100644 index 000000000..72413de15 --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.8.3 diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/plugins.sbt b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/plugins.sbt new file mode 100644 index 000000000..b8589b92c --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/project/plugins.sbt @@ -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 + ) +} diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala new file mode 100644 index 000000000..3052d7576 --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2021-2024 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 demo + +import smithy4s.example.{ SimpleValidatedString, TestValidatedNewTypes } + +object Main extends App { + try { + val validatedStringOrError: Either[String, SimpleValidatedString] = SimpleValidatedString("test") + println( + validatedStringOrError match { + case Right(value) => s"Success: ${TestValidatedNewTypes(value, "unwraped string test")}" + case Left(error) => s"Error: $error" + } + ) + } catch { + case _: java.lang.ExceptionInInitializerError => + println("failed") + sys.exit(1) + } +} diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy new file mode 100644 index 000000000..879bf393a --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy @@ -0,0 +1,15 @@ +namespace smithy4s.example + +use smithy4s.meta#unwrap + +@length(min: 1, max: 10) +string SimpleValidatedString + +@unwrap +@length(min: 1, max: 10) +string UnwrappedValidatedString + +structure TestValidatedNewTypes { + one: SimpleValidatedString + two: UnwrappedValidatedString +} diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/test b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/test new file mode 100644 index 000000000..863293500 --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/test @@ -0,0 +1,2 @@ +# check if smithy4sCodegen works and everything compiles +> compile diff --git a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala index 4f2c625ad..5bc558262 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala @@ -138,6 +138,11 @@ object Smithy4sCodegenPlugin extends AutoPlugin { "Boolean value to indicate whether or not to generate optics" ) + val smithy4sRenderValidatedNewtypes = + taskKey[Boolean]( + "Boolean value to indicate whether or not to generate validated newtypes" + ) + val smithy4sGeneratedSmithyFiles = taskKey[Seq[File]]( "Generated smithy files" @@ -252,14 +257,15 @@ object Smithy4sCodegenPlugin extends AutoPlugin { } }, config / smithy4sRenderOptics := false, + config / smithy4sRenderValidatedNewtypes := false, config / smithy4sGeneratedSmithyMetadataFile := { (config / sourceManaged).value / "smithy" / "generated-metadata.smithy" }, config / smithy4sGeneratedSmithyFiles := { val cacheFactory = (config / streams).value.cacheStoreFactory - val cached = Tracked.inputChanged[(String, Boolean), Seq[File]]( + val cached = Tracked.inputChanged[(String, Boolean, Boolean), Seq[File]]( cacheFactory.make("smithy4sGeneratedSmithyFilesInput") - ) { case (changed, (wildcardArg, shouldGenerateOptics)) => + ) { case (changed, (wildcardArg, shouldGenerateOptics, shouldRenderValidatedNewtypes)) => val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") ) { case (changed, prevResult) => @@ -270,6 +276,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { s"""$$version: "2" |metadata smithy4sWildcardArgument = "$wildcardArg" |metadata smithy4sRenderOptics = $shouldGenerateOptics + |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes |""".stripMargin ) Seq(file) @@ -281,7 +288,8 @@ object Smithy4sCodegenPlugin extends AutoPlugin { } val wildcardArg = (config / smithy4sWildcardArgument).value val generateOptics = (config / smithy4sRenderOptics).value - cached((wildcardArg, generateOptics)) + val renderValidatedNewtypes = (config / smithy4sRenderValidatedNewtypes).value + cached((wildcardArg, generateOptics, renderValidatedNewtypes)) }, config / sourceGenerators += (config / smithy4sCodegen).map( _.filter(_.ext == "scala") diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index bbac2f041..8c38a84fc 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -41,7 +41,8 @@ private[internals] object Renderer { case class Config( errorsAsScala3Unions: Boolean, wildcardArgument: String, - renderOptics: Boolean + renderOptics: Boolean, + renderValidatedNewtypes: Boolean ) object Config { def load(metadata: Map[String, Node]): Renderer.Config = { @@ -61,6 +62,12 @@ private[internals] object Renderer { .flatMap(_.asBooleanNode().asScala) .map(_.getValue()) .getOrElse(false) + + val renderValidatedNewtypes = metadata + .get("smithy4sRenderValidatedNewtypes") + .flatMap(_.asBooleanNode().asScala) + .map(_.getValue()) + .getOrElse(false) if (wildcardArgument != "?" && wildcardArgument != "_") { throw new IllegalArgumentException( @@ -71,7 +78,8 @@ private[internals] object Renderer { Renderer.Config( errorsAsScala3Unions = errorsAsScala3Unions, wildcardArgument = wildcardArgument, - renderOptics = renderOptics + renderOptics = renderOptics, + renderValidatedNewtypes = renderValidatedNewtypes ) } } @@ -186,7 +194,11 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => case ta @ TypeAlias(shapeId, _, tpe, _, recursive, hints) => renderNewtype(shapeId, ta.nameRef, tpe, recursive, hints) case vta @ ValidatedTypeAlias(shapeId, _, tpe, recursive, hints) => - renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) + if(compilationUnit.rendererConfig.renderValidatedNewtypes) { + renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) + } else { + renderNewtype(shapeId, vta.nameRef, tpe, recursive, hints) + } case enumeration @ Enumeration(shapeId, _, tag, values, hints) => renderEnum(shapeId, enumeration.nameRef, tag, values, hints) } @@ -1581,7 +1593,12 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => if (wroteCollection && !topLevel) false -> text else - false -> line"${ref.show}.unsafeApply($text)" + if(compilationUnit.rendererConfig.renderValidatedNewtypes) { + false -> line"${ref.show}.unsafeApply($text)" + + } else { + false -> line"${ref.show}($text)" + } }) case AltTN(ref, altName, AltValueTN.TypeAltTN(alt)) => diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index 64243fd51..828505f1b 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -485,10 +485,44 @@ final class RendererSpec extends munit.FunSuite { ) } - test("newtype with constraint") { + test("newtype with constraint and smithy4sRenderValidatedNewtypes=false") { val smithy = """ |$version: "2" | + |metadata smithy4sRenderValidatedNewtypes=false + | + |namespace smithy4s + | + |@length(min: 1, max: 10) + |string MyValidatedString + | + |structure ValidatedFoo { + | mvs: MyValidatedString + |} + |""".stripMargin + + val contents = generateScalaCode(smithy).values + + assert( + contents.exists( + _.contains("object MyValidatedString extends Newtype[String]") + ) + ) + assert( + contents.exists( + _.contains( + "final case class ValidatedFoo(mvs: Option[MyValidatedString] = None)" + ) + ) + ) + } + + test("newtype with constraint and smithy4sRenderValidatedNewtypes=true") { + val smithy = """ + |$version: "2" + | + |metadata smithy4sRenderValidatedNewtypes=true + | |namespace smithy4s | |@length(min: 1, max: 10) @@ -515,10 +549,12 @@ final class RendererSpec extends munit.FunSuite { ) } - test("unwrapped newtype with constraint") { + test("unwrapped newtype with constraint and smithy4sRenderValidatedNewtypes=true") { val smithy = """ |$version: "2" | + |metadata smithy4sRenderValidatedNewtypes=true + | |namespace smithy4s | |use smithy4s.meta#unwrap diff --git a/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala b/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala index adb020cf9..47482ae5e 100644 --- a/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala +++ b/modules/compliance-tests/src/smithy4s/compliancetests/internals/ServerHttpComplianceTestCase.scala @@ -245,8 +245,8 @@ private[compliancetests] class ServerHttpComplianceTestCase[ ): (Service.Reflective[NoInputOp], Request[F]) = { val newHints = { val newHttp = smithy.api.Http( - method = smithy.api.NonEmptyString.unsafeApply("GET"), - uri = smithy.api.NonEmptyString.unsafeApply("/") + method = smithy.api.NonEmptyString("GET"), + uri = smithy.api.NonEmptyString("/") ) val code = endpoint.hints.get[smithy.api.Http].map(_.code).getOrElse(newHttp.code) diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala index 0efb6066f..720a693b6 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataDecoderSchemaVisitor.scala @@ -112,7 +112,7 @@ private[http] class UrlFormDataDecoderSchemaVisitor( val vField = value.required[KV]("value", _._2) Schema .struct(kField, vField)((_, _)) - .addHints(UrlFormName.unsafeApply("entry")) + .addHints(UrlFormName("entry")) } compile(Schema.vector(kvSchema).addHints(hints)) .map(_.toMap) diff --git a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala index 1332828b4..edceb1794 100644 --- a/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala +++ b/modules/core/src/smithy4s/http/internals/UrlFormDataEncoderSchemaVisitor.scala @@ -96,7 +96,7 @@ private[http] class UrlFormDataEncoderSchemaVisitor( val vField = value.required[KV]("value", _._2) Schema .struct(kField, vField)((_, _)) - .addHints(UrlFormName.unsafeApply("entry")) + .addHints(UrlFormName("entry")) } // Avoid serialising empty maps, see comment in collection case and // https://github.com/smithy-lang/smithy/issues/1868. From 301ed39d906c2ce21a868cc30ad794c86fa5dda3 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 19 Mar 2024 17:04:35 +0200 Subject: [PATCH 06/33] Fix formatting again --- .../codegen/Smithy4sCodegenPlugin.scala | 61 +++++++++++-------- .../smithy4s/codegen/internals/Renderer.scala | 17 +++--- .../codegen/internals/RendererSpec.scala | 4 +- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala index 5bc558262..e6c22d30e 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala @@ -141,7 +141,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { val smithy4sRenderValidatedNewtypes = taskKey[Boolean]( "Boolean value to indicate whether or not to generate validated newtypes" - ) + ) val smithy4sGeneratedSmithyFiles = taskKey[Seq[File]]( @@ -263,32 +263,45 @@ object Smithy4sCodegenPlugin extends AutoPlugin { }, config / smithy4sGeneratedSmithyFiles := { val cacheFactory = (config / streams).value.cacheStoreFactory - val cached = Tracked.inputChanged[(String, Boolean, Boolean), Seq[File]]( - cacheFactory.make("smithy4sGeneratedSmithyFilesInput") - ) { case (changed, (wildcardArg, shouldGenerateOptics, shouldRenderValidatedNewtypes)) => - val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( - cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") - ) { case (changed, prevResult) => - if (changed || prevResult.isEmpty) { - val file = (config / smithy4sGeneratedSmithyMetadataFile).value - IO.write( - file, - s"""$$version: "2" - |metadata smithy4sWildcardArgument = "$wildcardArg" - |metadata smithy4sRenderOptics = $shouldGenerateOptics - |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes - |""".stripMargin - ) - Seq(file) - } else { - prevResult.get + val cached = + Tracked + .inputChanged[(String, Boolean, Boolean), Seq[File]]( + cacheFactory.make("smithy4sGeneratedSmithyFilesInput") + ) { + case ( + changed, + ( + wildcardArg, + shouldGenerateOptics, + shouldRenderValidatedNewtypes + ) + ) => + val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( + cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") + ) { + case (changed, prevResult) => + if (changed || prevResult.isEmpty) { + val file = + (config / smithy4sGeneratedSmithyMetadataFile).value + IO.write( + file, + s"""$$version: "2" + |metadata smithy4sWildcardArgument = "$wildcardArg" + |metadata smithy4sRenderOptics = $shouldGenerateOptics + |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes + |""".stripMargin + ) + Seq(file) + } else { + prevResult.get + } + } + lastOutput(changed) } - } - lastOutput(changed) - } val wildcardArg = (config / smithy4sWildcardArgument).value val generateOptics = (config / smithy4sRenderOptics).value - val renderValidatedNewtypes = (config / smithy4sRenderValidatedNewtypes).value + val renderValidatedNewtypes = + (config / smithy4sRenderValidatedNewtypes).value cached((wildcardArg, generateOptics, renderValidatedNewtypes)) }, config / sourceGenerators += (config / smithy4sCodegen).map( diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 8c38a84fc..2af6521a8 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -62,7 +62,7 @@ private[internals] object Renderer { .flatMap(_.asBooleanNode().asScala) .map(_.getValue()) .getOrElse(false) - + val renderValidatedNewtypes = metadata .get("smithy4sRenderValidatedNewtypes") .flatMap(_.asBooleanNode().asScala) @@ -194,7 +194,7 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => case ta @ TypeAlias(shapeId, _, tpe, _, recursive, hints) => renderNewtype(shapeId, ta.nameRef, tpe, recursive, hints) case vta @ ValidatedTypeAlias(shapeId, _, tpe, recursive, hints) => - if(compilationUnit.rendererConfig.renderValidatedNewtypes) { + if (compilationUnit.rendererConfig.renderValidatedNewtypes) { renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) } else { renderNewtype(shapeId, vta.nameRef, tpe, recursive, hints) @@ -1592,13 +1592,12 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => val (wroteCollection, text) = target.run(topLevel) if (wroteCollection && !topLevel) false -> text - else - if(compilationUnit.rendererConfig.renderValidatedNewtypes) { - false -> line"${ref.show}.unsafeApply($text)" - - } else { - false -> line"${ref.show}($text)" - } + else if (compilationUnit.rendererConfig.renderValidatedNewtypes) { + false -> line"${ref.show}.unsafeApply($text)" + + } else { + false -> line"${ref.show}($text)" + } }) case AltTN(ref, altName, AltValueTN.TypeAltTN(alt)) => diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index 828505f1b..d25d919fd 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -549,7 +549,9 @@ final class RendererSpec extends munit.FunSuite { ) } - test("unwrapped newtype with constraint and smithy4sRenderValidatedNewtypes=true") { + test( + "unwrapped newtype with constraint and smithy4sRenderValidatedNewtypes=true" + ) { val smithy = """ |$version: "2" | From cf9ec8eb2df2b3e91ee798823f943dc632297877 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Mon, 25 Mar 2024 19:25:50 +0200 Subject: [PATCH 07/33] Don't validate types from smithy.api namespace --- .../render-validated-newtypes/build.sbt | 3 +- .../src/main/scala/Main.scala | 15 +++++--- .../src/main/smithy/validated-newtypes.smithy | 23 ++++++++--- .../codegen/GenerateSmithyBuild.scala | 3 +- .../codegen/Smithy4sCodegenPlugin.scala | 38 ++++++++++--------- .../internals/CollisionAvoidance.scala | 4 +- .../codegen/internals/ModelLoader.scala | 4 +- .../codegen/internals/SmithyToIR.scala | 20 ++++++---- .../smithy4s/codegen/internals/ToLines.scala | 2 +- 9 files changed, 68 insertions(+), 44 deletions(-) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt index 1dd2fe9bc..c7d5992db 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt @@ -5,6 +5,7 @@ lazy val root = (project in file(".")) Compile / smithy4sRenderValidatedNewtypes := true, libraryDependencies ++= Seq( "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value, - "com.disneystreaming.smithy4s" %% "smithy4s-dynamic" % smithy4sVersion.value + "com.disneystreaming.smithy4s" %% "smithy4s-dynamic" % smithy4sVersion.value, + "com.disneystreaming.alloy" % "alloy-core" % "0.3.4", ) ) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala index 3052d7576..e69390a1c 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala @@ -14,17 +14,20 @@ * limitations under the License. */ -package demo +package newtypes.validated -import smithy4s.example.{ SimpleValidatedString, TestValidatedNewTypes } +import newtypes.validated._ object Main extends App { try { - val validatedStringOrError: Either[String, SimpleValidatedString] = SimpleValidatedString("test") + val cityOrError: Either[String, ValidatedCity] = ValidatedCity("test-city") + val nameOrError: Either[String, ValidatedName] = ValidatedName("test-name") + val country: String = "test-country" + println( - validatedStringOrError match { - case Right(value) => s"Success: ${TestValidatedNewTypes(value, "unwraped string test")}" - case Left(error) => s"Error: $error" + (nameOrError, cityOrError) match { + case (Right(name), Right(city)) => s"Success: ${Person(name, Some(city), Some("unwraped string test"))}" + case _ => s"Error" } ) } catch { diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy index 879bf393a..156d7512c 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy @@ -1,15 +1,26 @@ -namespace smithy4s.example +namespace newtypes.validated use smithy4s.meta#unwrap +use alloy#simpleRestJson @length(min: 1, max: 10) -string SimpleValidatedString +string ValidatedCity + +@length(min: 1, max: 10) +string ValidatedName @unwrap @length(min: 1, max: 10) -string UnwrappedValidatedString +string ValidatedCountry + +structure Person { + @httpLabel + @required + name: ValidatedName + + @httpQuery("town") + town: ValidatedCity -structure TestValidatedNewTypes { - one: SimpleValidatedString - two: UnwrappedValidatedString + @httpQuery("country") + country: ValidatedCountry } diff --git a/modules/codegen-plugin/src/smithy4s/codegen/GenerateSmithyBuild.scala b/modules/codegen-plugin/src/smithy4s/codegen/GenerateSmithyBuild.scala index d7f830b25..6f0f51f7f 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/GenerateSmithyBuild.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/GenerateSmithyBuild.scala @@ -16,8 +16,9 @@ package smithy4s.codegen -import sbt._ import sbt.Keys._ +import sbt._ + import Smithy4sCodegenPlugin.autoImport._ private final case class SmithyBuildData( diff --git a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala index e6c22d30e..883fa7fae 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala @@ -19,7 +19,10 @@ package smithy4s.codegen import sbt.Keys._ import sbt.util.CacheImplicits._ import sbt.{fileJsonFormatter => _, _} -import scala.util.{Success, Try} + +import scala.util.Success +import scala.util.Try + import JsonConverters._ object Smithy4sCodegenPlugin extends AutoPlugin { @@ -278,23 +281,22 @@ object Smithy4sCodegenPlugin extends AutoPlugin { ) => val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") - ) { - case (changed, prevResult) => - if (changed || prevResult.isEmpty) { - val file = - (config / smithy4sGeneratedSmithyMetadataFile).value - IO.write( - file, - s"""$$version: "2" - |metadata smithy4sWildcardArgument = "$wildcardArg" - |metadata smithy4sRenderOptics = $shouldGenerateOptics - |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes - |""".stripMargin - ) - Seq(file) - } else { - prevResult.get - } + ) { case (changed, prevResult) => + if (changed || prevResult.isEmpty) { + val file = + (config / smithy4sGeneratedSmithyMetadataFile).value + IO.write( + file, + s"""$$version: "2" + |metadata smithy4sWildcardArgument = "$wildcardArg" + |metadata smithy4sRenderOptics = $shouldGenerateOptics + |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes + |""".stripMargin + ) + Seq(file) + } else { + prevResult.get + } } lastOutput(changed) } diff --git a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala index f64cdc5f0..ad09d6259 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala @@ -19,12 +19,12 @@ package smithy4s.codegen.internals import cats.~> import Type.Alias +import Type.Nullable import Type.PrimitiveType +import Type.ValidatedAlias import TypedNode._ import Type.ExternalType import LineSegment._ -import smithy4s.codegen.internals.Type.Nullable -import smithy4s.codegen.internals.Type.ValidatedAlias private[internals] object CollisionAvoidance { def apply(compilationUnit: CompilationUnit): CompilationUnit = { diff --git a/modules/codegen/src/smithy4s/codegen/internals/ModelLoader.scala b/modules/codegen/src/smithy4s/codegen/internals/ModelLoader.scala index d9ec46158..9958dc94c 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/ModelLoader.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/ModelLoader.scala @@ -30,10 +30,10 @@ import software.amazon.smithy.model.loader.ModelManifestException import java.io.File import java.net.URLClassLoader -import scala.jdk.CollectionConverters._ import java.nio.file.FileSystems -import scala.util.Using import java.nio.file.Files +import scala.jdk.CollectionConverters._ +import scala.util.Using private[codegen] object ModelLoader { diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index 53874cf43..178000e4a 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -16,27 +16,28 @@ package smithy4s.codegen.internals +import alloy.StructurePatternTrait import cats.data.NonEmptyList import cats.implicits._ import smithy4s.meta.AdtMemberTrait +import smithy4s.meta.AdtTrait import smithy4s.meta.ErrorMessageTrait +import smithy4s.meta.GenerateOpticsTrait +import smithy4s.meta.GenerateServiceProductTrait import smithy4s.meta.IndexedSeqTrait import smithy4s.meta.NoStackTraceTrait import smithy4s.meta.PackedInputsTrait import smithy4s.meta.RefinementTrait -import smithy4s.meta.VectorTrait -import smithy4s.meta.AdtTrait -import smithy4s.meta.GenerateServiceProductTrait -import smithy4s.meta.GenerateOpticsTrait import smithy4s.meta.TypeclassTrait -import alloy.StructurePatternTrait +import smithy4s.meta.VectorTrait import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.Model import software.amazon.smithy.model.node._ import software.amazon.smithy.model.selector.PathFinder import software.amazon.smithy.model.shapes._ import software.amazon.smithy.model.traits.DefaultTrait -import software.amazon.smithy.model.traits.{RequiredTrait, TimestampFormatTrait} +import software.amazon.smithy.model.traits.RequiredTrait +import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits._ import scala.annotation.nowarn @@ -525,6 +526,10 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { shapeId.getName() ) + private def canBeRenderedAsValidatedNewtype(shapeId: ShapeId): Boolean = { + shapeId.getNamespace() != smithyNamespace + } + private sealed trait ExternalTypeInfo private object ExternalTypeInfo { case class RefinementInfo(trt: RefinementTrait) extends ExternalTypeInfo @@ -619,7 +624,8 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { !isUnboxedPrimitive(shape.getId()) ) { val isUnwrapped = isUnwrappedShape(shape) - if (isUnwrapped || !hasConstraints(shape)) { + val canBeValidated = canBeRenderedAsValidatedNewtype(shape.getId()) + if (isUnwrapped || !(hasConstraints(shape) && canBeValidated)) { Type .Alias( shape.getId().getNamespace(), diff --git a/modules/codegen/src/smithy4s/codegen/internals/ToLines.scala b/modules/codegen/src/smithy4s/codegen/internals/ToLines.scala index b26b0e387..d544596f8 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/ToLines.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/ToLines.scala @@ -16,11 +16,11 @@ package smithy4s.codegen.internals +import cats.Foldable import cats.kernel.Monoid import cats.syntax.all._ import LineSegment.{Literal, NameRef} -import cats.Foldable /** * Construct allowing to flatten arbitrary levels of nested lists From de82030e07df36d6ff03f727e6b2a0c0a5fdaa08 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Mon, 25 Mar 2024 19:30:56 +0200 Subject: [PATCH 08/33] Minor fixup of sbt plugin test --- .../render-validated-newtypes/src/main/scala/Main.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala index e69390a1c..0eec3bd3c 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/scala/Main.scala @@ -26,7 +26,7 @@ object Main extends App { println( (nameOrError, cityOrError) match { - case (Right(name), Right(city)) => s"Success: ${Person(name, Some(city), Some("unwraped string test"))}" + case (Right(name), Right(city)) => s"Success: ${Person(name, Some(city), Some(country))}" case _ => s"Error" } ) From cd48f63c1443fcf1725108b1e3a71a32887f0fa7 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Mon, 8 Apr 2024 15:57:09 +0300 Subject: [PATCH 09/33] Add validated newtypes usage to generated metadata --- .../codegen/internals/CodegenImpl.scala | 14 ++++++++------ .../codegen/internals/CodegenRecord.scala | 8 ++++++-- .../internals/GeneratedNamespace.scala | 6 ++++++ .../codegen/internals/SmithyToIR.scala | 19 +++++++++++++++---- 4 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala index bda3526c2..2c9cfce1a 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala @@ -125,14 +125,16 @@ private[codegen] object CodegenImpl { self => // Retrieving metadata that indicates what has already been generated by Smithy4s // in upstream jars. - val alreadyGenerated: Set[String] = { + val alreadyGenerated: Set[GeneratedNamespace] = { val allGenerated = CodegenRecord .recordsFromModel(model) .flatMap { r => - r.namespaces + r.namespaces.map( + GeneratedNamespace(_, r.validatedNewtypes.getOrElse(false)) + ) } for (g <- allGenerated) { - if (allGenerated.count(_ == g) > 1) + if (allGenerated.count(_.namespace == g.namespace) > 1) throw new IllegalStateException( s"Multiple artifact manifests indicate containing generated code for namespace $g" ) @@ -147,18 +149,18 @@ private[codegen] object CodegenImpl { self => namespaces .filter(allowedNamespaces) .filterNot(excluded) - .filterNot(alreadyGenerated) + .filterNot(alreadyGenerated.map(_.namespace)) case None => namespaces .filterNot(_.startsWith("aws.")) .filterNot(_.startsWith("smithy.")) .filterNot(ns => reserved.exists(ns.startsWith)) .filterNot(excluded) - .filterNot(alreadyGenerated) + .filterNot(alreadyGenerated.map(_.namespace)) } filteredNamespaces.toList - .map { ns => SmithyToIR(model, ns) } + .map { ns => SmithyToIR(model, ns, alreadyGenerated) } .flatMap { cu => val amended = CollisionAvoidance(cu) Renderer(amended) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala b/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala index da20fb605..435bbf530 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala @@ -20,9 +20,11 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import scala.jdk.CollectionConverters._ +import scala.jdk.OptionConverters._ private[internals] final case class CodegenRecord( - namespaces: List[String] + namespaces: List[String], + validatedNewtypes: Option[Boolean] ) private[internals] object CodegenRecord { @@ -41,11 +43,13 @@ private[internals] object CodegenRecord { def fromNode(node: Node): CodegenRecord = { val obj = node.expectObjectNode() val arrayNode = obj.expectArrayMember("namespaces") + val validatedNewtypes = + obj.getBooleanMember("validatedNewtypes").toScala.map(_.getValue()) val namespaces = arrayNode .getElements() .asScala .map(_.expectStringNode().getValue()) .toList - CodegenRecord(namespaces) + CodegenRecord(namespaces, validatedNewtypes) } } diff --git a/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala b/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala new file mode 100644 index 000000000..adad231bd --- /dev/null +++ b/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala @@ -0,0 +1,6 @@ +package smithy4s.codegen.internals + +private[internals] final case class GeneratedNamespace( + namespace: String, + validatedNewtypes: Boolean +) diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index 178000e4a..30a0bd325 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -47,8 +47,12 @@ import Type.Alias private[codegen] object SmithyToIR { - def apply(model: Model, namespace: String): CompilationUnit = { - val smithyToIR = new SmithyToIR(model, namespace) + def apply( + model: Model, + namespace: String, + generatedNamespaces: Set[GeneratedNamespace] + ): CompilationUnit = { + val smithyToIR = new SmithyToIR(model, namespace, generatedNamespaces) PostProcessor( CompilationUnit(namespace, smithyToIR.allDecls, smithyToIR.rendererConfig) ) @@ -65,7 +69,11 @@ private[codegen] object SmithyToIR { } -private[codegen] class SmithyToIR(model: Model, namespace: String) { +private[codegen] class SmithyToIR( + model: Model, + namespace: String, + generatedNamespaces: Set[GeneratedNamespace] +) { val finder = PathFinder.create(model) @@ -78,6 +86,9 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { val rendererConfig = Renderer.Config.load(model.getMetadata().asScala.toMap) + val namespaceToValidatedNewtypes: Map[String, Boolean] = + generatedNamespaces.map(ns => ns.namespace -> ns.validatedNewtypes).toMap + private sealed trait DefaultRenderMode private object DefaultRenderMode { case object Full extends DefaultRenderMode @@ -527,7 +538,7 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { ) private def canBeRenderedAsValidatedNewtype(shapeId: ShapeId): Boolean = { - shapeId.getNamespace() != smithyNamespace + namespaceToValidatedNewtypes.getOrElse(shapeId.getNamespace(), true) } private sealed trait ExternalTypeInfo From 043c8591b3dd8ff2bfe305108610712330868caa Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Mon, 8 Apr 2024 16:53:33 +0300 Subject: [PATCH 10/33] Fix newline --- sampleSpecs/validated-newtype.smithy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sampleSpecs/validated-newtype.smithy b/sampleSpecs/validated-newtype.smithy index b6fc3a239..ca5a1c56d 100644 --- a/sampleSpecs/validated-newtype.smithy +++ b/sampleSpecs/validated-newtype.smithy @@ -19,4 +19,4 @@ string UnwrappedValidatedString structure UnwrappedValidatedFoo { name: UnwrappedValidatedString -} \ No newline at end of file +} From be7b3e120f3b46b87bc6492e7a94d05185284e26 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 9 Apr 2024 12:30:50 +0300 Subject: [PATCH 11/33] Fix missing license header --- .../codegen/internals/GeneratedNamespace.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala b/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala index adad231bd..15d46c109 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/GeneratedNamespace.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021-2024 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.internals private[internals] final case class GeneratedNamespace( From 7ec86d6b026bffbfab398e7220f24b5defebf84e Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 10 Apr 2024 16:30:54 +0300 Subject: [PATCH 12/33] Add ValidatedNewtypesTransformer and switch to meta trait --- ...dString.scala => NonValidatedString.scala} | 6 +- .../example/UnwrappedValidatedFoo.scala | 21 --- .../smithy4s/example/ValidatedString.scala | 12 +- .../generated/smithy4s/example/package.scala | 2 +- .../src/smithy4s/ValidatedNewtypesSpec.scala | 94 +++++++++++++ ....amazon.smithy.build.ProjectionTransformer | 1 + .../codegen/internals/CodegenImpl.scala | 24 ++-- .../src/smithy4s/codegen/internals/IR.scala | 1 + .../smithy4s/codegen/internals/Renderer.scala | 24 +--- .../codegen/internals/SmithyToIR.scala | 42 ++---- .../ValidatedNewtypesTransformer.scala | 128 ++++++++++++++++++ ...re.amazon.smithy.model.traits.TraitService | 1 + .../META-INF/smithy/smithy4s.meta.smithy | 9 ++ .../meta/ValidateNewtypeConstraintsTrait.java | 46 +++++++ sampleSpecs/validated-newtype.smithy | 15 +- 15 files changed, 329 insertions(+), 97 deletions(-) rename modules/bootstrapped/src/generated/smithy4s/example/{UnwrappedValidatedString.scala => NonValidatedString.scala} (63%) delete mode 100644 modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala create mode 100644 modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala create mode 100644 modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala create mode 100644 modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/NonValidatedString.scala similarity index 63% rename from modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala rename to modules/bootstrapped/src/generated/smithy4s/example/NonValidatedString.scala index dc84551bf..bde61f006 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/NonValidatedString.scala @@ -7,9 +7,9 @@ import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object UnwrappedValidatedString extends Newtype[String] { - val id: ShapeId = ShapeId("smithy4s.example", "UnwrappedValidatedString") +object NonValidatedString extends Newtype[String] { + val id: ShapeId = ShapeId("smithy4s.example", "NonValidatedString") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) - implicit val schema: Schema[UnwrappedValidatedString] = bijection(underlyingSchema, asBijection) + implicit val schema: Schema[NonValidatedString] = bijection(underlyingSchema, asBijection) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala b/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala deleted file mode 100644 index 6d2254e00..000000000 --- a/modules/bootstrapped/src/generated/smithy4s/example/UnwrappedValidatedFoo.scala +++ /dev/null @@ -1,21 +0,0 @@ -package smithy4s.example - -import smithy4s.Hints -import smithy4s.Schema -import smithy4s.ShapeId -import smithy4s.ShapeTag -import smithy4s.schema.Schema.struct - -final case class UnwrappedValidatedFoo(name: Option[String] = None) - -object UnwrappedValidatedFoo extends ShapeTag.Companion[UnwrappedValidatedFoo] { - val id: ShapeId = ShapeId("smithy4s.example", "UnwrappedValidatedFoo") - - val hints: Hints = Hints.empty - - implicit val schema: Schema[UnwrappedValidatedFoo] = struct( - UnwrappedValidatedString.underlyingSchema.optional[UnwrappedValidatedFoo]("name", _.name), - ){ - UnwrappedValidatedFoo.apply - }.withId(id).addHints(hints) -} diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala index 18c331f8e..e40ca23f6 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala @@ -1,15 +1,21 @@ package smithy4s.example import smithy4s.Hints -import smithy4s.Newtype +import smithy4s.NewtypeValidated import smithy4s.Schema import smithy4s.ShapeId import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string -object ValidatedString extends Newtype[String] { +object ValidatedString extends NewtypeValidated[String] { val id: ShapeId = ShapeId("smithy4s.example", "ValidatedString") val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) - implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijection) + implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijectionUnsafe) + val validators: List[String => Either[String, String]] = List( + a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) + ) + @inline def apply(a: String): Either[String, ValidatedString] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/package.scala b/modules/bootstrapped/src/generated/smithy4s/example/package.scala index 21864caef..3425b9bba 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/package.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/package.scala @@ -41,7 +41,6 @@ package object example { type Weather[F[_]] = smithy4s.kinds.FunctorAlgebra[WeatherGen, F] val Weather = WeatherGen - type UnwrappedValidatedString = smithy4s.example.UnwrappedValidatedString.Type type PublishersList = smithy4s.example.PublishersList.Type type TestString = smithy4s.example.TestString.Type type ArbitraryData = smithy4s.example.ArbitraryData.Type @@ -93,6 +92,7 @@ package object example { type TestStructurePattern = smithy4s.example.TestStructurePattern.Type type UnicodeRegexString = smithy4s.example.UnicodeRegexString.Type type UVIndex = smithy4s.example.UVIndex.Type + type NonValidatedString = smithy4s.example.NonValidatedString.Type @deprecated(message = "N/A", since = "N/A") type Strings = smithy4s.example.Strings.Type type NonEmptyCandies = smithy4s.example.NonEmptyCandies.Type diff --git a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala new file mode 100644 index 000000000..ce41dc1a8 --- /dev/null +++ b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala @@ -0,0 +1,94 @@ +/* + * Copyright 2021-2023 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 + +import smithy4s.schema.Schema.bijection +import smithy4s.schema.Schema.string + +class ValidatedNewtypesSpec() extends munit.FunSuite { + val id1 = "id-1" + val id2 = "id-2" + + test("Validated newtypes are consistent") { + expect.same(AccountId.unsafeApply(id1).value, id1) + expect.different( + AccountId.unsafeApply(id1).value, + AccountId.unsafeApply(id2).value + ) + expect.different( + implicitly[ShapeTag[AccountId]], + implicitly[ShapeTag[DeviceId]] + ) + expect.same(AccountId.unapply(AccountId.unsafeApply(id1)), Some(id1)) + } + + test("Newtypes have well defined unapply") { + val aid = AccountId.unsafeApply(id1) + aid match { + case AccountId(id) => expect(id == id1) + } + } + + type DeviceId = DeviceId.Type + object DeviceId extends NewtypeValidated[String] { + + val id: ShapeId = ShapeId("foo", "DeviceId") + val hints: Hints = Hints.empty + + val underlyingSchema: Schema[String] = string + .withId(id) + .addHints(hints) + .validated(smithy.api.Length(min = Some(1L), max = None)) + + implicit val schema: Schema[DeviceId] = + bijection(underlyingSchema, asBijectionUnsafe) + + val validators: List[String => Either[String, String]] = List(a => + validateInternal(smithy.api.Length(min = Some(1L), max = None))(a) + ) + @inline def apply(a: String): Either[String, DeviceId] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) + + } + + type AccountId = AccountId.Type + + object AccountId extends NewtypeValidated[String] { + def id: smithy4s.ShapeId = ShapeId("foo", "AccountId") + val hints: Hints = Hints.empty + + val underlyingSchema: Schema[String] = string + .withId(id) + .addHints(hints) + .validated(smithy.api.Length(min = Some(1L), max = None)) + .validated(smithy.api.Pattern("[a-zA-Z0-9]+")) + + implicit val schema: Schema[AccountId] = + bijection(underlyingSchema, asBijectionUnsafe) + + val validators: List[String => Either[String, String]] = List( + a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), + a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) + ) + @inline def apply(a: String): Either[String, AccountId] = validators + .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) + .map(unsafeApply) + + } + +} diff --git a/modules/codegen/resources/META-INF/services/software.amazon.smithy.build.ProjectionTransformer b/modules/codegen/resources/META-INF/services/software.amazon.smithy.build.ProjectionTransformer index 8ab5341eb..73d22aeb2 100644 --- a/modules/codegen/resources/META-INF/services/software.amazon.smithy.build.ProjectionTransformer +++ b/modules/codegen/resources/META-INF/services/software.amazon.smithy.build.ProjectionTransformer @@ -1,3 +1,4 @@ smithy4s.codegen.transformers.AwsStandardTypesTransformer smithy4s.codegen.transformers.AwsConstraintsRemover smithy4s.codegen.transformers.OpenEnumTransformer +smithy4s.codegen.transformers.ValidatedNewtypesTransformer diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala index 2c9cfce1a..cd77a34ff 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala @@ -34,7 +34,7 @@ private[codegen] object CodegenImpl { self => args.specs.map(_.toIO).toSet, args.dependencies, args.repositories, - withAwsTypeTransformer(args.transformers), + withMandatoryTransformers(args.transformers), args.discoverModels, args.localJars ) @@ -125,16 +125,14 @@ private[codegen] object CodegenImpl { self => // Retrieving metadata that indicates what has already been generated by Smithy4s // in upstream jars. - val alreadyGenerated: Set[GeneratedNamespace] = { + val alreadyGenerated: Set[String] = { val allGenerated = CodegenRecord .recordsFromModel(model) .flatMap { r => - r.namespaces.map( - GeneratedNamespace(_, r.validatedNewtypes.getOrElse(false)) - ) + r.namespaces } for (g <- allGenerated) { - if (allGenerated.count(_.namespace == g.namespace) > 1) + if (allGenerated.count(_ == g) > 1) throw new IllegalStateException( s"Multiple artifact manifests indicate containing generated code for namespace $g" ) @@ -149,18 +147,18 @@ private[codegen] object CodegenImpl { self => namespaces .filter(allowedNamespaces) .filterNot(excluded) - .filterNot(alreadyGenerated.map(_.namespace)) + .filterNot(alreadyGenerated) case None => namespaces .filterNot(_.startsWith("aws.")) .filterNot(_.startsWith("smithy.")) .filterNot(ns => reserved.exists(ns.startsWith)) .filterNot(excluded) - .filterNot(alreadyGenerated.map(_.namespace)) + .filterNot(alreadyGenerated) } filteredNamespaces.toList - .map { ns => SmithyToIR(model, ns, alreadyGenerated) } + .map { ns => SmithyToIR(model, ns) } .flatMap { cu => val amended = CollisionAvoidance(cu) Renderer(amended) @@ -177,7 +175,7 @@ private[codegen] object CodegenImpl { self => args.specs.map(_.toIO).toSet, args.dependencies, args.repositories, - withAwsTypeTransformer(args.transformers), + withMandatoryTransformers(args.transformers), discoverModels = false, args.localJars ) @@ -185,7 +183,9 @@ private[codegen] object CodegenImpl { self => Node.prettyPrintJson(ModelSerializer.builder().build.serialize(model)) } - private def withAwsTypeTransformer(transformers: List[String]): List[String] = - transformers :+ AwsConstraintsRemover.name :+ AwsStandardTypesTransformer.name :+ OpenEnumTransformer.name + private def withMandatoryTransformers( + transformers: List[String] + ): List[String] = + transformers :+ AwsConstraintsRemover.name :+ AwsStandardTypesTransformer.name :+ OpenEnumTransformer.name :+ ValidatedNewtypesTransformer.name } diff --git a/modules/codegen/src/smithy4s/codegen/internals/IR.scala b/modules/codegen/src/smithy4s/codegen/internals/IR.scala index bdb7a4f38..01e2b665d 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/IR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/IR.scala @@ -342,6 +342,7 @@ private[internals] object Hint { extends Hint case object GenerateServiceProduct extends Hint case object GenerateOptics extends Hint + case object ValidateNewtypeConstraints extends Hint implicit val eq: Eq[Hint] = Eq.fromUniversalEquals } diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 780fce13b..d32ed1035 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -41,8 +41,7 @@ private[internals] object Renderer { case class Config( errorsAsScala3Unions: Boolean, wildcardArgument: String, - renderOptics: Boolean, - renderValidatedNewtypes: Boolean + renderOptics: Boolean ) object Config { def load(metadata: Map[String, Node]): Renderer.Config = { @@ -63,12 +62,6 @@ private[internals] object Renderer { .map(_.getValue()) .getOrElse(false) - val renderValidatedNewtypes = metadata - .get("smithy4sRenderValidatedNewtypes") - .flatMap(_.asBooleanNode().asScala) - .map(_.getValue()) - .getOrElse(false) - if (wildcardArgument != "?" && wildcardArgument != "_") { throw new IllegalArgumentException( s"`smithy4sWildcardArgument` possible values are: `?` or `_`. found `$wildcardArgument`." @@ -78,8 +71,7 @@ private[internals] object Renderer { Renderer.Config( errorsAsScala3Unions = errorsAsScala3Unions, wildcardArgument = wildcardArgument, - renderOptics = renderOptics, - renderValidatedNewtypes = renderValidatedNewtypes + renderOptics = renderOptics ) } } @@ -194,11 +186,7 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => case ta @ TypeAlias(shapeId, _, tpe, _, recursive, hints) => renderNewtype(shapeId, ta.nameRef, tpe, recursive, hints) case vta @ ValidatedTypeAlias(shapeId, _, tpe, recursive, hints) => - if (compilationUnit.rendererConfig.renderValidatedNewtypes) { - renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) - } else { - renderNewtype(shapeId, vta.nameRef, tpe, recursive, hints) - } + renderValidatedNewtype(shapeId, vta.nameRef, tpe, recursive, hints) case enumeration @ Enumeration(shapeId, _, tag, values, hints) => renderEnum(shapeId, enumeration.nameRef, tag, values, hints) } @@ -1587,12 +1575,8 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => val (wroteCollection, text) = target.run(topLevel) if (wroteCollection && !topLevel) false -> text - else if (compilationUnit.rendererConfig.renderValidatedNewtypes) { + else false -> line"${ref.show}.unsafeApply($text)" - - } else { - false -> line"${ref.show}($text)" - } }) case AltTN(ref, altName, AltValueTN.TypeAltTN(alt)) => diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index f27496395..fff10d634 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -29,6 +29,7 @@ import smithy4s.meta.NoStackTraceTrait import smithy4s.meta.PackedInputsTrait import smithy4s.meta.RefinementTrait import smithy4s.meta.TypeclassTrait +import smithy4s.meta.ValidateNewtypeConstraintsTrait import smithy4s.meta.VectorTrait import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.Model @@ -46,10 +47,9 @@ private[codegen] object SmithyToIR { def apply( model: Model, - namespace: String, - generatedNamespaces: Set[GeneratedNamespace] + namespace: String ): CompilationUnit = { - val smithyToIR = new SmithyToIR(model, namespace, generatedNamespaces) + val smithyToIR = new SmithyToIR(model, namespace) PostProcessor( CompilationUnit(namespace, smithyToIR.allDecls, smithyToIR.rendererConfig) ) @@ -68,8 +68,7 @@ private[codegen] object SmithyToIR { private[codegen] class SmithyToIR( model: Model, - namespace: String, - generatedNamespaces: Set[GeneratedNamespace] + namespace: String ) { val finder = PathFinder.create(model) @@ -83,9 +82,6 @@ private[codegen] class SmithyToIR( val rendererConfig = Renderer.Config.load(model.getMetadata().asScala.toMap) - val namespaceToValidatedNewtypes: Map[String, Boolean] = - generatedNamespaces.map(ns => ns.namespace -> ns.validatedNewtypes).toMap - private sealed trait DefaultRenderMode private object DefaultRenderMode { case object Full extends DefaultRenderMode @@ -534,10 +530,6 @@ private[codegen] class SmithyToIR( shapeId.getName() ) - private def canBeRenderedAsValidatedNewtype(shapeId: ShapeId): Boolean = { - namespaceToValidatedNewtypes.getOrElse(shapeId.getNamespace(), true) - } - private sealed trait ExternalTypeInfo private object ExternalTypeInfo { case class RefinementInfo(trt: RefinementTrait) extends ExternalTypeInfo @@ -613,13 +605,6 @@ private[codegen] class SmithyToIR( shape.hasTrait(classOf[smithy4s.meta.UnwrapTrait]) } - private def hasConstraints(shape: Shape): Boolean = { - shape.getAllTraits().asScala.values.exists { - case ConstraintTrait(_) => true - case _ => false - } - } - def primitive( shape: Shape, primitiveId: String, @@ -631,27 +616,26 @@ private[codegen] class SmithyToIR( shape.getId() != ShapeId.from(primitiveId) && !isUnboxedPrimitive(shape.getId()) ) { - val isUnwrapped = isUnwrappedShape(shape) - val canBeValidated = canBeRenderedAsValidatedNewtype(shape.getId()) - if (isUnwrapped || !(hasConstraints(shape) && canBeValidated)) { + val shouldValidate = + shape.hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + if (shouldValidate) { Type - .Alias( + .ValidatedAlias( shape.getId().getNamespace(), shape.getId().getName(), - externalOrBase, - isUnwrappedShape(shape) + externalOrBase ) .some } else { Type - .ValidatedAlias( + .Alias( shape.getId().getNamespace(), shape.getId().getName(), - externalOrBase + externalOrBase, + isUnwrappedShape(shape) ) .some } - } else externalOrBase.some } @@ -968,6 +952,8 @@ private[codegen] class SmithyToIR( Hint.GenerateServiceProduct case _: GenerateOpticsTrait => Hint.GenerateOptics + case _: ValidateNewtypeConstraintsTrait => + Hint.ValidateNewtypeConstraints case t if t.toShapeId() == ShapeId.fromParts("smithy.api", "trait") => Hint.Trait case ConstraintTrait(tr) => Hint.Constraint(toTypeRef(tr), unfoldTrait(tr)) diff --git a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala new file mode 100644 index 000000000..89b7c4aea --- /dev/null +++ b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala @@ -0,0 +1,128 @@ +/* + * Copyright 2021-2024 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 smithy4s.meta.UnwrapTrait +import smithy4s.meta.ValidateNewtypeConstraintsTrait +import software.amazon.smithy.build.ProjectionTransformer +import software.amazon.smithy.build.TransformContext +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.AbstractShapeBuilder +import software.amazon.smithy.model.shapes.NumberShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.RangeTrait + +import scala.jdk.CollectionConverters._ +import scala.jdk.OptionConverters._ + +class ValidatedNewtypesTransformer extends ProjectionTransformer { + + override def getName(): String = ValidatedNewtypesTransformer.name + + override def transform(context: TransformContext): Model = { + val transformer = context.getTransformer() + + val model = context.getModel() + val agn = alreadyGeneratedNamespaces(model) + + val supportsValidatedNewtypes = + model + .getMetadataProperty(ValidatedNewtypesTransformer.METADATA_KEY) + .toScala + .flatMap(_.asBooleanNode().toScala.map(_.getValue())) + .getOrElse(false) + + transformer.mapShapes( + model, + s => processShape(s, agn.getOrElse(_, supportsValidatedNewtypes)) + ) + } + + private def processShape(shape: Shape, lookup: String => Boolean) = + if (lookup(shape.getId().getNamespace())) + shape match { + case ValidatedNewtypesTransformer.SupportedShape(s) => + addTrait(Shape.shapeToBuilder(s): AbstractShapeBuilder[_, _]) + case _ => shape + } + else + shape + + private def addTrait[S <: Shape, B <: AbstractShapeBuilder[B, S]]( + builder: AbstractShapeBuilder[B, S] + ): S = { + builder.addTrait(new ValidateNewtypeConstraintsTrait()) + builder.build() + } + + private def alreadyGeneratedNamespaces(model: Model): Map[String, Boolean] = { + def fromNode(node: Node): List[(String, Boolean)] = { + val obj = node.expectObjectNode() + val arrayNode = obj.expectArrayMember("namespaces") + val validatedNewtypes = + obj + .getBooleanMember("validatedNewtypes") + .toScala + .map(_.getValue()) + .getOrElse(false) + + arrayNode + .getElements() + .asScala + .map(_.expectStringNode().getValue()) + .map(ns => (ns, validatedNewtypes)) + .toList + } + + model + .getMetadataProperty(ValidatedNewtypesTransformer.SMITHY4S_GENERATED) + .toScala + .map( + _.expectArrayNode().getElements().asScala.toList.flatMap(fromNode).toMap + ) + .getOrElse(Map.empty) + } + +} + +object ValidatedNewtypesTransformer { + val name = "ValidatedNewtypesTransformer" + + private val METADATA_KEY = "smithy4sRenderValidatedNewtypes" + private val SMITHY4S_GENERATED = "smithy4sGenerated" + + object SupportedShape { + def unapply(shape: Shape): Option[Shape] = shape match { + case _ if shape.hasTrait(classOf[UnwrapTrait]) => None + case _ if shape.hasTrait(classOf[ValidateNewtypeConstraintsTrait]) => None + case s: StringShape if hasStringConstraints(s) => Some(s) + case n: NumberShape if hasNumberConstraints(n) => Some(n) + case _ => None + } + + private def hasStringConstraints(shape: Shape): Boolean = + shape.getTrait(classOf[LengthTrait]).isPresent || + shape.getTrait(classOf[PatternTrait]).isPresent + + private def hasNumberConstraints(shape: Shape): Boolean = + shape.getTrait(classOf[RangeTrait]).isPresent + } +} diff --git a/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 8c2ade972..918d36b89 100644 --- a/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -10,3 +10,4 @@ smithy4s.meta.AdtTrait$Provider smithy4s.meta.TypeclassTrait$Provider smithy4s.meta.GenerateServiceProductTrait$Provider smithy4s.meta.GenerateOpticsTrait$Provider +smithy4s.meta.ValidateNewtypeConstraintsTrait$Provider diff --git a/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy b/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy index c1e498c85..ec659caa5 100644 --- a/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy +++ b/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy @@ -160,3 +160,12 @@ structure generateOptics {} /// via extending scala.util.control.NoStackTrace instead of Throwable. @trait(selector: "structure :is([trait|error])") structure noStackTrace {} + +@trait(selector: """ + :is( + number[trait|range], + string[trait|pattern], + string[trait|length] + )""", + conflicts: ["smithy4s.meta#unwrap"]) +structure validateNewtypeConstraints {} diff --git a/modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java b/modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java new file mode 100644 index 000000000..07faca82f --- /dev/null +++ b/modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021-2024 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.meta; + +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AnnotationTrait; +import software.amazon.smithy.model.traits.AbstractTrait; + +public class ValidateNewtypeConstraintsTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("smithy4s.meta#validateNewtypeConstraints"); + + public ValidateNewtypeConstraintsTrait(ObjectNode node) { + super(ID, node); + } + + public ValidateNewtypeConstraintsTrait() { + super(ID, Node.objectNode()); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public ValidateNewtypeConstraintsTrait createTrait(ShapeId target, Node node) { + return new ValidateNewtypeConstraintsTrait(node.expectObjectNode()); + } + } +} diff --git a/sampleSpecs/validated-newtype.smithy b/sampleSpecs/validated-newtype.smithy index ca5a1c56d..c4d16bc49 100644 --- a/sampleSpecs/validated-newtype.smithy +++ b/sampleSpecs/validated-newtype.smithy @@ -2,21 +2,18 @@ $version: "2" namespace smithy4s.example -use smithy4s.meta#unwrap +use smithy4s.meta#validateNewtypeConstraints @length(min: 1) @pattern("[a-zA-Z0-9]+") +@validateNewtypeConstraints string ValidatedString -structure ValidatedFoo { - name: ValidatedString -} - @length(min: 1) @pattern("[a-zA-Z0-9]+") -@unwrap -string UnwrappedValidatedString +string NonValidatedString -structure UnwrappedValidatedFoo { - name: UnwrappedValidatedString +structure ValidatedFoo { + name: ValidatedString } + From e6db5e1a80556f06e6d4f2e063b13181e347e240 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 10 Apr 2024 16:39:20 +0300 Subject: [PATCH 13/33] Add default value to bootstrapped sample --- .../src/generated/smithy4s/example/ValidatedFoo.scala | 4 ++-- sampleSpecs/validated-newtype.smithy | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala index d8a57727a..ace458e97 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala @@ -6,7 +6,7 @@ import smithy4s.ShapeId import smithy4s.ShapeTag import smithy4s.schema.Schema.struct -final case class ValidatedFoo(name: Option[ValidatedString] = None) +final case class ValidatedFoo(name: ValidatedString = smithy4s.example.ValidatedString.unsafeApply("abc")) object ValidatedFoo extends ShapeTag.Companion[ValidatedFoo] { val id: ShapeId = ShapeId("smithy4s.example", "ValidatedFoo") @@ -14,7 +14,7 @@ object ValidatedFoo extends ShapeTag.Companion[ValidatedFoo] { val hints: Hints = Hints.empty implicit val schema: Schema[ValidatedFoo] = struct( - ValidatedString.schema.optional[ValidatedFoo]("name", _.name), + ValidatedString.schema.field[ValidatedFoo]("name", _.name).addHints(smithy.api.Default(smithy4s.Document.fromString("abc"))), ){ ValidatedFoo.apply }.withId(id).addHints(hints) diff --git a/sampleSpecs/validated-newtype.smithy b/sampleSpecs/validated-newtype.smithy index c4d16bc49..9d2466661 100644 --- a/sampleSpecs/validated-newtype.smithy +++ b/sampleSpecs/validated-newtype.smithy @@ -14,6 +14,6 @@ string ValidatedString string NonValidatedString structure ValidatedFoo { - name: ValidatedString + name: ValidatedString = "abc" } From 4f7b3133c26a619d917837646f049f04ffe84606 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 10 Apr 2024 17:37:14 +0300 Subject: [PATCH 14/33] Fix renderer spec --- .../codegen/internals/RendererSpec.scala | 74 +------------------ 1 file changed, 4 insertions(+), 70 deletions(-) diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index d25d919fd..f244bc825 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -485,47 +485,16 @@ final class RendererSpec extends munit.FunSuite { ) } - test("newtype with constraint and smithy4sRenderValidatedNewtypes=false") { + test("newtype with constraint and validateNewtypeConstraints annotation") { val smithy = """ |$version: "2" | - |metadata smithy4sRenderValidatedNewtypes=false + |namespace smithy4s.example | - |namespace smithy4s - | - |@length(min: 1, max: 10) - |string MyValidatedString - | - |structure ValidatedFoo { - | mvs: MyValidatedString - |} - |""".stripMargin - - val contents = generateScalaCode(smithy).values - - assert( - contents.exists( - _.contains("object MyValidatedString extends Newtype[String]") - ) - ) - assert( - contents.exists( - _.contains( - "final case class ValidatedFoo(mvs: Option[MyValidatedString] = None)" - ) - ) - ) - } - - test("newtype with constraint and smithy4sRenderValidatedNewtypes=true") { - val smithy = """ - |$version: "2" - | - |metadata smithy4sRenderValidatedNewtypes=true - | - |namespace smithy4s + |use smithy4s.meta#validateNewtypeConstraints | |@length(min: 1, max: 10) + |@validateNewtypeConstraints |string MyValidatedString | |structure ValidatedFoo { @@ -548,39 +517,4 @@ final class RendererSpec extends munit.FunSuite { ) ) } - - test( - "unwrapped newtype with constraint and smithy4sRenderValidatedNewtypes=true" - ) { - val smithy = """ - |$version: "2" - | - |metadata smithy4sRenderValidatedNewtypes=true - | - |namespace smithy4s - | - |use smithy4s.meta#unwrap - | - |@unwrap - |@length(min: 1, max: 10) - |string MyValidatedString - | - |structure ValidatedFoo { - | mvs: MyValidatedString - |} - |""".stripMargin - - val contents = generateScalaCode(smithy).values - - assert( - contents.exists( - _.contains("object MyValidatedString extends Newtype[String]") - ) - ) - assert( - contents.exists( - _.contains("final case class ValidatedFoo(mvs: Option[String] = None)") - ) - ) - } } From 54762fe9549dfffd66c24522858452a3409556cb Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 10 Apr 2024 17:37:25 +0300 Subject: [PATCH 15/33] Add transformer spec --- .../ValidatedNewtypesTransformerSpec.scala | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala new file mode 100644 index 000000000..ee699b608 --- /dev/null +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala @@ -0,0 +1,163 @@ +/* + * Copyright 2021-2024 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 software.amazon.smithy.build.TransformContext +import smithy4s.meta.ValidateNewtypeConstraintsTrait +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.Model + +class ValidatedNewtypesTransformerSpec extends munit.FunSuite { + + import smithy4s.codegen.internals.TestUtils._ + + test("Leaves shape unchanged when @validateNewtypeConstraints is already present") { + assertPresent("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = false + | + |namespace smithy4s.transformer.test + | + |use smithy4s.meta#validateNewtypeConstraints + | + |@length(min: 1, max: 10) + |@validateNewtypeConstraints + |string ValidatedString + |""".stripMargin + } + } + + + test("Adds @validateNewtypeConstraints on string alias with constraint") { + assertPresent("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = true + | + |namespace smithy4s.transformer.test + | + |@length(min: 1, max: 10) + |string ValidatedString + |""".stripMargin + } + } + + test("Adds @validateNewtypeConstraints on number alias with constraint") { + assertPresent("smithy4s.transformer.test#ValidatedNumber"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = true + | + |namespace smithy4s.transformer.test + | + |@range(min: 1, max: 10) + |integer ValidatedNumber + |""".stripMargin + } + } + + test("Does not add @validateNewtypeConstraints on unwrapped string alias") { + assertMissing("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = true + | + |namespace smithy4s.transformer.test + | + |use smithy4s.meta#unwrap + | + |@length(min: 1, max: 10) + |@unwrap + |string ValidatedString + |""".stripMargin + } + } + + test("Does not add @validateNewtypeConstraints on type when smithy4sRenderValidatedNewtypes=false") { + assertMissing("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = false + | + |namespace smithy4s.transformer.test + | + |@length(min: 1, max: 10) + |string ValidatedString + |""".stripMargin + } + } + + test("Does not add @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=false") { + assertMissing("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = true + |metadata smithy4sGenerated = [{smithy4sVersion: "dev-SNAPSHOT", namespaces: ["smithy4s.transformer.test"], validatedNewtypes: false}] + | + |namespace smithy4s.transformer.test + | + |@length(min: 1, max: 10) + |string ValidatedString + |""".stripMargin + } + } + + test("Adds @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=true") { + assertPresent("smithy4s.transformer.test#ValidatedString"){ + """|$version: "2.0" + | + |metadata smithy4sRenderValidatedNewtypes = true + |metadata smithy4sGenerated = [{smithy4sVersion: "dev-SNAPSHOT", namespaces: ["smithy4s.transformer.test"], validatedNewtypes: true}] + | + |namespace smithy4s.transformer.test + | + |@length(min: 1, max: 10) + |string ValidatedString + |""".stripMargin + } + } + + private def assertPresent(shapeId: String)(inputModel: String*)(implicit + loc: munit.Location + ): Unit = { + val containsTrait = loadAndTransformModel(inputModel: _*) + .expectShape(ShapeId.from(shapeId)) + .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + + assert(containsTrait, "Expected validateNewtypeConstraints trait to be present") + } + + private def assertMissing(shapeId: String)(inputModel: String*)(implicit + loc: munit.Location + ): Unit = { + val containsTrait = loadAndTransformModel(inputModel: _*) + .expectShape(ShapeId.from(shapeId)) + .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + + assert(!containsTrait, "Expected validateNewtypeConstraints trait to be missing") + } + + def loadAndTransformModel(inputModel: String*): Model = + new ValidatedNewtypesTransformer() + .transform( + TransformContext + .builder() + .model(loadModel(inputModel: _*)) + .build() + ) + +} From c00ed39d42315866898c8f05354dac9e1ba7b3e7 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 10 Apr 2024 17:52:06 +0300 Subject: [PATCH 16/33] Fix formatting --- .../ValidatedNewtypesTransformerSpec.scala | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala index ee699b608..1f6076cd4 100644 --- a/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala @@ -24,8 +24,10 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { import smithy4s.codegen.internals.TestUtils._ - test("Leaves shape unchanged when @validateNewtypeConstraints is already present") { - assertPresent("smithy4s.transformer.test#ValidatedString"){ + test( + "Leaves shape unchanged when @validateNewtypeConstraints is already present" + ) { + assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = false @@ -41,9 +43,8 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Adds @validateNewtypeConstraints on string alias with constraint") { - assertPresent("smithy4s.transformer.test#ValidatedString"){ + assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = true @@ -57,7 +58,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } test("Adds @validateNewtypeConstraints on number alias with constraint") { - assertPresent("smithy4s.transformer.test#ValidatedNumber"){ + assertPresent("smithy4s.transformer.test#ValidatedNumber") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = true @@ -71,7 +72,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } test("Does not add @validateNewtypeConstraints on unwrapped string alias") { - assertMissing("smithy4s.transformer.test#ValidatedString"){ + assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = true @@ -87,8 +88,10 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Does not add @validateNewtypeConstraints on type when smithy4sRenderValidatedNewtypes=false") { - assertMissing("smithy4s.transformer.test#ValidatedString"){ + test( + "Does not add @validateNewtypeConstraints on type when smithy4sRenderValidatedNewtypes=false" + ) { + assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = false @@ -101,8 +104,10 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Does not add @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=false") { - assertMissing("smithy4s.transformer.test#ValidatedString"){ + test( + "Does not add @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=false" + ) { + assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = true @@ -116,8 +121,10 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Adds @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=true") { - assertPresent("smithy4s.transformer.test#ValidatedString"){ + test( + "Adds @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=true" + ) { + assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | |metadata smithy4sRenderValidatedNewtypes = true @@ -138,7 +145,10 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { .expectShape(ShapeId.from(shapeId)) .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) - assert(containsTrait, "Expected validateNewtypeConstraints trait to be present") + assert( + containsTrait, + "Expected validateNewtypeConstraints trait to be present" + ) } private def assertMissing(shapeId: String)(inputModel: String*)(implicit @@ -148,16 +158,19 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { .expectShape(ShapeId.from(shapeId)) .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) - assert(!containsTrait, "Expected validateNewtypeConstraints trait to be missing") + assert( + !containsTrait, + "Expected validateNewtypeConstraints trait to be missing" + ) } def loadAndTransformModel(inputModel: String*): Model = new ValidatedNewtypesTransformer() - .transform( - TransformContext - .builder() - .model(loadModel(inputModel: _*)) - .build() - ) - + .transform( + TransformContext + .builder() + .model(loadModel(inputModel: _*)) + .build() + ) + } From 5ff681c62601d62db27b6e084bfd6a0ccccd44e4 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 13:10:27 +0300 Subject: [PATCH 17/33] Add Validator interface and refactor constraint validation --- .../smithy4s/example/ValidatedString.scala | 9 +-- .../src/smithy4s/ValidatedNewtypesSpec.scala | 28 +++++---- .../internals/CollisionAvoidance.scala | 1 + .../smithy4s/codegen/internals/Renderer.scala | 16 ++--- modules/core/src-2/NewtypeValidated.scala | 18 +----- modules/core/src/smithy4s/Validator.scala | 61 +++++++++++++++++++ 6 files changed, 86 insertions(+), 47 deletions(-) create mode 100644 modules/core/src/smithy4s/Validator.scala diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala index e40ca23f6..460b4c910 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala @@ -4,6 +4,7 @@ import smithy4s.Hints import smithy4s.NewtypeValidated import smithy4s.Schema import smithy4s.ShapeId +import smithy4s.Validator import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string @@ -12,10 +13,6 @@ object ValidatedString extends NewtypeValidated[String] { val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List( - a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) - ) - @inline def apply(a: String): Either[String, ValidatedString] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + val validator: Validator[String, ValidatedString] = Validator.of[String, ValidatedString].validating(smithy.api.Length(min = Some(1L), max = None)).validating(smithy.api.Pattern("[a-zA-Z0-9]+")).build + @inline def apply(a: String): Either[String, ValidatedString] = validator.validate(a) } diff --git a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala index ce41dc1a8..49bbf04b7 100644 --- a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala @@ -57,12 +57,13 @@ class ValidatedNewtypesSpec() extends munit.FunSuite { implicit val schema: Schema[DeviceId] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List(a => - validateInternal(smithy.api.Length(min = Some(1L), max = None))(a) - ) - @inline def apply(a: String): Either[String, DeviceId] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + val validator: Validator[String, DeviceId] = Validator + .of[String, DeviceId] + .validating(smithy.api.Length(min = Some(1L), max = None)) + .build + + @inline def apply(a: String): Either[String, DeviceId] = + validator.validate(a) } @@ -81,13 +82,14 @@ class ValidatedNewtypesSpec() extends munit.FunSuite { implicit val schema: Schema[AccountId] = bijection(underlyingSchema, asBijectionUnsafe) - val validators: List[String => Either[String, String]] = List( - a => validateInternal(smithy.api.Length(min = Some(1L), max = None))(a), - a => validateInternal(smithy.api.Pattern("[a-zA-Z0-9]+"))(a) - ) - @inline def apply(a: String): Either[String, AccountId] = validators - .foldLeft(Right(a): Either[String, String])((acc, v) => acc.flatMap(v)) - .map(unsafeApply) + val validator: Validator[String, AccountId] = Validator + .of[String, AccountId] + .validating(smithy.api.Length(min = Some(1L), max = None)) + .validating(smithy.api.Pattern("[a-zA-Z0-9]+")) + .build + + @inline def apply(a: String): Either[String, AccountId] = + validator.validate(a) } diff --git a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala index ad09d6259..6944810ba 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala @@ -306,6 +306,7 @@ private[internals] object CollisionAvoidance { val NoInput_ = NameRef("smithy4s", "NoInput") val ShapeId_ = NameRef("smithy4s", "ShapeId") val Schema_ = NameRef("smithy4s", "Schema") + val Validator_ = NameRef("smithy4s", "Validator") val OperationSchema_ = NameRef("smithy4s.schema", "OperationSchema") val FunctorAlgebra_ = NameRef("smithy4s.kinds", "FunctorAlgebra") val BiFunctorAlgebra_ = NameRef("smithy4s.kinds", "BiFunctorAlgebra") diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index d32ed1035..3fdd39f02 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -1337,13 +1337,13 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => recursive: Boolean, hints: List[Hint] ): Lines = { - val validators = { + val validator = { val tags = hints.collect { case t: Hint.Constraint => t } tags .map { tag => - line"a => validateInternal(${renderNativeHint(tag.native)})(a)" + line".validating(${renderNativeHint(tag.native)})" } - .intercalate(Line.comma) + .intercalate(Line.empty) } val definition = @@ -1363,15 +1363,9 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => line"implicit val schema: $Schema_[$name] = $definition$bijection_(underlyingSchema, asBijectionUnsafe)$closing" ), lines( - line"val validators: List[$tpe => Either[String, $tpe]] = List(", - indent(validators), - line")" - ), - lines( - line"@inline def apply(a: $tpe): Either[String, $name] = validators", - indent(line".foldLeft(Right(a): Either[String, $tpe])((acc, v) => acc.flatMap(v))"), - indent(line".map(unsafeApply)") + line"val validator: $Validator_[$tpe, $name] = $Validator_.of[$tpe, $name]$validator.build" ), + line"@inline def apply(a: $tpe): Either[String, $name] = validator.validate(a)", renderTypeclasses(hints, name) ) ) diff --git a/modules/core/src-2/NewtypeValidated.scala b/modules/core/src-2/NewtypeValidated.scala index a246f1bb1..69ea434b4 100644 --- a/modules/core/src-2/NewtypeValidated.scala +++ b/modules/core/src-2/NewtypeValidated.scala @@ -43,23 +43,7 @@ abstract class NewtypeValidated[A] extends HasId { self => def unapply(t: Type): Some[A] = Some(t.value) - implicit def refinementProvider[C](implicit - ev: RefinementProvider.Simple[C, A] - ): RefinementProvider.Simple[C, Type] = - new RefinementProvider.Simple[C, Type] { - val tag: ShapeTag[C] = ev.tag - override def make(c: C): Refinement.Aux[C, Type, Type] = - ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) - } - - protected val validators: List[A => Either[String, A]] - - protected def validateInternal[C](c: C)(value: A)(implicit - ev: RefinementProvider.Simple[C, A] - ): Either[String, A] = - ev.make(c).apply(value) - - protected val asBijectionUnsafe: Bijection[A, Type] = + protected implicit val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { def to(a: A): Type = self.unsafeApply(a) def from(t: Type): A = value(t) diff --git a/modules/core/src/smithy4s/Validator.scala b/modules/core/src/smithy4s/Validator.scala new file mode 100644 index 000000000..5230c0f08 --- /dev/null +++ b/modules/core/src/smithy4s/Validator.scala @@ -0,0 +1,61 @@ +/* + * Copyright 2021-2024 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 + +trait Validator[A, B] { + def validate(value: A): Either[String, B] +} + +object Validator { + + def of[A, B]: PartialValidatorBuilder[A, B] = + new PartialValidatorBuilder[A, B] + + private[smithy4s] class PartialValidatorBuilder[A, B] { + def validating[C](constraint: C)(implicit + bijection: Bijection[A, B], + ev: RefinementProvider.Simple[C, A] + ): ValidatorBuilder[A, B] = + new ValidatorBuilder[A, B](ev.make(constraint), bijection) + } + + private[smithy4s] class ValidatorBuilder[A, B] private ( + first: Refinement.Aux[_, A, A], + other: List[Refinement.Aux[_, A, A]], + bijection: Bijection[A, B] + ) { + + def this(ref: Refinement.Aux[_, A, A], bijection: Bijection[A, B]) = + this(ref, List.empty, bijection) + + def validating[C](constraint: C)(implicit + ev: RefinementProvider.Simple[C, A] + ): ValidatorBuilder[A, B] = + new ValidatorBuilder[A, B](first, other :+ ev.make(constraint), bijection) + + def build: Validator[A, B] = new Validator[A, B] { + override def validate(value: A): Either[String, B] = { + other + .foldLeft(first.apply(value)) { (valueOrError, refinement) => + valueOrError.flatMap(refinement.apply) + } + .map(bijection.apply) + } + } + } + +} From 5f2741328a1374b1c20ddba76190e5257b84fdb9 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 13:26:55 +0300 Subject: [PATCH 18/33] Rename trait --- .../src/smithy4s/codegen/internals/IR.scala | 2 +- .../codegen/internals/SmithyToIR.scala | 8 +++--- .../ValidatedNewtypesTransformer.scala | 6 ++-- .../codegen/internals/RendererSpec.scala | 6 ++-- .../ValidatedNewtypesTransformerSpec.scala | 28 +++++++++---------- ...re.amazon.smithy.model.traits.TraitService | 2 +- .../META-INF/smithy/smithy4s.meta.smithy | 2 +- ...tsTrait.java => ValidateNewtypeTrait.java} | 12 ++++---- sampleSpecs/validated-newtype.smithy | 4 +-- 9 files changed, 35 insertions(+), 35 deletions(-) rename modules/protocol/src/smithy4s/meta/{ValidateNewtypeConstraintsTrait.java => ValidateNewtypeTrait.java} (77%) diff --git a/modules/codegen/src/smithy4s/codegen/internals/IR.scala b/modules/codegen/src/smithy4s/codegen/internals/IR.scala index 01e2b665d..ea851a7bb 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/IR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/IR.scala @@ -342,7 +342,7 @@ private[internals] object Hint { extends Hint case object GenerateServiceProduct extends Hint case object GenerateOptics extends Hint - case object ValidateNewtypeConstraints extends Hint + case object ValidateNewtype extends Hint implicit val eq: Eq[Hint] = Eq.fromUniversalEquals } diff --git a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala index fff10d634..028a771d0 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala @@ -29,7 +29,7 @@ import smithy4s.meta.NoStackTraceTrait import smithy4s.meta.PackedInputsTrait import smithy4s.meta.RefinementTrait import smithy4s.meta.TypeclassTrait -import smithy4s.meta.ValidateNewtypeConstraintsTrait +import smithy4s.meta.ValidateNewtypeTrait import smithy4s.meta.VectorTrait import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.Model @@ -617,7 +617,7 @@ private[codegen] class SmithyToIR( !isUnboxedPrimitive(shape.getId()) ) { val shouldValidate = - shape.hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + shape.hasTrait(classOf[ValidateNewtypeTrait]) if (shouldValidate) { Type .ValidatedAlias( @@ -952,8 +952,8 @@ private[codegen] class SmithyToIR( Hint.GenerateServiceProduct case _: GenerateOpticsTrait => Hint.GenerateOptics - case _: ValidateNewtypeConstraintsTrait => - Hint.ValidateNewtypeConstraints + case _: ValidateNewtypeTrait => + Hint.ValidateNewtype case t if t.toShapeId() == ShapeId.fromParts("smithy.api", "trait") => Hint.Trait case ConstraintTrait(tr) => Hint.Constraint(toTypeRef(tr), unfoldTrait(tr)) diff --git a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala index 89b7c4aea..2bbace82c 100644 --- a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala +++ b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala @@ -17,7 +17,7 @@ package smithy4s.codegen.transformers import smithy4s.meta.UnwrapTrait -import smithy4s.meta.ValidateNewtypeConstraintsTrait +import smithy4s.meta.ValidateNewtypeTrait import software.amazon.smithy.build.ProjectionTransformer import software.amazon.smithy.build.TransformContext import software.amazon.smithy.model.Model @@ -69,7 +69,7 @@ class ValidatedNewtypesTransformer extends ProjectionTransformer { private def addTrait[S <: Shape, B <: AbstractShapeBuilder[B, S]]( builder: AbstractShapeBuilder[B, S] ): S = { - builder.addTrait(new ValidateNewtypeConstraintsTrait()) + builder.addTrait(new ValidateNewtypeTrait()) builder.build() } @@ -112,7 +112,7 @@ object ValidatedNewtypesTransformer { object SupportedShape { def unapply(shape: Shape): Option[Shape] = shape match { case _ if shape.hasTrait(classOf[UnwrapTrait]) => None - case _ if shape.hasTrait(classOf[ValidateNewtypeConstraintsTrait]) => None + case _ if shape.hasTrait(classOf[ValidateNewtypeTrait]) => None case s: StringShape if hasStringConstraints(s) => Some(s) case n: NumberShape if hasNumberConstraints(n) => Some(n) case _ => None diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index f244bc825..d2c003b49 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -485,16 +485,16 @@ final class RendererSpec extends munit.FunSuite { ) } - test("newtype with constraint and validateNewtypeConstraints annotation") { + test("newtype with constraint and validateNewtype annotation") { val smithy = """ |$version: "2" | |namespace smithy4s.example | - |use smithy4s.meta#validateNewtypeConstraints + |use smithy4s.meta#validateNewtype | |@length(min: 1, max: 10) - |@validateNewtypeConstraints + |@validateNewtype |string MyValidatedString | |structure ValidatedFoo { diff --git a/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala index 1f6076cd4..0f9ed0577 100644 --- a/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformerSpec.scala @@ -16,7 +16,7 @@ package smithy4s.codegen.transformers import software.amazon.smithy.build.TransformContext -import smithy4s.meta.ValidateNewtypeConstraintsTrait +import smithy4s.meta.ValidateNewtypeTrait import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.Model @@ -25,7 +25,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { import smithy4s.codegen.internals.TestUtils._ test( - "Leaves shape unchanged when @validateNewtypeConstraints is already present" + "Leaves shape unchanged when @validateNewtype is already present" ) { assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" @@ -34,16 +34,16 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { | |namespace smithy4s.transformer.test | - |use smithy4s.meta#validateNewtypeConstraints + |use smithy4s.meta#validateNewtype | |@length(min: 1, max: 10) - |@validateNewtypeConstraints + |@validateNewtype |string ValidatedString |""".stripMargin } } - test("Adds @validateNewtypeConstraints on string alias with constraint") { + test("Adds @validateNewtype on string alias with constraint") { assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | @@ -57,7 +57,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Adds @validateNewtypeConstraints on number alias with constraint") { + test("Adds @validateNewtype on number alias with constraint") { assertPresent("smithy4s.transformer.test#ValidatedNumber") { """|$version: "2.0" | @@ -71,7 +71,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } } - test("Does not add @validateNewtypeConstraints on unwrapped string alias") { + test("Does not add @validateNewtype on unwrapped string alias") { assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" | @@ -89,7 +89,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } test( - "Does not add @validateNewtypeConstraints on type when smithy4sRenderValidatedNewtypes=false" + "Does not add @validateNewtype on type when smithy4sRenderValidatedNewtypes=false" ) { assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" @@ -105,7 +105,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } test( - "Does not add @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=false" + "Does not add @validateNewtype on previously generated shapes with validatedNewtypes=false" ) { assertMissing("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" @@ -122,7 +122,7 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { } test( - "Adds @validateNewtypeConstraints on previously generated shapes with validatedNewtypes=true" + "Adds @validateNewtype on previously generated shapes with validatedNewtypes=true" ) { assertPresent("smithy4s.transformer.test#ValidatedString") { """|$version: "2.0" @@ -143,11 +143,11 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { ): Unit = { val containsTrait = loadAndTransformModel(inputModel: _*) .expectShape(ShapeId.from(shapeId)) - .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + .hasTrait(classOf[ValidateNewtypeTrait]) assert( containsTrait, - "Expected validateNewtypeConstraints trait to be present" + "Expected validateNewtype trait to be present" ) } @@ -156,11 +156,11 @@ class ValidatedNewtypesTransformerSpec extends munit.FunSuite { ): Unit = { val containsTrait = loadAndTransformModel(inputModel: _*) .expectShape(ShapeId.from(shapeId)) - .hasTrait(classOf[ValidateNewtypeConstraintsTrait]) + .hasTrait(classOf[ValidateNewtypeTrait]) assert( !containsTrait, - "Expected validateNewtypeConstraints trait to be missing" + "Expected validateNewtype trait to be missing" ) } diff --git a/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 918d36b89..6a9216a59 100644 --- a/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/modules/protocol/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -10,4 +10,4 @@ smithy4s.meta.AdtTrait$Provider smithy4s.meta.TypeclassTrait$Provider smithy4s.meta.GenerateServiceProductTrait$Provider smithy4s.meta.GenerateOpticsTrait$Provider -smithy4s.meta.ValidateNewtypeConstraintsTrait$Provider +smithy4s.meta.ValidateNewtypeTrait$Provider diff --git a/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy b/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy index ec659caa5..9e03a5f11 100644 --- a/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy +++ b/modules/protocol/resources/META-INF/smithy/smithy4s.meta.smithy @@ -168,4 +168,4 @@ structure noStackTrace {} string[trait|length] )""", conflicts: ["smithy4s.meta#unwrap"]) -structure validateNewtypeConstraints {} +structure validateNewtype {} diff --git a/modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java b/modules/protocol/src/smithy4s/meta/ValidateNewtypeTrait.java similarity index 77% rename from modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java rename to modules/protocol/src/smithy4s/meta/ValidateNewtypeTrait.java index 07faca82f..50ee8b445 100644 --- a/modules/protocol/src/smithy4s/meta/ValidateNewtypeConstraintsTrait.java +++ b/modules/protocol/src/smithy4s/meta/ValidateNewtypeTrait.java @@ -22,14 +22,14 @@ import software.amazon.smithy.model.traits.AnnotationTrait; import software.amazon.smithy.model.traits.AbstractTrait; -public class ValidateNewtypeConstraintsTrait extends AnnotationTrait { - public static ShapeId ID = ShapeId.from("smithy4s.meta#validateNewtypeConstraints"); +public class ValidateNewtypeTrait extends AnnotationTrait { + public static ShapeId ID = ShapeId.from("smithy4s.meta#validateNewtype"); - public ValidateNewtypeConstraintsTrait(ObjectNode node) { + public ValidateNewtypeTrait(ObjectNode node) { super(ID, node); } - public ValidateNewtypeConstraintsTrait() { + public ValidateNewtypeTrait() { super(ID, Node.objectNode()); } @@ -39,8 +39,8 @@ public Provider() { } @Override - public ValidateNewtypeConstraintsTrait createTrait(ShapeId target, Node node) { - return new ValidateNewtypeConstraintsTrait(node.expectObjectNode()); + public ValidateNewtypeTrait createTrait(ShapeId target, Node node) { + return new ValidateNewtypeTrait(node.expectObjectNode()); } } } diff --git a/sampleSpecs/validated-newtype.smithy b/sampleSpecs/validated-newtype.smithy index 9d2466661..62dac2c8f 100644 --- a/sampleSpecs/validated-newtype.smithy +++ b/sampleSpecs/validated-newtype.smithy @@ -2,11 +2,11 @@ $version: "2" namespace smithy4s.example -use smithy4s.meta#validateNewtypeConstraints +use smithy4s.meta#validateNewtype @length(min: 1) @pattern("[a-zA-Z0-9]+") -@validateNewtypeConstraints +@validateNewtype string ValidatedString @length(min: 1) From 382b96a64f47553eb69ebd3169df197c155daef9 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 13:37:43 +0300 Subject: [PATCH 19/33] Fix scala3 encoding --- modules/core/src-3/NewtypeValidated.scala | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/modules/core/src-3/NewtypeValidated.scala b/modules/core/src-3/NewtypeValidated.scala index 0c34b3c64..ceca092e1 100644 --- a/modules/core/src-3/NewtypeValidated.scala +++ b/modules/core/src-3/NewtypeValidated.scala @@ -34,24 +34,7 @@ abstract class NewtypeValidated[A] extends HasId { self => def schema: Schema[Type] = self.schema } - given refinementProvider[C](using - ev: RefinementProvider.Simple[C, A] - ): RefinementProvider.Simple[C, Type] with { - - val tag: ShapeTag[C] = ev.tag - - override def make(c: C): Refinement.Aux[C, Type, Type] = - ev.make(c).imapFull[Type, Type](asBijectionUnsafe, asBijectionUnsafe) - } - - protected val validators: List[A => Either[String, A]] - - protected def validateInternal[C](c: C)(value: A)(using - ev: RefinementProvider.Simple[C, A] - ): Either[String, A] = - ev.make(c).apply(value) - - protected val asBijectionUnsafe: Bijection[A, Type] = + protected implicit val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { def to(a: A): Type = self.unsafeApply(a) def from(t: Type): A = value(t) From a09a5684b4cf5e4702d17159e26ac66167e910ff Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 13:38:10 +0300 Subject: [PATCH 20/33] Fix formatting, again --- .../transformers/ValidatedNewtypesTransformer.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala index 2bbace82c..63ca5e374 100644 --- a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala +++ b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala @@ -111,11 +111,11 @@ object ValidatedNewtypesTransformer { object SupportedShape { def unapply(shape: Shape): Option[Shape] = shape match { - case _ if shape.hasTrait(classOf[UnwrapTrait]) => None + case _ if shape.hasTrait(classOf[UnwrapTrait]) => None case _ if shape.hasTrait(classOf[ValidateNewtypeTrait]) => None - case s: StringShape if hasStringConstraints(s) => Some(s) - case n: NumberShape if hasNumberConstraints(n) => Some(n) - case _ => None + case s: StringShape if hasStringConstraints(s) => Some(s) + case n: NumberShape if hasNumberConstraints(n) => Some(n) + case _ => None } private def hasStringConstraints(shape: Shape): Boolean = From 9fe555a221f0b976d3912d9733530b118e408bb7 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 16:47:08 +0300 Subject: [PATCH 21/33] Add doc entry --- .../01-customisation/13-validated-newtypes.md | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md diff --git a/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md b/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md new file mode 100644 index 000000000..9aac25c1b --- /dev/null +++ b/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md @@ -0,0 +1,66 @@ +--- +sidebar_label: Validated Newtypes +title: Validated Newtypes +--- + +As of version `0.19.x` Smithy4s has the ability to render constrained newtypes over Smithy primitives as +"validated" classes in the code it generates. In practice this means that a newtype will now have an +`apply` method that returns either a validated value or an error. + +If you're using Smithy4s via `mill` or `sbt`, then you can enable this functionality with the following keys: + +* in mill, task: `def smithy4sRenderValidatedNewtypes = true` +* in sbt, setting: `smithy4sRenderValidatedNewtypes := true` + +If you are using Smithy4s via the CLI, then they way to utilize this feature is through your Smithy specifications. +The simplest approach is to add a file with the following content to your CLI invocation: + +```kotlin +$version: "2" + +metadata smithy4sRenderValidatedNewtypes = true +``` + +Alternatively, if you want to generate validated newtypes only for select shapes in your model, you can accomplish +this using the `smithy4s.meta#validateNewtype` trait. This trait can only be used on number shapes with a range +constraint or string shapes with pattern and/or length constraints. + +```kotlin +use smithy4s.meta#validateNewtype + +@validateNewtype +@length(min: 5) +string Name +``` + +Below is the generated scala class that Smithy4s will generate: + +```scala mdoc + +import smithy4s._ +import smithy4s.schema.Schema.bijection +import smithy4s.schema.Schema.string + +type Name = Name.Type + +object Name extends NewtypeValidated[String] { + val id: ShapeId = ShapeId("smithy4s.example", "Name") + + val hints: Hints = Hints.empty + + val underlyingSchema: Schema[String] = + string + .withId(id) + .addHints(hints) + .validated(smithy.api.Length(min = Some(5L), max = None)) + + implicit val schema: Schema[Name] = bijection(underlyingSchema, asBijectionUnsafe) + + val validator: Validator[String, Name] = + Validator.of[String, Name] + .validating(smithy.api.Length(min = Some(5L), max = None)) + .build + + @inline def apply(a: String): Either[String, Name] = validator.validate(a) +} +``` \ No newline at end of file From e378c0aca0a87bb8a337c7529e0feb6fa39a13ca Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 18 Apr 2024 16:55:55 +0300 Subject: [PATCH 22/33] Make mdoc compile only --- .../04-codegen/01-customisation/13-validated-newtypes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md b/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md index 9aac25c1b..e7e28ae8f 100644 --- a/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md +++ b/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md @@ -35,7 +35,7 @@ string Name Below is the generated scala class that Smithy4s will generate: -```scala mdoc +```scala mdoc:compile-only import smithy4s._ import smithy4s.schema.Schema.bijection From 1e2b2ea6ee6990ab31f30fd5ff198c4836691cda Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 24 Apr 2024 23:22:00 +0300 Subject: [PATCH 23/33] Format mandatory transformers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Kozłowski --- .../src/smithy4s/codegen/internals/CodegenImpl.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala index cd77a34ff..64a812345 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala @@ -186,6 +186,10 @@ private[codegen] object CodegenImpl { self => private def withMandatoryTransformers( transformers: List[String] ): List[String] = - transformers :+ AwsConstraintsRemover.name :+ AwsStandardTypesTransformer.name :+ OpenEnumTransformer.name :+ ValidatedNewtypesTransformer.name + transformers :+ + AwsConstraintsRemover.name :+ + AwsStandardTypesTransformer.name :+ + OpenEnumTransformer.name :+ + ValidatedNewtypesTransformer.name } From c98a563cfb2b75e91de9215c48a6e7c5205911d1 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 24 Apr 2024 23:38:18 +0300 Subject: [PATCH 24/33] Make Validator trait sealed --- .../codegen/internals/CodegenImpl.scala | 8 ++--- modules/core/src/smithy4s/Validator.scala | 31 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala index 64a812345..ecec8eea0 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CodegenImpl.scala @@ -186,10 +186,10 @@ private[codegen] object CodegenImpl { self => private def withMandatoryTransformers( transformers: List[String] ): List[String] = - transformers :+ - AwsConstraintsRemover.name :+ - AwsStandardTypesTransformer.name :+ - OpenEnumTransformer.name :+ + transformers :+ + AwsConstraintsRemover.name :+ + AwsStandardTypesTransformer.name :+ + OpenEnumTransformer.name :+ ValidatedNewtypesTransformer.name } diff --git a/modules/core/src/smithy4s/Validator.scala b/modules/core/src/smithy4s/Validator.scala index 5230c0f08..c2f3e8a67 100644 --- a/modules/core/src/smithy4s/Validator.scala +++ b/modules/core/src/smithy4s/Validator.scala @@ -16,7 +16,7 @@ package smithy4s -trait Validator[A, B] { +sealed trait Validator[A, B] { def validate(value: A): Either[String, B] } @@ -30,31 +30,34 @@ object Validator { bijection: Bijection[A, B], ev: RefinementProvider.Simple[C, A] ): ValidatorBuilder[A, B] = - new ValidatorBuilder[A, B](ev.make(constraint), bijection) + new ValidatorBuilder[A, B](ev.make(constraint), List.empty, bijection) } - private[smithy4s] class ValidatorBuilder[A, B] private ( + private[smithy4s] class ValidatorBuilder[A, B]( first: Refinement.Aux[_, A, A], other: List[Refinement.Aux[_, A, A]], bijection: Bijection[A, B] ) { - def this(ref: Refinement.Aux[_, A, A], bijection: Bijection[A, B]) = - this(ref, List.empty, bijection) - def validating[C](constraint: C)(implicit ev: RefinementProvider.Simple[C, A] ): ValidatorBuilder[A, B] = new ValidatorBuilder[A, B](first, other :+ ev.make(constraint), bijection) - def build: Validator[A, B] = new Validator[A, B] { - override def validate(value: A): Either[String, B] = { - other - .foldLeft(first.apply(value)) { (valueOrError, refinement) => - valueOrError.flatMap(refinement.apply) - } - .map(bijection.apply) - } + def build: Validator[A, B] = new ComposedValidator(first, other, bijection) + } + + private final class ComposedValidator[A, B]( + first: Refinement.Aux[_, A, A], + other: List[Refinement.Aux[_, A, A]], + bijection: Bijection[A, B] + ) extends Validator[A, B] { + override def validate(value: A): Either[String, B] = { + other + .foldLeft(first.apply(value)) { (valueOrError, refinement) => + valueOrError.flatMap(refinement.apply) + } + .map(bijection.apply) } } From e7ca9851b4815596cf2c658e490cee75649f438d Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 24 Apr 2024 23:43:26 +0300 Subject: [PATCH 25/33] Add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 725d623a4..f75c5ee03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 0.19.0 +## Validated newtypes [#1454](https://github.com/disneystreaming/smithy4s/pull/1454) + +Add support for rendering constrained newtypes over Smithy primitives as validated newtypes. These types now have an `apply` method which returns either an error or a validated value. + ## Documentation fix Prevent documentation from being generated for case class when the field are not generated because they're annotated with `@streaming` From d38247dd326b426c065cb2d26d3ff9e25f07517a Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Fri, 26 Apr 2024 11:48:09 +0300 Subject: [PATCH 26/33] Change unsafeApply to throw exceptions --- .../src/smithy4s/ValidatedNewtypesSpec.scala | 16 ++++++++++++++-- modules/core/src-2/NewtypeValidated.scala | 7 +++++-- modules/core/src-3/NewtypeValidated.scala | 7 +++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala index 49bbf04b7..7d6ad28ce 100644 --- a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala @@ -18,10 +18,11 @@ package smithy4s import smithy4s.schema.Schema.bijection import smithy4s.schema.Schema.string +import munit.Assertions class ValidatedNewtypesSpec() extends munit.FunSuite { - val id1 = "id-1" - val id2 = "id-2" + val id1 = "id1" + val id2 = "id2" test("Validated newtypes are consistent") { expect.same(AccountId.unsafeApply(id1).value, id1) @@ -43,6 +44,17 @@ class ValidatedNewtypesSpec() extends munit.FunSuite { } } + test("Validated newtypes unsafeApply throws exception") { + val e = Assertions.intercept[IllegalArgumentException] { + AccountId.unsafeApply("!^%&") + } + + expect.same( + e.getMessage(), + "String '!^%&' does not match pattern '[a-zA-Z0-9]+'" + ) + } + type DeviceId = DeviceId.Type object DeviceId extends NewtypeValidated[String] { diff --git a/modules/core/src-2/NewtypeValidated.scala b/modules/core/src-2/NewtypeValidated.scala index 69ea434b4..43bc4992c 100644 --- a/modules/core/src-2/NewtypeValidated.scala +++ b/modules/core/src-2/NewtypeValidated.scala @@ -25,7 +25,10 @@ abstract class NewtypeValidated[A] extends HasId { self => @inline def apply(a: A): Either[String, Type] - @inline final def unsafeApply(a: A): Type = a.asInstanceOf[Type] + @inline final def unsafeApply(a: A): Type = apply(a) match { + case Right(value) => value + case Left(error) => throw new IllegalArgumentException(error) + } @inline final def value(x: Type): A = x.asInstanceOf[A] @@ -45,7 +48,7 @@ abstract class NewtypeValidated[A] extends HasId { self => protected implicit val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { - def to(a: A): Type = self.unsafeApply(a) + def to(a: A): Type = a.asInstanceOf[Type] def from(t: Type): A = value(t) } diff --git a/modules/core/src-3/NewtypeValidated.scala b/modules/core/src-3/NewtypeValidated.scala index ceca092e1..bac19395e 100644 --- a/modules/core/src-3/NewtypeValidated.scala +++ b/modules/core/src-3/NewtypeValidated.scala @@ -21,7 +21,10 @@ abstract class NewtypeValidated[A] extends HasId { self => def apply(a: A): Either[String, Type] - def unsafeApply(a: A): Type = a + def unsafeApply(a: A): Type = apply(a) match { + case Right(value) => value + case Left(error) => throw new IllegalArgumentException(error) + } extension (orig: Type) def value: A = orig @@ -36,7 +39,7 @@ abstract class NewtypeValidated[A] extends HasId { self => protected implicit val asBijectionUnsafe: Bijection[A, Type] = new NewtypeValidated.Make[A, Type] { - def to(a: A): Type = self.unsafeApply(a) + def to(a: A): Type = a.asInstanceOf[Type] def from(t: Type): A = value(t) } From 8fd25f1d80609cd929c7f05620eeaedbdf1c0a98 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 22 May 2024 17:04:57 +0300 Subject: [PATCH 27/33] Fix compilation --- .../codegen/internals/CollisionAvoidance.scala | 1 - .../src/smithy4s/codegen/internals/Renderer.scala | 15 ++++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala index be483f360..2970e425c 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/CollisionAvoidance.scala @@ -17,7 +17,6 @@ package smithy4s.codegen.internals import cats.~> -import smithy4s.codegen.internals.Type.Nullable import Type.Alias import Type.Nullable diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 5d4f4255f..1c0ebea06 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -261,8 +261,11 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => def renderPackageContents: Lines = { val typeAliases = compilationUnit.declarations - .collect { case TypeAlias(_, name, _, _, _, hints) => - (name, hints) + .collect { + case TypeAlias(_, name, _, _, _, hints) => + (name, hints) + case ValidatedTypeAlias(_, name, _, _, hints) => + (name, hints) } .sortBy(_._1) .map { case (name, hints) => @@ -271,13 +274,7 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => deprecationAnnotation(hints), line"type $name = ${compilationUnit.namespace}.${name}.Type" ) - case ValidatedTypeAlias(_, name, _, _, hints) => - lines( - documentationAnnotation(hints), - deprecationAnnotation(hints), - line"type $name = ${compilationUnit.namespace}.${name}.Type" - ) - } + } val blk = block( From 804b5746c735f21707c040865d10d5035740eed0 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 22 May 2024 17:38:39 +0300 Subject: [PATCH 28/33] Fix bootstrapped generated code --- .../src/generated/smithy4s/example/ValidatedFoo.scala | 7 ++++--- .../src/generated/smithy4s/example/package.scala | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala index ace458e97..56e6257e0 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedFoo.scala @@ -13,9 +13,10 @@ object ValidatedFoo extends ShapeTag.Companion[ValidatedFoo] { val hints: Hints = Hints.empty + // constructor using the original order from the spec + private def make(name: ValidatedString): ValidatedFoo = ValidatedFoo(name) + implicit val schema: Schema[ValidatedFoo] = struct( ValidatedString.schema.field[ValidatedFoo]("name", _.name).addHints(smithy.api.Default(smithy4s.Document.fromString("abc"))), - ){ - ValidatedFoo.apply - }.withId(id).addHints(hints) + )(make).withId(id).addHints(hints) } diff --git a/modules/bootstrapped/src/generated/smithy4s/example/package.scala b/modules/bootstrapped/src/generated/smithy4s/example/package.scala index 26b3d506a..52d37ec2c 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/package.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/package.scala @@ -82,6 +82,7 @@ package object example { type NonEmptyMapNumbers = smithy4s.example.NonEmptyMapNumbers.Type type NonEmptyNames = smithy4s.example.NonEmptyNames.Type type NonEmptyStrings = smithy4s.example.NonEmptyStrings.Type + type NonValidatedString = smithy4s.example.NonValidatedString.Type type ObjectKey = smithy4s.example.ObjectKey.Type type ObjectSize = smithy4s.example.ObjectSize.Type type OrderNumber = smithy4s.example.OrderNumber.Type @@ -115,5 +116,6 @@ package object example { type UVIndex = smithy4s.example.UVIndex.Type type UnicodeRegexString = smithy4s.example.UnicodeRegexString.Type type UnwrappedFancyList = smithy4s.example.UnwrappedFancyList.Type + type ValidatedString = smithy4s.example.ValidatedString.Type } \ No newline at end of file From 712f773179f41201c7415b25303593deed5be550 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 22 May 2024 18:08:46 +0300 Subject: [PATCH 29/33] Remove configuration from build tool --- .../render-validated-newtypes/build.sbt | 1 - .../src/main/smithy/validated-newtypes.smithy | 4 ++++ .../smithy4s/codegen/Smithy4sCodegenPlugin.scala | 16 +++------------- .../codegen/internals/RendererSpec.scala | 1 - ...ated-newtypes.md => 15-validated-newtypes.md} | 9 ++------- 5 files changed, 9 insertions(+), 22 deletions(-) rename modules/docs/markdown/04-codegen/01-customisation/{13-validated-newtypes.md => 15-validated-newtypes.md} (78%) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt index c7d5992db..3fe4c889c 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/build.sbt @@ -2,7 +2,6 @@ lazy val root = (project in file(".")) .enablePlugins(Smithy4sCodegenPlugin) .settings( scalaVersion := "2.13.10", - Compile / smithy4sRenderValidatedNewtypes := true, libraryDependencies ++= Seq( "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value, "com.disneystreaming.smithy4s" %% "smithy4s-dynamic" % smithy4sVersion.value, diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy index 156d7512c..98b73badb 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/render-validated-newtypes/src/main/smithy/validated-newtypes.smithy @@ -1,3 +1,7 @@ +$version: "2.0" + +metadata smithy4sRenderValidatedNewtypes = true + namespace newtypes.validated use smithy4s.meta#unwrap diff --git a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala index 55791d017..298feeaff 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala @@ -145,11 +145,6 @@ object Smithy4sCodegenPlugin extends AutoPlugin { "Boolean value to indicate whether or not to generate optics" ) - val smithy4sRenderValidatedNewtypes = - taskKey[Boolean]( - "Boolean value to indicate whether or not to generate validated newtypes" - ) - val smithy4sGeneratedSmithyFiles = taskKey[Seq[File]]( "Generated smithy files" @@ -265,7 +260,6 @@ object Smithy4sCodegenPlugin extends AutoPlugin { } }, config / smithy4sRenderOptics := false, - config / smithy4sRenderValidatedNewtypes := false, config / smithy4sGeneratedSmithyMetadataFile := { (config / sourceManaged).value / "smithy" / "generated-metadata.smithy" }, @@ -273,15 +267,14 @@ object Smithy4sCodegenPlugin extends AutoPlugin { val cacheFactory = (config / streams).value.cacheStoreFactory val cached = Tracked - .inputChanged[(String, Boolean, Boolean), Seq[File]]( + .inputChanged[(String, Boolean), Seq[File]]( cacheFactory.make("smithy4sGeneratedSmithyFilesInput") ) { case ( changed, ( wildcardArg, - shouldGenerateOptics, - shouldRenderValidatedNewtypes + shouldGenerateOptics ) ) => val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( @@ -295,7 +288,6 @@ object Smithy4sCodegenPlugin extends AutoPlugin { s"""$$version: "2" |metadata smithy4sWildcardArgument = "$wildcardArg" |metadata smithy4sRenderOptics = $shouldGenerateOptics - |metadata smithy4sRenderValidatedNewtypes = $shouldRenderValidatedNewtypes |""".stripMargin ) Seq(file) @@ -307,9 +299,7 @@ object Smithy4sCodegenPlugin extends AutoPlugin { } val wildcardArg = (config / smithy4sWildcardArgument).value val generateOptics = (config / smithy4sRenderOptics).value - val renderValidatedNewtypes = - (config / smithy4sRenderValidatedNewtypes).value - cached((wildcardArg, generateOptics, renderValidatedNewtypes)) + cached((wildcardArg, generateOptics)) }, config / sourceGenerators += (config / smithy4sCodegen).map( _.filter(_.ext == "scala") diff --git a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala index 960a41745..260671583 100644 --- a/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala +++ b/modules/codegen/test/src/smithy4s/codegen/internals/RendererSpec.scala @@ -520,7 +520,6 @@ final class RendererSpec extends munit.ScalaCheckSuite { } } - test("newtype with constraint and validateNewtype annotation") { val smithy = """ |$version: "2" diff --git a/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md b/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md similarity index 78% rename from modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md rename to modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md index e7e28ae8f..17e47451f 100644 --- a/modules/docs/markdown/04-codegen/01-customisation/13-validated-newtypes.md +++ b/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md @@ -7,13 +7,8 @@ As of version `0.19.x` Smithy4s has the ability to render constrained newtypes o "validated" classes in the code it generates. In practice this means that a newtype will now have an `apply` method that returns either a validated value or an error. -If you're using Smithy4s via `mill` or `sbt`, then you can enable this functionality with the following keys: - -* in mill, task: `def smithy4sRenderValidatedNewtypes = true` -* in sbt, setting: `smithy4sRenderValidatedNewtypes := true` - -If you are using Smithy4s via the CLI, then they way to utilize this feature is through your Smithy specifications. -The simplest approach is to add a file with the following content to your CLI invocation: +The way to utilize this feature is through your Smithy specifications by adding a file with the following +content to your Smithy sources: ```kotlin $version: "2" From 39f4d4862ed04cda61798e52981e983c7bc4af24 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 23 May 2024 14:05:06 +0300 Subject: [PATCH 30/33] Refactor Validator --- .../smithy4s/example/ValidatedString.scala | 2 +- .../src/smithy4s/ValidatedNewtypesSpec.scala | 4 +- .../smithy4s/codegen/internals/Renderer.scala | 15 +++--- modules/core/src/smithy4s/Validator.scala | 47 +++++++++---------- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala index 460b4c910..c02d6ba6d 100644 --- a/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala +++ b/modules/bootstrapped/src/generated/smithy4s/example/ValidatedString.scala @@ -13,6 +13,6 @@ object ValidatedString extends NewtypeValidated[String] { val hints: Hints = Hints.empty val underlyingSchema: Schema[String] = string.withId(id).addHints(hints).validated(smithy.api.Length(min = Some(1L), max = None)).validated(smithy.api.Pattern("[a-zA-Z0-9]+")) implicit val schema: Schema[ValidatedString] = bijection(underlyingSchema, asBijectionUnsafe) - val validator: Validator[String, ValidatedString] = Validator.of[String, ValidatedString].validating(smithy.api.Length(min = Some(1L), max = None)).validating(smithy.api.Pattern("[a-zA-Z0-9]+")).build + val validator: Validator[String, ValidatedString] = Validator.of[String, ValidatedString].validating(smithy.api.Length(min = Some(1L), max = None)).alsoValidating(smithy.api.Pattern("[a-zA-Z0-9]+")) @inline def apply(a: String): Either[String, ValidatedString] = validator.validate(a) } diff --git a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala index 7d6ad28ce..7aa4a4063 100644 --- a/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala +++ b/modules/bootstrapped/test/src/smithy4s/ValidatedNewtypesSpec.scala @@ -72,7 +72,6 @@ class ValidatedNewtypesSpec() extends munit.FunSuite { val validator: Validator[String, DeviceId] = Validator .of[String, DeviceId] .validating(smithy.api.Length(min = Some(1L), max = None)) - .build @inline def apply(a: String): Either[String, DeviceId] = validator.validate(a) @@ -97,8 +96,7 @@ class ValidatedNewtypesSpec() extends munit.FunSuite { val validator: Validator[String, AccountId] = Validator .of[String, AccountId] .validating(smithy.api.Length(min = Some(1L), max = None)) - .validating(smithy.api.Pattern("[a-zA-Z0-9]+")) - .build + .alsoValidating(smithy.api.Pattern("[a-zA-Z0-9]+")) @inline def apply(a: String): Either[String, AccountId] = validator.validate(a) diff --git a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala index 1c0ebea06..ffe5386ff 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/internals/Renderer.scala @@ -1352,11 +1352,14 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => ): Lines = { val validator = { val tags = hints.collect { case t: Hint.Constraint => t } - tags - .map { tag => - line".validating(${renderNativeHint(tag.native)})" - } - .intercalate(Line.empty) + tags match { + case h :: tail => + ( + line".validating(${renderNativeHint(h.native)})" +: + tail.map { tag => line".alsoValidating(${renderNativeHint(tag.native)})" } + ).intercalate(Line.empty) + case _ => Line.empty + } } val definition = @@ -1376,7 +1379,7 @@ private[internals] class Renderer(compilationUnit: CompilationUnit) { self => line"implicit val schema: $Schema_[$name] = $definition$bijection_(underlyingSchema, asBijectionUnsafe)$closing" ), lines( - line"val validator: $Validator_[$tpe, $name] = $Validator_.of[$tpe, $name]$validator.build" + line"val validator: $Validator_[$tpe, $name] = $Validator_.of[$tpe, $name]$validator" ), line"@inline def apply(a: $tpe): Either[String, $name] = validator.validate(a)", renderTypeclasses(hints, name) diff --git a/modules/core/src/smithy4s/Validator.scala b/modules/core/src/smithy4s/Validator.scala index c2f3e8a67..4122da908 100644 --- a/modules/core/src/smithy4s/Validator.scala +++ b/modules/core/src/smithy4s/Validator.scala @@ -18,47 +18,42 @@ package smithy4s sealed trait Validator[A, B] { def validate(value: A): Either[String, B] + + def alsoValidating[C](constraint: C)(implicit + ev: RefinementProvider.Simple[C, A] + ): Validator[A, B] } object Validator { - def of[A, B]: PartialValidatorBuilder[A, B] = - new PartialValidatorBuilder[A, B] + def of[A, B]: ValidatorBuilder[A, B] = + new ValidatorBuilder[A, B] - private[smithy4s] class PartialValidatorBuilder[A, B] { + final class ValidatorBuilder[A, B] private[smithy4s] () { def validating[C](constraint: C)(implicit bijection: Bijection[A, B], ev: RefinementProvider.Simple[C, A] - ): ValidatorBuilder[A, B] = - new ValidatorBuilder[A, B](ev.make(constraint), List.empty, bijection) - } - - private[smithy4s] class ValidatorBuilder[A, B]( - first: Refinement.Aux[_, A, A], - other: List[Refinement.Aux[_, A, A]], - bijection: Bijection[A, B] - ) { - - def validating[C](constraint: C)(implicit - ev: RefinementProvider.Simple[C, A] - ): ValidatorBuilder[A, B] = - new ValidatorBuilder[A, B](first, other :+ ev.make(constraint), bijection) - - def build: Validator[A, B] = new ComposedValidator(first, other, bijection) + ): Validator[A, B] = + new ValidatorImpl[A, B](List(ev.make(constraint)), bijection) } - private final class ComposedValidator[A, B]( - first: Refinement.Aux[_, A, A], - other: List[Refinement.Aux[_, A, A]], + private class ValidatorImpl[A, B]( + refinements: List[Refinement.Aux[_, A, A]], bijection: Bijection[A, B] ) extends Validator[A, B] { + override def validate(value: A): Either[String, B] = { - other - .foldLeft(first.apply(value)) { (valueOrError, refinement) => - valueOrError.flatMap(refinement.apply) + refinements + .foldLeft(Right(value): Either[String, A]) { + (valueOrError, refinement) => + valueOrError.flatMap(refinement.apply) } .map(bijection.apply) } - } + override def alsoValidating[C](constraint: C)(implicit + ev: RefinementProvider.Simple[C, A] + ): Validator[A, B] = + new ValidatorImpl[A, B](refinements :+ ev.make(constraint), bijection) + } } From bff9cc4017dcee1ecbc233730a68b17d191e8db0 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 23 May 2024 14:08:47 +0300 Subject: [PATCH 31/33] Revert formatting changes --- .../codegen/Smithy4sCodegenPlugin.scala | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala index 298feeaff..0ffa2cf65 100644 --- a/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala +++ b/modules/codegen-plugin/src/smithy4s/codegen/Smithy4sCodegenPlugin.scala @@ -265,38 +265,29 @@ object Smithy4sCodegenPlugin extends AutoPlugin { }, config / smithy4sGeneratedSmithyFiles := { val cacheFactory = (config / streams).value.cacheStoreFactory - val cached = - Tracked - .inputChanged[(String, Boolean), Seq[File]]( - cacheFactory.make("smithy4sGeneratedSmithyFilesInput") - ) { - case ( - changed, - ( - wildcardArg, - shouldGenerateOptics - ) - ) => - val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( - cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") - ) { case (changed, prevResult) => - if (changed || prevResult.isEmpty) { - val file = - (config / smithy4sGeneratedSmithyMetadataFile).value - IO.write( - file, - s"""$$version: "2" - |metadata smithy4sWildcardArgument = "$wildcardArg" - |metadata smithy4sRenderOptics = $shouldGenerateOptics - |""".stripMargin - ) - Seq(file) - } else { - prevResult.get - } - } - lastOutput(changed) + val cached = Tracked.inputChanged[(String, Boolean), Seq[File]]( + cacheFactory.make("smithy4sGeneratedSmithyFilesInput") + ) { case (changed, (wildcardArg, shouldGenerateOptics)) => + val lastOutput = Tracked.lastOutput[Boolean, Seq[File]]( + cacheFactory.make("smithy4sGeneratedSmithyFilesOutput") + ) { case (changed, prevResult) => + if (changed || prevResult.isEmpty) { + val file = + (config / smithy4sGeneratedSmithyMetadataFile).value + IO.write( + file, + s"""$$version: "2" + |metadata smithy4sWildcardArgument = "$wildcardArg" + |metadata smithy4sRenderOptics = $shouldGenerateOptics + |""".stripMargin + ) + Seq(file) + } else { + prevResult.get } + } + lastOutput(changed) + } val wildcardArg = (config / smithy4sWildcardArgument).value val generateOptics = (config / smithy4sRenderOptics).value cached((wildcardArg, generateOptics)) From 2a292bf4a8233796f5dd1bc184b49c7e3db754fc Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 23 May 2024 14:34:21 +0300 Subject: [PATCH 32/33] Fix documentation --- .../04-codegen/01-customisation/15-validated-newtypes.md | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md b/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md index 17e47451f..1af8b8977 100644 --- a/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md +++ b/modules/docs/markdown/04-codegen/01-customisation/15-validated-newtypes.md @@ -54,7 +54,6 @@ object Name extends NewtypeValidated[String] { val validator: Validator[String, Name] = Validator.of[String, Name] .validating(smithy.api.Length(min = Some(5L), max = None)) - .build @inline def apply(a: String): Either[String, Name] = validator.validate(a) } From abcd4fd2f074e3bac3f978e63b278b2a76fea1f5 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 23 May 2024 16:01:37 +0300 Subject: [PATCH 33/33] Refactor CodegenRecord usage --- .../{internals => }/CodegenRecord.scala | 6 +-- .../ValidatedNewtypesTransformer.scala | 41 ++++--------------- 2 files changed, 12 insertions(+), 35 deletions(-) rename modules/codegen/src/smithy4s/codegen/{internals => }/CodegenRecord.scala (92%) diff --git a/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala b/modules/codegen/src/smithy4s/codegen/CodegenRecord.scala similarity index 92% rename from modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala rename to modules/codegen/src/smithy4s/codegen/CodegenRecord.scala index 435bbf530..727cff4f7 100644 --- a/modules/codegen/src/smithy4s/codegen/internals/CodegenRecord.scala +++ b/modules/codegen/src/smithy4s/codegen/CodegenRecord.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package smithy4s.codegen.internals +package smithy4s.codegen import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node @@ -22,12 +22,12 @@ import software.amazon.smithy.model.node.Node import scala.jdk.CollectionConverters._ import scala.jdk.OptionConverters._ -private[internals] final case class CodegenRecord( +private[codegen] final case class CodegenRecord( namespaces: List[String], validatedNewtypes: Option[Boolean] ) -private[internals] object CodegenRecord { +private[codegen] object CodegenRecord { val METADATA_KEY = "smithy4sGenerated" diff --git a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala index 63ca5e374..2a25405f0 100644 --- a/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala +++ b/modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala @@ -21,7 +21,6 @@ import smithy4s.meta.ValidateNewtypeTrait import software.amazon.smithy.build.ProjectionTransformer import software.amazon.smithy.build.TransformContext import software.amazon.smithy.model.Model -import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.AbstractShapeBuilder import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.model.shapes.Shape @@ -30,8 +29,8 @@ import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait -import scala.jdk.CollectionConverters._ import scala.jdk.OptionConverters._ +import smithy4s.codegen.CodegenRecord class ValidatedNewtypesTransformer extends ProjectionTransformer { @@ -41,7 +40,14 @@ class ValidatedNewtypesTransformer extends ProjectionTransformer { val transformer = context.getTransformer() val model = context.getModel() - val agn = alreadyGeneratedNamespaces(model) + val agn = CodegenRecord + .recordsFromModel(model) + .flatMap(record => + record.namespaces.map(ns => + (ns, record.validatedNewtypes.getOrElse(false)) + ) + ) + .toMap val supportsValidatedNewtypes = model @@ -73,41 +79,12 @@ class ValidatedNewtypesTransformer extends ProjectionTransformer { builder.build() } - private def alreadyGeneratedNamespaces(model: Model): Map[String, Boolean] = { - def fromNode(node: Node): List[(String, Boolean)] = { - val obj = node.expectObjectNode() - val arrayNode = obj.expectArrayMember("namespaces") - val validatedNewtypes = - obj - .getBooleanMember("validatedNewtypes") - .toScala - .map(_.getValue()) - .getOrElse(false) - - arrayNode - .getElements() - .asScala - .map(_.expectStringNode().getValue()) - .map(ns => (ns, validatedNewtypes)) - .toList - } - - model - .getMetadataProperty(ValidatedNewtypesTransformer.SMITHY4S_GENERATED) - .toScala - .map( - _.expectArrayNode().getElements().asScala.toList.flatMap(fromNode).toMap - ) - .getOrElse(Map.empty) - } - } object ValidatedNewtypesTransformer { val name = "ValidatedNewtypesTransformer" private val METADATA_KEY = "smithy4sRenderValidatedNewtypes" - private val SMITHY4S_GENERATED = "smithy4sGenerated" object SupportedShape { def unapply(shape: Shape): Option[Shape] = shape match {