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

Using custom AJV keywords results in openAPI error #794

Open
2 tasks done
SeanReece opened this issue Mar 21, 2024 · 0 comments
Open
2 tasks done

Using custom AJV keywords results in openAPI error #794

SeanReece opened this issue Mar 21, 2024 · 0 comments

Comments

@SeanReece
Copy link

SeanReece commented Mar 21, 2024

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.26.2

Plugin version

8.14.0

Node.js version

20

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

14.3

Description

When using custom keywords with AJV, those keywords are added to the resulting swagger/openAPI definition which results in a "structural error" being reported in the swagger editor.

Results in open api spec giving this validation error

image

Normally I wouldn't care about an error like this but we have teams attempting to generate Java clients using swagger generator and it throws an error because of this structure error.

Steps to Reproduce

Example

import Fastify from 'fastify'
import fastifySwagger from '@fastify/swagger'

// Any custom keyword
const evenKeyword: FuncKeywordDefinition = {
  keyword: 'even',
  type: 'number',
  schemaType: 'boolean',
  compile: (schema, parentSchema) => {
    return (data) => {
      return data % 2 === 0
    }
  },
}

// Add our custom keyword to fastify
const fastify = Fastify({
  logger: true,
  ajv: {
    customOptions: {
      keywords: [evenKeyword]
    }
  }
})

// Register @fastify/swagger
await fastify.register(fastifySwagger, { openapi: { ... } })

// Define our schema using our custom keyword
const schema = {
  body: {
    type: 'object',
    properties: {
      value: { type: 'number', even: true },
    },
  },
}

// Declare our route using schema
fastify.post('/', { schema }, function (request, reply) {
  reply.send({ hello: 'world' })
})

// Run the server!
fastify.listen({ port: 3000 }, function (err, address) {...})

The resulting swagger json then contains the custom keyword:

Trimmed for brevity

{
  "openapi": "3.0.3",
  "paths": {
    "/": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "value": {
                    "type": "number",
                    "even": true                <-- this is not a valid openapi JSON Schema property
                  }
                }
              }
            }
          }
        }
      }
    }
  },
}

Expected Behavior

I would expect @fastify/swagger to generate valid openAPI spec regardless of AJV keywords used, but I understand this may be a difficult problem considering AJV keywords might need to be added which add valid openAPI keywords (because of openAPIs "extended" JSON Schema spec I think this is a non-trivial issue)

I'm thinking about solving it this way using the "transform" function to remove any invalid keywords, but this seems fragile and inefficient. Not sure if there is something better

  await fastify.register(fastifySwagger, {
    openapi: {
      transform(transform) {
        if (transform.schema && !transform.schema.hide) {
          // Deep clone original schema (unsure if necessary)
          transform.schema = structuredClone(transform.schema)
          // TODO do this in a more flexible way
          delete transform.schema.body.properties.value.even
        }

        return transform
      },
    },
  })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant