zio / zio-http

A next-generation Scala framework for building scalable, correct, and efficient HTTP clients and servers
https://zio.dev/zio-http
Apache License 2.0
748 stars 380 forks source link

OpenAPI spec to endpoint generator does not recognize field validation #2786

Open stanislav-chetvertkov opened 2 months ago

stanislav-chetvertkov commented 2 months ago

Is your feature request related to a problem? Please describe.

The code that gets generated based on an openapi spec does not take into account additional field validations, for example, for the following definition

 "schemas": {
      "Pet": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string",
            "minLength": 3
          },
          "tag": {
            "type": "string"
          }
        }
      }
}

and the endpoint using it

  "paths": {
      "post": {
        "summary": "Create a pet",
        "operationId": "createPets",
        "tags": [
          "pets"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Pet"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Null response"
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },

the case class that gets generated looks like this

case class Pet(
 id: Long,
 name: String,
 tag: Option[String]
)

running a server with a dummy implementation

val httpApp = Pets.createPets.implement(Handler.fromFunctionZIO(pet => {ZIO.logInfo("Pet: " + pet.toString)})).toHttpApp
Server.serve(httpApp)

and issuing a POST request against /pets with payload that should fail validation (the name size should be at least 3 character long)

{
    "id":1,
    "name": "n"
}

gets processed successfully instead

Describe the solution you'd like

changing the generated case class to include zio-schema validations seems to work with no changes to other parts of the generated code

case class Pet(
 id: Long,
 @validate(Validation.minLength(3))
 name: String,
 tag: Option[String]
)

in the corresponding endpoint definition

val createPets=Endpoint(Method.POST / "pets")
  .in[Pet]
  .out[Unit](status = Status.Created)
}

.in[Pet] already responsible for validating input against the schema, and after adding the validation annotation I'm now getting 400 response from the server, However the response body is empty which could be a problem.

Describe alternatives you've considered An alternative could be to generate endpoints that return Either[ValidationError, T] for users to implement and adjust format of validation errors if needed

Additional context the full example https://github.com/stanislav-chetvertkov/zio-http-gen-example

987Nabil commented 2 months ago

Your described solution should be the right way. However, I am not sure that all Open API validations can be mapped to current schema validations. It might need some extension of zio schema. This would need to be checked

jdegoes commented 1 month ago

/bounty $250

algora-pbc[bot] commented 1 month ago

💎 $250 bounty • ZIO

Steps to solve:

  1. Start working: Comment /attempt #2786 with your implementation plan
  2. Submit work: Create a pull request including /claim #2786 in the PR body to claim the bounty
  3. Receive payment: 100% of the bounty is received 2-5 days post-reward. Make sure you are eligible for payouts

Additional opportunities:

Thank you for contributing to zio/zio-http!

Add a bountyShare on socials

Attempt Started (GMT+0) Solution
🔴 @stanislav-chetvertkov Jun 8, 2024, 11:39:56 AM WIP
stanislav-chetvertkov commented 1 month ago

/attempt #2786

stanislav-chetvertkov commented 4 weeks ago

I got a bit stuck with adding more validation parameters when parsing openapi schemas

@nowarn("msg=possible missing interpolator")
private[openapi] case class SerializableJsonSchema(
  @fieldName("$ref") ref: Option[String] = None,
  @fieldName("type") schemaType: Option[TypeOrTypes] = None,
  format: Option[String] = None,
  oneOf: Option[Chunk[SerializableJsonSchema]] = None,
  allOf: Option[Chunk[SerializableJsonSchema]] = None,
  anyOf: Option[Chunk[SerializableJsonSchema]] = None,
  @fieldName("enum") enumValues: Option[Chunk[Json]] = None,
  properties: Option[Map[String, SerializableJsonSchema]] = None,
  additionalProperties: Option[BoolOrSchema] = None,
  required: Option[Chunk[String]] = None,
  items: Option[SerializableJsonSchema] = None,
  nullable: Option[Boolean] = None,
  description: Option[String] = None,
  example: Option[Json] = None,
  examples: Option[Chunk[Json]] = None,
  discriminator: Option[OpenAPI.Discriminator] = None,
  deprecated: Option[Boolean] = None,
  contentEncoding: Option[String] = None,
  contentMediaType: Option[String] = None,
  default: Option[Json] = None,
  pattern: Option[String] = None,
  minimum: Option[Double] = None,
  maximum: Option[Double] = None,
)

First, I added minimum: Option[Double] = None, and everything worked and propagated without issues. However, when I added the second field maximum: Option[Double] = None, I started getting the following error:

  .components.schemas.Pet(none of the subtypes could decode the data)

It looks like the error is coming from zio-schema and has something to do with the 22 field restriction, with the last added field being the 23rd.

I'll try to look for a workaround, maybe there is an hlist based approach or schema declarations could be rearranged.

stanislav-chetvertkov commented 4 weeks ago

might be a bug - I was able to reproduce it in a simplified form https://github.com/zio/zio-schema/issues/691

987Nabil commented 1 week ago
987Nabil commented 1 week ago

I am done with the impl. But another fix for zio-schema is needed https://github.com/zio/zio-schema/pull/698