From 3d1ef6f62e777219cd1ed9c14bbfef384bb38b89 Mon Sep 17 00:00:00 2001 From: mitchldtn Date: Thu, 3 Oct 2024 15:21:15 -0500 Subject: [PATCH 1/4] added docs for delimited codec --- doc/endpoint/codecs.md | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/doc/endpoint/codecs.md b/doc/endpoint/codecs.md index 5337dc3998..3760932697 100644 --- a/doc/endpoint/codecs.md +++ b/doc/endpoint/codecs.md @@ -88,6 +88,89 @@ Different codec formats can be used in different contexts. When defining a path, with the `TextPlain` media type can be used. However, for bodies, any media type is allowed. For example, the input/output described by `jsonBody[T]` requires a json codec. +## Handling Delimited Path Parameters + +Tapir allows you to handle complex path parameters, such as lists of custom types separated by delimiters (e.g., commas). +This can be achieved using Codec.delimited, which facilitates the serialization and deserialization of delimited lists +within path segments. + +### Use Case +Suppose you want to define an endpoint that accepts a list of names as a comma-separated path parameter. Each name should +adhere to a specific pattern (e.g., only uppercase letters). + +### Implementation Steps: + +### 1. Define the Custom Type and Validator +Start by defining your custom type and the associated validator to enforce the desired pattern. + +```scala +import sttp.tapir._ +import sttp.tapir.generic.auto._ +import sttp.tapir.Codec +import sttp.tapir.Validator + +case class Name(value: String) + +// Validator to ensure names consist of uppercase letters only +val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") +``` + +### 2. Create Codecs for the Custom Type and Delimited List +Utilize `Codec.parsedString` for individual `Name` instances and `Codec.delimited` for handling the list. + +```scala +// Codec for single Name +given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) + .validate(nameValidator.contramap(_.value)) + +// Codec for a list of Names, delimited by commas +given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited +``` + +### 3. Define the Endpoint with Delimited Path Parameter +Incorporate the delimited codec into your endpoint definition to handle the list of names in the path. + +```scala +val getUserEndpoint = + endpoint.get + .in("user" / path[List[Name]]("id")) + .out(stringBody) +``` + +### 4. Generated OpenAPI Schema +When you generate the OpenAPI documentation for this endpoint, the schema for the `id` path parameter will +correctly reflect it as an array with the specified pattern for each item. + +```yaml +paths: + /user/{id}: + get: + operationId: getUserId + parameters: + - name: id + in: path + required: true + schema: + type: array + items: + type: string + pattern: ^[A-Z]+$ +``` + +### Explanation +- `Codec.parsedString`: Transforms a `String` into a custom type (`Name`) and vice versa. It also applies validation to +ensure each `Name` adheres to the specified pattern. +- `Codec.delimited`: Handles the serialization and deserialization of a delimited list (e.g., comma-separated) of the +custom type. By specifying `Delimited[",", Name]`, Tapir knows how to split and join the list based on the delimiter. +- Endpoint Definition: The `path[List[Name]]("id")` indicates that the id path parameter should be treated as a list of +`Name` objects, utilizing the previously defined codecs. + +### Validation +Validators play a crucial role in ensuring that each element within the delimited list meets the required criteria. In +this example, `nameValidator` ensures that each `Name` consists solely of uppercase letters. Tapir applies this validation +to each element in the list, providing robust input validation. + + ## Next Read on about [custom types](customtypes.md). From 078664b15aec69c7b65821fcacb994ba3f8b2841 Mon Sep 17 00:00:00 2001 From: mitchldtn Date: Thu, 10 Oct 2024 23:22:12 -0500 Subject: [PATCH 2/4] move delimited path parameters example to its own .md --- doc/endpoint/codecs.md | 83 ------------------------ doc/how-tos/delimited-path-parameters.md | 81 +++++++++++++++++++++++ doc/index.md | 1 + 3 files changed, 82 insertions(+), 83 deletions(-) create mode 100644 doc/how-tos/delimited-path-parameters.md diff --git a/doc/endpoint/codecs.md b/doc/endpoint/codecs.md index 3760932697..5337dc3998 100644 --- a/doc/endpoint/codecs.md +++ b/doc/endpoint/codecs.md @@ -88,89 +88,6 @@ Different codec formats can be used in different contexts. When defining a path, with the `TextPlain` media type can be used. However, for bodies, any media type is allowed. For example, the input/output described by `jsonBody[T]` requires a json codec. -## Handling Delimited Path Parameters - -Tapir allows you to handle complex path parameters, such as lists of custom types separated by delimiters (e.g., commas). -This can be achieved using Codec.delimited, which facilitates the serialization and deserialization of delimited lists -within path segments. - -### Use Case -Suppose you want to define an endpoint that accepts a list of names as a comma-separated path parameter. Each name should -adhere to a specific pattern (e.g., only uppercase letters). - -### Implementation Steps: - -### 1. Define the Custom Type and Validator -Start by defining your custom type and the associated validator to enforce the desired pattern. - -```scala -import sttp.tapir._ -import sttp.tapir.generic.auto._ -import sttp.tapir.Codec -import sttp.tapir.Validator - -case class Name(value: String) - -// Validator to ensure names consist of uppercase letters only -val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") -``` - -### 2. Create Codecs for the Custom Type and Delimited List -Utilize `Codec.parsedString` for individual `Name` instances and `Codec.delimited` for handling the list. - -```scala -// Codec for single Name -given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) - .validate(nameValidator.contramap(_.value)) - -// Codec for a list of Names, delimited by commas -given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited -``` - -### 3. Define the Endpoint with Delimited Path Parameter -Incorporate the delimited codec into your endpoint definition to handle the list of names in the path. - -```scala -val getUserEndpoint = - endpoint.get - .in("user" / path[List[Name]]("id")) - .out(stringBody) -``` - -### 4. Generated OpenAPI Schema -When you generate the OpenAPI documentation for this endpoint, the schema for the `id` path parameter will -correctly reflect it as an array with the specified pattern for each item. - -```yaml -paths: - /user/{id}: - get: - operationId: getUserId - parameters: - - name: id - in: path - required: true - schema: - type: array - items: - type: string - pattern: ^[A-Z]+$ -``` - -### Explanation -- `Codec.parsedString`: Transforms a `String` into a custom type (`Name`) and vice versa. It also applies validation to -ensure each `Name` adheres to the specified pattern. -- `Codec.delimited`: Handles the serialization and deserialization of a delimited list (e.g., comma-separated) of the -custom type. By specifying `Delimited[",", Name]`, Tapir knows how to split and join the list based on the delimiter. -- Endpoint Definition: The `path[List[Name]]("id")` indicates that the id path parameter should be treated as a list of -`Name` objects, utilizing the previously defined codecs. - -### Validation -Validators play a crucial role in ensuring that each element within the delimited list meets the required criteria. In -this example, `nameValidator` ensures that each `Name` consists solely of uppercase letters. Tapir applies this validation -to each element in the list, providing robust input validation. - - ## Next Read on about [custom types](customtypes.md). diff --git a/doc/how-tos/delimited-path-parameters.md b/doc/how-tos/delimited-path-parameters.md new file mode 100644 index 0000000000..615419d22f --- /dev/null +++ b/doc/how-tos/delimited-path-parameters.md @@ -0,0 +1,81 @@ +## Handling Delimited Path Parameters + +Tapir allows you to handle complex path parameters, such as lists of custom types separated by delimiters (e.g., commas). +This can be achieved using `Codec.delimited`, which facilitates the serialization and deserialization of delimited lists +within path segments. + +### Use Case +Suppose you want to define an endpoint that accepts a list of names as a comma-separated path parameter. Each name should +adhere to a specific pattern (e.g., only uppercase letters). + +### Implementation Steps: + +### 1. Define the Custom Type and Validator +Start by defining your custom type and the associated validator to enforce the desired pattern. + +```scala mdoc:compile-only +import sttp.tapir._ +import sttp.tapir.generic.auto._ +import sttp.tapir.Codec +import sttp.tapir.Validator + +case class Name(value: String) + +// Validator to ensure names consist of uppercase letters only +val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") +``` + +### 2. Create Codecs for the Custom Type and Delimited List +Utilize `Codec.parsedString` for individual `Name` instances and `Codec.delimited` for handling the list. + +```scala mdoc:compile-only +// Codec for single Name +given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) + .validate(nameValidator.contramap(_.value)) + +// Codec for a list of Names, delimited by commas +given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited +``` + +### 3. Define the Endpoint with Delimited Path Parameter +Incorporate the delimited codec into your endpoint definition to handle the list of names in the path. + +```scala mdoc:compile-only +val getUserEndpoint = + endpoint.get + .in("user" / path[List[Name]]("id")) + .out(stringBody) +``` + +### 4. Generated OpenAPI Schema +When you generate the OpenAPI documentation for this endpoint, the schema for the `id` path parameter will +correctly reflect it as an array with the specified pattern for each item. + +```yaml +paths: + /user/{id}: + get: + operationId: getUserId + parameters: + - name: id + in: path + required: true + schema: + type: array + items: + type: string + pattern: ^[A-Z]+$ +``` + +### Explanation +- `Codec.parsedString`: Transforms a `String` into a custom type (`Name`) and vice versa. It also applies validation to + ensure each `Name` adheres to the specified pattern. +- `Codec.delimited`: Handles the serialization and deserialization of a delimited list (e.g., comma-separated) of the + custom type. By specifying `Delimited[",", Name]`, Tapir knows how to split and join the list based on the delimiter. +- Endpoint Definition: The `path[List[Name]]("id")` indicates that the id path parameter should be treated as a list of + `Name` objects, utilizing the previously defined codecs. + +### Validation +Validators play a crucial role in ensuring that each element within the delimited list meets the required criteria. In +this example, `nameValidator` ensures that each `Name` consists solely of uppercase letters. Tapir applies this validation +to each element in the list, providing robust input validation. \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 9fe3f0bbe4..b57ef23384 100644 --- a/doc/index.md +++ b/doc/index.md @@ -156,6 +156,7 @@ sttp is a family of Scala HTTP-related projects, and currently includes: examples external + how-tos/delimited-path-parameters .. toctree:: :maxdepth: 2 From b251615c270de12bdbf0398cf4104cd62e874052 Mon Sep 17 00:00:00 2001 From: mitchldtn Date: Fri, 11 Oct 2024 00:17:20 -0500 Subject: [PATCH 3/4] refactor --- doc/how-tos/delimited-path-parameters.md | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/how-tos/delimited-path-parameters.md b/doc/how-tos/delimited-path-parameters.md index 615419d22f..228dfd2fc8 100644 --- a/doc/how-tos/delimited-path-parameters.md +++ b/doc/how-tos/delimited-path-parameters.md @@ -13,11 +13,13 @@ adhere to a specific pattern (e.g., only uppercase letters). ### 1. Define the Custom Type and Validator Start by defining your custom type and the associated validator to enforce the desired pattern. -```scala mdoc:compile-only +```scala import sttp.tapir._ import sttp.tapir.generic.auto._ import sttp.tapir.Codec import sttp.tapir.Validator +import sttp.tapir.CodecFormat.TextPlain +import sttp.tapir.model.Delimited case class Name(value: String) @@ -28,7 +30,7 @@ val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") ### 2. Create Codecs for the Custom Type and Delimited List Utilize `Codec.parsedString` for individual `Name` instances and `Codec.delimited` for handling the list. -```scala mdoc:compile-only +```scala // Codec for single Name given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) .validate(nameValidator.contramap(_.value)) @@ -41,9 +43,28 @@ given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited Incorporate the delimited codec into your endpoint definition to handle the list of names in the path. ```scala mdoc:compile-only +import sttp.tapir._ +import sttp.tapir.generic.auto._ +import sttp.tapir.Codec +import sttp.tapir.Validator +import sttp.tapir.CodecFormat.TextPlain +import sttp.tapir.model.Delimited + +case class Name(value: String) + +// Validator to ensure names consist of uppercase letters only +val nameValidator: Validator[String] = Validator.pattern("^[A-Z]+$") + +// Codec for single Name +given Codec[String, Name, TextPlain] = Codec.parsedString(Name.apply) + .validate(nameValidator.contramap(_.value)) + +// Codec for a list of Names, delimited by commas +given Codec[String, Delimited[",", Name], TextPlain] = Codec.delimited + val getUserEndpoint = endpoint.get - .in("user" / path[List[Name]]("id")) + .in("user" / path[Delimited[",", Name]]("id")) .out(stringBody) ``` From 6126840e53770eea3400638e5290e344aafbbfdd Mon Sep 17 00:00:00 2001 From: adamw Date: Fri, 11 Oct 2024 09:39:46 +0200 Subject: [PATCH 4/4] Adjust headers, compile example --- doc/how-tos/delimited-path-parameters.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/how-tos/delimited-path-parameters.md b/doc/how-tos/delimited-path-parameters.md index 228dfd2fc8..9e975e2f8e 100644 --- a/doc/how-tos/delimited-path-parameters.md +++ b/doc/how-tos/delimited-path-parameters.md @@ -1,19 +1,20 @@ -## Handling Delimited Path Parameters +# Handling Delimited Path Parameters Tapir allows you to handle complex path parameters, such as lists of custom types separated by delimiters (e.g., commas). This can be achieved using `Codec.delimited`, which facilitates the serialization and deserialization of delimited lists within path segments. -### Use Case +## Use Case + Suppose you want to define an endpoint that accepts a list of names as a comma-separated path parameter. Each name should adhere to a specific pattern (e.g., only uppercase letters). -### Implementation Steps: +## Implementation Steps: ### 1. Define the Custom Type and Validator Start by defining your custom type and the associated validator to enforce the desired pattern. -```scala +```scala mdoc:compile-only import sttp.tapir._ import sttp.tapir.generic.auto._ import sttp.tapir.Codec @@ -88,7 +89,7 @@ paths: pattern: ^[A-Z]+$ ``` -### Explanation +## Explanation - `Codec.parsedString`: Transforms a `String` into a custom type (`Name`) and vice versa. It also applies validation to ensure each `Name` adheres to the specified pattern. - `Codec.delimited`: Handles the serialization and deserialization of a delimited list (e.g., comma-separated) of the @@ -96,7 +97,7 @@ paths: - Endpoint Definition: The `path[List[Name]]("id")` indicates that the id path parameter should be treated as a list of `Name` objects, utilizing the previously defined codecs. -### Validation +## Validation Validators play a crucial role in ensuring that each element within the delimited list meets the required criteria. In this example, `nameValidator` ensures that each `Name` consists solely of uppercase letters. Tapir applies this validation to each element in the list, providing robust input validation. \ No newline at end of file