Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
stanislav-chetvertkov opened this issue Apr 21, 2024 · 6 comments
Labels
💎 Bounty enhancement New feature or request

Comments

@stanislav-chetvertkov
Copy link

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

@stanislav-chetvertkov stanislav-chetvertkov added the enhancement New feature or request label Apr 21, 2024
@987Nabil
Copy link
Contributor

987Nabil commented May 5, 2024

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
Copy link
Member

jdegoes commented Jun 5, 2024

/bounty $250

Copy link

algora-pbc bot commented Jun 5, 2024

💎 $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:

  • 🔴 Livestream on Algora TV while solving this bounty & earn $200 upon merge! Make sure to have your camera and microphone on. Comment /livestream once live

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
Copy link
Author

stanislav-chetvertkov commented Jun 8, 2024

/attempt #2786

@stanislav-chetvertkov
Copy link
Author

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
Copy link
Author

might be a bug - I was able to reproduce it in a simplified form zio/zio-schema#691

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💎 Bounty enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants