GithubHelp home page GithubHelp logo

seriousme / fastify-openapi-glue Goto Github PK

View Code? Open in Web Editor NEW
193.0 6.0 33.0 2.84 MB

A plugin for the Fastify webserver to autogenerate a Fastify configuration based on a OpenApi(v2/v3) specification.

License: MIT License

JavaScript 100.00%
swagger fastify fastify-plugin openapi openapi3 openapi3-1 openapi-codegen javascript apis openapi-specification

fastify-openapi-glue's People

Contributors

bluebrown avatar dependabot-preview[bot] avatar dependabot[bot] avatar erasmuswill avatar greenkeeper[bot] avatar greenkeeperio-bot avatar guillaumedeconinck avatar ivan-tymoshenko avatar justinsc avatar kutyepov avatar lundibundi avatar mhamann avatar motorro avatar schulz3000 avatar seriousme avatar zekth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

fastify-openapi-glue's Issues

Property names are missing in error text

From version v2.4.1 onwards, property names are missing in the response body. This is due to a breaking change in ajv@8:

property name is removed from "propertyName" keyword error message (it is still available in error.params.propertyName).
more details here

This issue was introduced in #190 where ajv@7 was upgraded to avj@8. Specific example of missing property name can be seen in updated test in that PR.


I am happy to raise a PR to fix this issue, but need some guidance on what would be the best approach to tackle it.
So far, I found an approach that works.
If the following is added to setValidatorCompiler in index.js:

  instance.setSchemaErrorFormatter(function (errors, dataVar) {
    return new Error(ajv.errorsText(errors))
  })

then the error text will include property name.
However, I am not sure if this is the right place to do so. Potentially, this might need to be addressed in fastify itself since it has its own ajv instance and therefore it might have the same issue. Fastify@3 is using ajv@6, so their schemas work fine.

Inconsistencies with validation and additional properties

Hey,

I've recently noticed that the Request body schema validation isnt working as expected (at least for me).

Additional properties are going through as opposed to being ignored and the noAdditional prop doesnt have any effect in my code.
Type validation isnt working either - i can pass whatever value i want to my properties and it will completely ignore them.

I'm on the latest version

i will be adding an example/replication as soon as i'm able to tomorrow but just wanted to post this just in case it's a known issue.

Swagger Docs not showing when using openapi-glue routes.

Hello! I have defined my routes in an openapi v2 json that looks something like this
{ "swagger": "2.0", "info": { "title": "Posts API", "description": "Post swagger definition for fastify", "termsOfService": "http://swagger.io/terms/", "contact": { "name": "API Support", "url": "http://www.swagger.io/support", "email": "[email protected]" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, "version": "2" }, "tags":[ { "name": "posts", "description":"Posts" } ], "schemes": ["http"], "paths": { "/": { "get": { "summary": "Returns array with all posts", "description": "", "operationId": "getAllPosts", "consumes": ["application/json"], "produces": ["application/json"], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Post" } } } } } }, "/add": { "post": { "summary": "Creates a new post", "description": "", "operationId": "createPost", "consumes": ["application/json"], "produces": ["application/json"], "responses": { "201": { "description": "created", "schema": { "$ref": "#/definitions/Post" } } } } } }, "definitions": { "Post": { "type": "object", "required": ["id", "user", "title"], "properties": { "id": { "type": "string" }, "user": { "type": "string" }, "title": { "type": "string", "example": "titulo de ejemplo" } }, "xml": { "name": "Post" } } } }

The problem is that my instance of swagger is not showing in the docs the paths that I created. (They work fine in code, they are being called sucessfully from postman / insomnia) but there is no way to see them on the swagger instance.
Its worth noting that when I create and register a route manually it is shown correctly on the swagger docs.

This is my config:

void fastify.register(openapiGlue, { specification: openapiJson.default, service: new Service(), prefix: "v1", noAdditional: true, ajvOptions: { formats: { "custom-format": /\d{2}-\d{4}/ } } })

void fastify.register(swagger, { routePrefix: "/docs", swagger: { info: { title: "Test swagger", description: "Testing the Fastify swagger API", version: "0.1.0", }, externalDocs: { url: "https://swagger.io", description: "Find more info here", }, host: "localhost", schemes: ["http"], consumes: ["application/json"], produces: ["application/json"], }, uiConfig: { docExpansion: "full", deepLinking: false, }, staticCSP: true, transformStaticCSP: (header) => header, exposeRoute: true, });

Cannot find module 'ajv/dist/core'

Hello!
It seems that there is a problem with a dependency of ajv, when using openapi-glue v2.6.1 it throws an error whenever I want to run the project. Here is the stack:

`Error: Cannot find module 'ajv/dist/core'
Require stack:

  • /proyectos/txManagerApi/txmanagerApi/node_modules/ajv-draft-04/dist/index.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/@seriousme/openapi-schema-validator/index.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/fastify-openapi-glue/lib/parser.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/fastify-openapi-glue/index.js
  • /proyectos/txManagerApi/txmanagerApi/dist/app.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/util.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/start.js
  • /proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/cli.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15)
    at Function.Module._load (internal/modules/cjs/loader.js:746:27)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at Object. (/proyectos/txManagerApi/txmanagerApi/node_modules/ajv-draft-04/dist/index.js:4:16)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19) {
    code: 'MODULE_NOT_FOUND',
    requireStack: [
    '/proyectos/txManagerApi/txmanagerApi/node_modules/ajv-draft-04/dist/index.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/@seriousme/openapi-schema-validator/index.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/fastify-openapi-glue/lib/parser.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/fastify-openapi-glue/index.js',
    '/proyectos/txManagerApi/txmanagerApi/dist/app.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/util.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/start.js',
    '/proyectos/txManagerApi/txmanagerApi/node_modules/fastify-cli/cli.js'
    ]
    }`

It works fine on the 2.5 version of the openapi-glue plugin.

need a slash symbol in index.js

index.js in generated project

service:${__dirname}service.js,
should be
service:${__dirname}/service.js,

index.js

// Fastify plugin autogenerated by fastify-openapi-glue
const openapiGlue = require("fastify-openapi-glue");

const options = {
  specification: `${__dirname}/openApi.json`,
  service: `${__dirname}service.js`,
  securityHandlers: `${__dirname}security.js`
};

module.exports = async function(fastify, opts) {
  fastify.register(openapiGlue, options);
};

'this' is losing it's scope in service.js

if I call a sub method in my service.js file from the method that was requested by fastify it could not be found.

Sample:

async addPet(req, reply) {
    console.log("addPet", req.params);
    this.createPet(req.query.name);
    return { key: "value" };
  }

createPet(name) {
console.log(`Pet ${name} created`);
}

Getting a HTTP 500 error that says this.createPet method not found. Looks like 'this' is losing it's scope.

numbers are converted to string instead of throwing a schema validation error

Hi, I noticed that when posting a number, its converted to string. I did expect a validation error.

I use the below OpenAPI spec:

{
  "components": {
    "schemas": {
      "item": {
        "properties": {
          "msg": {
            "type": "string"
          }
        },
        "type": "object"
      }
    }
  },
  "info": {
    "title": "API Title",
    "version": "1.0"
  },
  "openapi": "3.0.2",
  "paths": {
    "/test": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/item"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/item"
                }
              }
            },
            "description": "OK"
          }
        }
      }
    }
  }
}

The I generate the project and run it:

npx github:seriousme/fastify-openapi-glue test.json 
cd generatedProject/
npm i 
npm run dev

Now I hit the server with an http request:

// use vscode extension: humao.rest-client
POST /test HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=utf-8

{
  "msg": 1
}

and I get this response back:

HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 11
Date: Tue, 21 Jun 2022 14:36:35 GMT
Connection: close

{
  "msg": "1"
}

The post handler returns the body as is:

async postTest(req, reply) {
   return req.body;
}

Wrong dynamic import in UPGRADING.md

To get the default export from a dynamic import you need to use

const {default: openapiGlue} = await import('fastify-openapi-glue');

instead of

const openapiGlue = await import("fastify-openapi-glue");

Error: querystring must be object

I am using openapi specification to generate a new server. I have the following settings:
...
/dnszone:
get:
tags:
- dnszone
summary: Searches for a certain DNS zone by nameserver name or IP address
description: Provide Type of DNS record and Name in parameters
operationId: dnszoneGET
parameters:
- $ref: '#/components/parameters/StatusDNSZoneParams'
responses:
"200":
...
The value of the StatusDNSZoneParams is:
parameters:
StatusDNSZoneParams:
name: StatusDNSZoneParams
in: query
description: A list of params for an existing DNS zone.
required: true
style: form
explode: false
schema:
$ref: '#/components/schemas/StatusDNSZoneRequestObj'

When I try to call:
curl -X 'GET'
'http://localhost:3000/dnszone?StatusDNSZoneParams=DNSZONE,string,EXTENDED,string'
-H 'accept: application/json'
I receive the error:
{"statusCode":400,"error":"Bad Request","message":"querystring must be object"}

Why would this be a problem?

I tried to change the object serialization by changing explode to true, then tried:
curl -X 'GET' 'http://localhost:3000/dnszone?DNSZONE=string&EXTENDED=string' -H 'accept: application/json'

I get the error:
{"statusCode":400,"error":"Bad Request","message":"querystring must have required property 'StatusDNSZoneParams'"}

Would you please explain the issue here? thanks in advance!

'byte' format validation failure

I'm finding that the 'byte' format validation in the new 'oai-formats' will fail after the first validation.

It appears that the Regex object created for each invocation is retaining the 'lastIndex' from the previous validation.

This issue can be replicated by duplicating line 22 of test/test-oai-formats.js

Migration to ESM per May 2021

Dear users,

Node.js will end LTS support on Node 10 per May 2021

This means that Node 12.x will be the oldest supported version by then. ECMA Script Modules are supported as of Node.js 12.17.

Therefore my intent is to migrate fastify-openapi-gluecto ESM per May 2021 by releasing a new major version (3.0.0). I have already migrated the code base in the migrate-to-esm branch.

I might release another 2.4.x version of fastify-openapi-glue before May 2021 e.g. if security issues popup in the module of one of its dependencies. However: if you plan to submit a PR on fastify-openapi-glue please create an issue first to discuss !

In the mean time: please make sure you stay reasonably current on the Node.js version you use !

Kind regards,
Hans

An in-range update of tap is breaking the build 🚨

The devDependency tap was updated from 12.6.1 to 12.6.2.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

tap is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 3 commits.

  • fa3f6e2 12.6.2
  • b8be134 new computer, phantom png changes
  • 3833522 update js-yaml and nyc for vuln advisories

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Response objects incorrect (?) validation with null fields

Response objects with $ref to a model with null fields. Im getting the current error:

TypeError: Cannot read properties of null (reading 'id')
    at $arr$mainvisitsitemsi0meeting (eval at build

This is my response object:
Screenshot 2022-06-10 at 11 55 30

This is what I'm getting back from the database and trying to return to the client:

    id: 'd64cc7a8-c723-4c4d-9d9c-58dae718074c',
    ...
    meetingId: null,
    meeting: null,

Because meeting is null, i didn't expected it to be validated since it's not required in the schema.

Allow custom resolvers for operation id or add a list support for services

Hi, I think it would be good if we could split the code into multiple services. Currently this module only supports a single service.

I want to be able to do something like this:

fastify.register(openApiGlue, {
  prefix: 'v1',
  specification: openapiSpec,
  services: [
    new FooService(),
    new BarService()
  ],
});

So I think it would be good to make this by default or allow an option to enable it. Perhaps something like this:

async function generateRoutes(routesInstance, routesOpts) {
    const service = opts.services.find(svc => item.operationId in svc)
    ...

Or maybe it could be a more flexible system, where people can provide their own resolver function and do whatever they wish to add the handler to the item.

async function generateRoutes(routesInstance, routesOpts) {
  const resolver = resolverFactory(routesInstance, opts)
  config.routes.forEach((item) => {
    const handler = resolver(item, routesOpts)
    item.handler = handler || notImplemented(item.operationId)

Then we could supply, our own resolvers, if required. For example:

function listResolverFactory(routesInstance, pluginOpts) {
  const services = pluginOpts.services;
  return function (item, routesOpts) {
    const service = services.find(svc => item.operationId in svc)
    return service && service[item.operationId].bind(service);
  }
}

Or if no resolver was provided, the default resolver could be used:

function defaultResolverFactory(routesInstance, pluginOpts) {
  // check is the service property exists and is valid
  const service = pluginOpts.service;
  checkObject(service, 'service');
  // return the resolver function
  return function (item, routesOpts) {
    const response = item.schema.response;
    if (item.operationId in service) {
      routesInstance.log.debug(`service has '${item.operationId}'`);
      return service[item.operationId].bind(service);
    }
  }
}

const resolverFactory = opts.resolverFactory || defaultResolverFactory

So we could use the package like this:

fastify.register(openApiGlue, {
  prefix: 'v1',
  specification: openapiSpec,
  resolverFactory: listResolverFactory,
  services: [
    new FooService(),
    new BarService()
  ],
});

Give option to allow additional properties

Hello, I have a need to allow additional properties that are not in the model definition to be returned, and the current setting is hardcoded and conflicts with the OpenApi specification which allows additional properties: https://github.com/OAI/OpenAPI-Specification/blame/3.0.2/versions/3.0.2.md#L2305

The following defaults could be made customizable:

  const ajv = new Ajv({
    // the fastify defaults
    removeAdditional: true,  <--- perhaps this needs to be false to match the openapi spec
    useDefaults: true,
    coerceTypes: true
  });

ref: https://github.com/seriousme/fastify-openapi-glue/blob/master/index.js#L53

I have two use cases which involve fetching related models that are not defined in the source model:

  1. return a project and its owner. Model defines userId, however, the getProjects api has an option to specify related models to fetch eagerly, like GET /projects?include=user.
  2. load a project and its inner tasks eagerly. The task model defines projectId. I tried defining the project property in the Task model but in practice it causes an infinite loop because a project is already defining tasks as a child props. So it would end up walking this path:
    project -> tasks -> task[0] -> project -> tasks -> task[0] -> project...etc.

Ideally it would be good to decide who much of the features we want to enable in fastify-openapi-glue, request validation? response validation? type coersion? allow additional properties...etc.

Extra query parameters are accepted in query

Hello, i am trying to use openapi-glue in a project but i have faced a problem that i would like to know if it should work like this. I have a DELETE endpoint where i can send the ids of the agents i want to delete as query parameters. For example:
/groups/16705c05-3f5c-4e02-95a2-6f3f551c620b/agents?where[id][inq]=278....
Here i have the specification for the following endpoint:

  delete:
    summary: Remove multiple agents from group
    description: Remove multiple agents from selection group.
    operationId: removeMultipleAgentsFromGroup
    tags:
      - Agents
    parameters:
      - name: id
        description: Group ID
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: where
        in: query
        description: Items to filter
        # style: deepObject
        explode: false
        schema:
          type: object
          additionalProperties: false
          properties:
            id:
              type: object
              additionalProperties: false
              properties:
                inq:
                  type: array
                  items:
                    type: string
    responses:
      200:
        description: Agents part of a group.
        content:
          application/json:
            schema:
              type: object
              properties:
                agents:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                      priority:
                        type: integer
                count:
                  type: integer
      401:
        $ref: 'errors.yaml#/components/responses/Unauthorized'
      404:
        $ref: 'errors.yaml#/components/responses/NotFound'
      500:
        $ref: 'errors.yaml#/components/responses/InternalServer'

The problems that i am having are two, on the one hand, i had to add explode false to avoid the parser to parse only the schema of the query parameter (function parseQueryString in openapi-glue). if i don't add it, i don't recieve any information past the openapi validation . The thing is that i want to add style: deepObject which it says that by default sets explode to true, so it is contradictory. (documentation of openapi about style: https://spec.openapis.org/oas/v3.1.0#style-examples )Is this supposed to work like this?

On the other hand, i would like openapi to stop all the queries that have extra query parameters apart from where. For example, if i send the following request /groups/16705c05-3f5c-4e02-95a2-6f3f551c620b/agents?blabla[id][inq]=278, openapi does not stop this as a bad request. Is there any way to add a additionalProperties: false to the query?

The configuration that i have set for ajv is coerceTypes: 'array' and i have also tried with removeAdditional: false, but did not get expected result.

Thank you for your time

Project lint and code conventions

Hello there πŸ‘‹

First, thank you for the work on this module!

I'm planning on contributing on this project for 2-3 things if possible, but from what I see, there aren't yet any linting or code conventions, which increases the friction for starting contributing.

Usually, I have eslint, prettier and editorconfig helping to keep the code base consistent. I was wondering if you agree, and would like, to have these tools added in your project ?

A good enough article on the setup of this. Note that I can do it myself and submit a PR if you want.

Have a nice day!

Support security schemas

given this openapi

openapi: "3.0.0"
info:
  title: API
  version: 1.0.0
paths:
  /:
    get:
      operationId: getVersion
      summary: Show API Version
      parameters:
        - name: authorization
          in: header
          schema:
            type: string
          required: true
      security:
        - FirebaseToken: []
components:
  securitySchemes:
    FirebaseToken:
      type: http
      scheme: bearer
      bearerFormat: bearer

I expect that user cannot access endpoint without providing authorization header

Secure select routes

Is it possible to configure select routes with preHandler methods? This will enable us to secure specific routes that require authenticated users or operations that require specific roles or permissions.

This also leads to the following question. Does fastify-openapi-glue play nice with other plugins that hook into the routes lifecycle?

Example:

    fastify.route({
      method: 'POST',
      url: '/auth-multiple',
      preHandler: fastify.auth([
        fastify.asyncVerifyJWTandLevel,
        fastify.asyncVerifyUserAndPassword
      ]),
      handler: (req, reply) => {
        req.log.info('Auth route')
        reply.send({ hello: 'world' })
      }
    })

Operation overrides global security with empty array

Hello !

I noticed that when an operation overrides the global security with an empty array, thus specifying that no security should be applied on this operation, it still somehow tries to validate the access to the route (and throws an error).

More specifically, and according to https://swagger.io/docs/specification/authentication/, if an operation has (e.g.)

...
security:
  - ApiKeyAuth: []
  - OAuth2:
      - read
      - write
paths:
  /ping:
    get:
      security: []   # No security

The route should be accessible publicly.

Currently, from what I see, it throws the default error. From what I checked, I think the check here should additionally check if the schemes array is empty or not. Doing so will bypass the security when the array is empty.

if (!schemes ||  schemes.length === 0) {

What do you think ? I'm open to do a MR for this

This could be seen as a breaking change, as if some people using the lib have "badly" configured security rules in their Open API specs and were happy with this default handler, it will suddenly change "badly configured" protected routes to unprotected routes

Support async service function

The value of options.service can be passed in as a function which will be executed during import:

fastify.register(require("fastify-openapi-glue"), {
  specification: `${__dirname}/petstore-swagger.v2.json`,
  service: () => return new Service()
});

Currently only synchronous functions are allowed. Adding support for async functions would give service implementors more flexibility:

fastify.register(require("fastify-openapi-glue"), {
  specification: `${__dirname}/petstore-swagger.v2.json`,
  service: async () => {
    const dataProvider = await DataStore.create();
    return new Service(dataProvider);
  }
});

Ability to specify multiple services

Hello, thanks for this nice plugin! Would it be possible to support passing of an array or map of services? This would allow us to organize functionality into multiple classes that represent different domains. An example would be UserService for user crud operations and, RoleService for the role specific operations...etc. Another example is a shopping cart application with catalog, cart and checkout services. It would be cumbersome to add all in one file.

Error starting glued server due to suspect schema error

Hi,
I struggled a bit to get fastify running with openapi glue.

If I start the fastify server I get following error:
FST_ERR_SCH_BUILD: Failed building the schema for POST: /MyPath, due error undefined unsupported

I created a minimum repro with the attached swagger file:

  • MySwagger.txt (Please rename file after download to MySwagger.yaml. Github is not supporting upload of yaml files.)
  • npx github:seriousme/fastify-openapi-glue MySwagger.yaml
  • cd generatedProject
  • npm install
  • npm run start
    -> results in described error

Maybe something is wrong with my swagger file but the online swagger editor mentioned no errors.

Can someone give me a hint what is going wrong here?

Does not support servers/url option in openApi v3

I noticed that the generated routes ignore the servers/url parameter (this is the equivalent of basePath in openApi v2) defined in the specs yaml file for openApi v3.

E.g.

openapi: '3.0.0'
servers:
  - url: /api/v3
paths:
  /info:
    get:
    ....

Expected:
Route generated with path /api/v3/info

Actual:
Route generated with path /info

Just wanted to check if this is intended, or just not yet supported.

The 1.7.1 tag doesn't support fastify 3

current workaround is to install the dependency via the github handle

npm install seriousme/fastify-openapi-glue

using this on fastify 3 will invoke an error on instance.setSchemaCompiler on index.js

besides the above. love the library! thank you for your work!

"PARAMETERS method is not supported!" when using cross-method parameters

According to OpenAPI 3 the following definition should be valid:

...
paths:
  /users/{id}:
    parameters:
      # definition of parameters for all methods of this path, e.g.
      - name: id
        in: path
        required: true
    get:
      # definition of a get request
      ...
    post:
      # definition of a post request
      ...

But when the parameters key of a Path Object is used, it seems to be handled like the get, post, etc. keys, so it is used as a definition of an additional method here.

Error: PARAMETERS method is not supported!\n                     
    at Object.route (/path/to/project/node_modules/fastify/lib/route.js:118:15)\n                     
     at Object._route [as route] (/path/to/project/node_modules/fastify/fastify.js:162:27)\n                     
     at config.routes.forEach.item (/path/to/project/node_modules/fastify-openapi-glue/index.js:80:22)\n                     
     at Array.forEach (<anonymous>)\n                     
     at generateRoutes (/path/to/project/node_modules/fastify-openapi-glue/index.js:67:19)\n                     
     at Plugin.exec (/path/to/project/node_modules/avvio/plugin.js:89:17)\n                     
     at Boot.loadPlugin (/path/to/project/node_modules/avvio/plugin.js:175:10)\n                     
     at release (/path/to/project/node_modules/fastq/queue.js:127:16)\n                     
     at Object.resume (/path/to/project/node_modules/fastq/queue.js:61:7)\n                     
     at Plugin.finish (/path/to/project/node_modules/avvio/plugin.js:166:10)

Instead, this parameters object should be merged into all method definitions.

OpenAPI 3 server url

Hi,

I converted the petstore-swagger.v2.json to openAPI 3 with editor.swagger.io. I noticed that v3 no longer requires basePath and it will be substitute by servers array.

"servers": [
{
"url": "http://petstore.swagger.io/v3"
}
],

However, when swapped with the petstore example:

const options = {
specification: ${__dirname}/petstore-swagger.v3.json,
service: ${__dirname}/service.js
};

curl -s -XGET http://localhost:3000/v3/pet/24 -H "accept: application/json" | python -m json.tool
{
"error": "Not Found",
"message": "Route GET:/v3/pet/24 not found",
"statusCode": 404
}

If "v3" is removed from the server URL then it works like this, BUT notice that the photoUrl key was not returned:

curl -s -XGET http://localhost:3000/pet/24 -H "accept: application/json" | python -m json.tool
{
"id": 24,
"name": "Kitty the cat",
"photoUrls": [],
"status": "available"
}

Any idea how to retain the server url as specified in the specification and how to return correct photoUrls?

Thanks.

Model composition using allOf doesn't work

The following schema break application without giving any errors or warnings

components:
  schemas:
    baseUser:
      type: "object"
      properties:
        username:
          type: "string"
        email:
          type: "string"
          format: "email"
        is_verified:
          type: "boolean"

    newUser:
      allOf:
        - type: "object"
          properties:
            password:
              type: "string"
        - $ref: "#/components/schemas/baseUser"

Application starts working fine if allOf is removed or if there is only a sinlge sub-schema under allOf.
I am using fastify-openapi-glue as a middleware to Fastify server.

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main

Hello, im having an issue importing this module when running even the simplest example. After upgrading this package from 2.7.x to latest, I'm getting this error when attempting to import



Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /Users/x/node_modules/fastify-openapi-glue/package.json
    at new NodeError (node:internal/errors:372:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:472:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:693:7)
    at resolveExports (node:internal/modules/cjs/loader:482:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/Users/x/dist/index.js:18:43) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

Is it something with the new esm update? I'm using typescript 4.7.4 and im using node 16.

index.ts

import fastify from "fastify";
import cors from "@fastify/cors";
import fastifyOpenapiGlue from "fastify-openapi-glue";
// const fastifyOpenapiGlue = require("fastify-openapi-glue");

const app = fastify();

app.register(cors);

app.register(fastifyOpenapiGlue);

console.log("fastify", fastify);

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "module": "CommonJS" /* Specify what module code is generated. */,
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
    "strict": true /* Enable all strict type-checking options. */,
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}

start script:

tsc && node index.js

Accessing schemas defined in YAML within Fastify app

Hi there, I'm working on understanding the best use of fastify-openapi-glue with respect to accessing the schemas defined within the OpenAPI document.

Mainly my digging has lead me to believe that as the schemas are defined in the YML file, and that file is loaded at app start, I would think that the schemas should all be available through fastify.getSchemas() which would then allow me to use them in part to further validate the incoming data with custom validation (ex: validation of multiple fields, searching the database for unique values, and so on).

However, when I examine fastify.getSchemas() this appears to be empty. Is there another way to access the schemas as they are loaded from glue so that I can use Ajv ahead of time to set up the validations where I would simply pass the request to?

In addition to the above, I would love to have the data available in a prepared POJO (like a global fastify schema) that that has been filtered which I could then pass around into other functions or methods instead of using request.body which may contain data that hasn't been treated. The alternative here is that I have to write a POJO and fill it myself from the request.body to then be able to use as I see fit.

Is there a best way from your experience to load schemas into the global collection via fastify.addSchemas?

Thoughts and thanks for your time in advance on this question.

Generate TypeScript types for each operation's params and response

It would be useful to generate TypeScript somewhat like this (based on what I see in this example):

type AddPetParams = {
  id?: number,
  category?: {id?: number, name?: string},
  /** @example "doggie" */
  name: string,
  photoUrls: []string,
  tags?: []{id?: number, name?: string},
  status?: 'available' | 'pending' | 'sold',
}

type Pet = {
  //...
}

async addPet(req: Req<AddPetParams>): Pet {
  console.log("addPet", req.params);
  return { name: "doggie", photoUrls: [] };
}

Error with 2.6.8 version

Hi

I've a problem with 2.6.8 version of this plugin. With 2.6.7 my yaml swagger specification file works fine, but with 2.6.8 update i've this error:
ENOENT: no such file or directory, open '/D:/pode/ts-employee-backend-write/node_modules/@seriousme/openapi-schema-validator/schemas/v2.0/schema.json'
Error: 'specification' parameter must contain a valid version 2.0 or 3.0.x or 3.1.x specification
at parser.parse (D:\pode\ts-employee-backend-write\node_modules\fastify-openapi-glue\lib\parser.js:54:13)
at async fastifyOpenapiGlue (D:\pode\ts-employee-backend-write\node_modules\fastify-openapi-glue\index.js:114:18)

My code with openapi-glue is very simple:

import FastifyOpenApiGlue from 'fastify-openapi-glue';
.
.
.
.
    const openapiGlueOptions = {
        specification: path.join(__dirname, 'api/swagger.yaml'),
        service: new WriteController()
    };
    const app = Fastify({
        bodyLimit: 524288,
        maxParamLength: 200
    });
    app.register(FastifyOpenApiGlue, openapiGlueOptions);

This is an example that works with 2.6.7 but brokes with 2.6.8

swagger: "2.0"
info:
    description: |
        TODO

    version: "5.0.1"
    title: WIP
    termsOfService: http://swagger.io/terms/
    contact:
        email: [email protected]
    license:
        name: Apache 2.0
        url: http://www.apache.org/licenses/LICENSE-2.0.html
externalDocs:
    description: Find out more about Swagger
    url: http://swagger.io
basePath: /
schemes:
    - https
    - http
produces:
    - application/json

paths:
    /docs/isReady:
        get:
            tags:
                - Probes
            summary: isReady (for k8s readiness probe)
            description: isReady (for k8s readiness probe)
            operationId: isReady
            responses:
                200:
                    description: isReady
                500:
                    description: ko

Sorry for my poor English

Filippo

Support multipart/form-data requests

Hi,

it seems that fastify-openapi-glue doesn't support multipart/form-data requests at all. I notice some warnings at service startup

body type: multipart/form-data found

and there's no input validation.
If I call my service with multipart/form-data body it returns 415 with FST_ERR_CTP_INVALID_MEDIA_TYPE error message.

Request to support coerceTypes for each part of an HTTP message separately.

Currently, when registering the openapi-glue with fastify, you can specify ajvOptions such as:

   await fastifyApp.register(openapiGlue, {
      specification: <myapi spec>,
      service: <myService>,
      prefix: <myAPIPrefix>,
      ajvOptions: {
         coerceTypes: false 
      }
   });

The drawback of this is that if you set coerceTypes to true, it enables type coercion on all parts of the message. This issue is to allow enabling type coercion on each part of the HTTP message separately. An example ajvOptions object might look like:

  ajvOptions: {
     coerceParms: true | false,
     coerceBody: true | false,
     coerceQueryString: true | false,
     coerceHeaders: true | false    
  }

[bug?] Validation errors not handled in TypeScript

I used fastify-openapi-glue with a vanilla JS project and it worked great:
https://github.com/Postman-Student-Program/restaurant-api-example/tree/chapter-3-persisting-data

Now I'm using TypeScript with the same setup but validation errors (400) are not being caught be fastify. Cryptic 500 errors are being returned from the API

server.ts

import Fastify from 'fastify'
import openapiGlue from 'fastify-openapi-glue'
import RouteHandler from './RouteHandler'

const glueOptions = {
  specification: `${__dirname}/schema.yaml`,
  service: new RouteHandler()
}

const fastify = Fastify({ logger: true })

fastify.register(openapiGlue, glueOptions)

const PORT = process.env.PORT || 4000

fastify.listen(PORT, (err, address) => {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  console.log(`Server listening at ${address}`)
})

RouteHandler.ts

import { FastifyReply, FastifyRequest } from 'fastify'
import context from './context'
import { castGetBooksParams, filterObj } from './utils'
import requireApiKey from './validations/requireApiKey'

const { booksService } = context.services

class RouteHandler {
  constructor() {}

  healthcheck = (_req: FastifyRequest, res: FastifyReply) => {
    return res.send({ message: 'ok' })
  }

  getBooks = async (req: FastifyRequest, res: FastifyReply) => {
    const params = castGetBooksParams(req.query as GetBooksParamsRaw)
    const books = await booksService.getBooks(params)
    return res.send(books)
  }

  createBook = async (req: FastifyRequest, res: FastifyReply) => {
    requireApiKey(req)

    const newBook = await booksService.createBook(req.body as CreateBookInput)
    return res.status(201).send(newBook)
  }

  getBook = async (req: FastifyRequest, res: FastifyReply) => {
    const book = await booksService.getBook(req.params as IdParams)
    return res.send(book)
  }

  updateBook = async (req: FastifyRequest, res: FastifyReply) => {
    requireApiKey(req)
    const params = req.params as IdParams
    const input = req.body as UpdateBookInput
    const book = await booksService.updateBook(params, input)
    return res.send(book)
  }

  deleteBook = async (req: FastifyRequest, res: FastifyReply) => {
    requireApiKey(req)
    const params = req.params as IdParams
    await booksService.deleteBook(params)
    return res.code(204).send()
  }
}

export default RouteHandler

example part of schema.yaml for POST /books

/books:
  post:
      summary: 'Add a book'
      operationId: createBook
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBookInput'
      responses:
        '201':
          description: 'Book created successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Book'
        '400':
          description: 'Bad Request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'Unauthorized'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        default:
          description: 'Unexpected error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

....

components:
  schemas:
    CreateBookInput:
      type: object
      required:
        - title
        - author
        - genre
        - yearPublished
      properties:
        title:
          type: string
          minLength: 1
        author:
          type: string
          minLength: 1
        genre:
          type: string
          minLength: 1
        yearPublished:
          type: integer
    

Trying to send a POST request to /books with a missing parameter returns a 500 error (expect 400 error with "missing" property type)

response from Fastify API

{
    "statusCode": 500,
    "error": "Internal Server Error",
    "message": "\"status\" is required!"
}

An in-range update of tap is breaking the build 🚨

The devDependency tap was updated from 12.6.0 to 12.6.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

tap is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 4 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Add custom properties to schema

In function makeSchema I found that tags, summary, description, operationId properties are adding in schema, so I think whould be very useful to add custom properties (which starts with x-) in schema too.

SecurityHandler overrides error

Hi!
If I throw an error within the securityhandler, like this:

  throw new ForbiddenError('User don't have permission to requested resource');
  throw new InternalServerError('Technical error, please try again later');

Its caught here https://github.com/seriousme/fastify-openapi-glue/blob/master/lib/securityHandlers.js#L69 and the statuscode gets overridden to always return 401.

I rather have it return the error catched here https://github.com/seriousme/fastify-openapi-glue/blob/master/lib/securityHandlers.js#L57 than wrapping it in a general error with wrong code.

Thanks!

Testing

Hi. Is there any reason that testing cannot be implemented as a part od the setup script? If the only constraint is developer time I'd like to give it a go.

An in-range update of fastify-cli is breaking the build 🚨

The devDependency fastify-cli was updated from 0.26.1 to 0.26.2.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

fastify-cli is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

Release Notes for v0.26.2

#138 - Normalize line endings

Commits

The new version differs by 5 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Any reason why AJV isn't passed in as an argument?

Passing an AJV instance into this plugin would result in fewer direct dependencies for this package and more customizability from a user's perspective. Not everyone needs the extra ajv format options so they could optimize their code by not including it or by only including the extra formats that they need.

Only last schema is used for endpoints with multiple mimetypes

Only last schema is being used for endpoints with multiple mimetypes:

parseBody(data) {
let schema;
if (data && data.content) {
for (const mimeType in data.content) {
this.config.contentTypes.add(mimeType);
schema = data.content[mimeType].schema;
}
}
return schema;
}

which contradicts the spec stating that Schemas can vary by media type.

Therefore, following sample spec will not consume text/plain requests by throwing

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "body should be object"
}
Sample spec:
openapi: 3.0.0
info:
  version: 1.0.0
  title: Test
paths:
  "/test":
    post:
      summary: Multiple mimetypes payload
      operationId: test
      requestBody:
        content:
          text/plain:
            schema:
              type: string
          application/json:
            schema:
              $ref: "#/components/schemas/TestMessage"
      responses:
        "200":
          description: Message received
components:
  schemas:
    TestMessage:
      type: object
      nullable: true
      properties:
        id:
          type: number
          description: Id

fastify server binding lost in Route handlers

Calling this in a route handler refers to the service object instead of fastify. This causes lost references to fastify and possibly other plugins attached to the fastify object. It may have been broken in #73

For example, when used with fastify-mongo:

fastify.get('/user/:id', function (req, reply) {
  const db = this.mongo.db // throws because fastify.mongo no longer exists
  ...
})

From Fastify docs:

handler(request, reply): the function that will handle this request. The Fastify server will be bound to this when the handler is called. Note: using an arrow function will break the binding of this.

I'm working around this by adding fastify back to the service object, but it may cause confusion for other users (as it did for me) when installing this plugin and following along with the docs.

const options = {
  specification: 'swagger.yaml',
  prefix: 'v1',
  noAdditional: true,
  service: () => ({
    fastify: app,
    handleRequest
  }),
};

Possible solutions:

  1. Document the scope change and workaround
  2. Bind routes with both service and fastify:

If this is where it's happening, something like:

async function generateRoutes(routesInstance, opts) {
    config.routes.forEach(item => {
      const response = item.schema.response;
      if (response) {
        stripResponseFormats(response);
      }
      if (service[item.operationId]) {
        routesInstance.log.debug("service has", item.operationId);
        item.handler = service[item.operationId].bind({...service, ...routeInstance}); // maybe? not sure if routeInstance is the fastify instance
      } else {
        item.handler = async (request, reply) => {
          throw new Error(`Operation ${item.operationId} not implemented`);
        };
      }
      routesInstance.route(item);
    });
  }

If I'm not missing something, and this is indeed an issue, I'll gladly create a PR.

Allow to override Ajv instance

Hey,

Could it be possible to add another option or extend the ajvOptions to use user given ajv instance instead of creating your own. The problem is here: https://github.com/seriousme/fastify-openapi-glue/blob/master/index.js#L60 the usage setValidatorCompiler overrides anything user might want and setValidatorCompiler was pretty much added so that user can override ajv instance on the fastify, but using this tool that's impossible.

I did a small patch https://github.com/seriousme/fastify-openapi-glue/blob/master/index.js#L56 on that line with following:

-  const ajv = new Ajv(ajvOptions);
+  const ajv = ajvOpts || new Ajv(ajvOptions);

To let me able to pass the ajv instance through the ajvOptions.

Would it be possible to add this as ajvInstance option (override ajv instance which doesn't take ajvOptions into account or such).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.