Giter Club home page Giter Club logo

express-openapi-validate's Introduction

express-openapi-validate

codecov npm Try on RunKit

Express middleware to validate requests based on an OpenAPI 3.0 document. OpenAPI specification was called the Swagger specification before version 3.

Usage

Install this package with npm or yarn:

npm install --save express-openapi-validate
# or
yarn add express-openapi-validate

Then use the validator like this:

index.js

const fs = require("fs");
const express = require("express");
const { OpenApiValidator } = require("express-openapi-validate");
const jsYaml = require("js-yaml");

const app = express();
app.use(express.json());

const openApiDocument = jsYaml.safeLoad(
  fs.readFileSync("openapi.yaml", "utf-8"),
);
const validator = new OpenApiValidator(openApiDocument);

app.post("/echo", validator.validate("post", "/echo"), (req, res, next) => {
  res.json({ output: req.body.input });
});

app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    error: {
      name: err.name,
      message: err.message,
      data: err.data,
    },
  });
});

const server = app.listen(3000, () => {
  console.log("Listening on", server.address());
});

Or using match():

// Apply to all requests
app.use(validator.match());

// On each request validator.validate("post", "/echo") is called automatically,
// if it has a matching specification in the OpenAPI schema
app.post("/echo", (req, res, next) => {
  res.json({ output: req.body.input });
});

openapi.yaml

openapi: 3.0.1
info:
  title: Example API with a single echo endpoint
  version: 1.0.0
components:
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            name:
              type: string
            message:
              type: string
            data:
              type: array
              items:
                type: object
          required:
            - name
            - message
      required:
        - error
  responses:
    error:
      description: Default error response with error object
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
paths:
  /echo:
    post:
      description: Echo input back
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                input:
                  type: string
              required:
                - input
      responses:
        "200":
          description: Echoed input
          content:
            application/json:
              schema:
                type: object
                properties:
                  output:
                    type: string
        default:
          $ref: "#/components/responses/error"

Supported features

Currently unsupported features

Public API

class OpenApiValidator

const { OpenApiValidator } = require("express-openapi-validate");

The main class of this package. Creates JSON schema validators for the given operations defined in the OpenAPI document. In the background Ajv is used to validate the request.

constructor(openApiDocument: OpenApiDocument, options: ValidatorConfig = {}))

Creates a new validator for the given OpenAPI document.

options parameter is optional. It has the following optional fields:

{
  ajvOptions: Ajv.Options;
}

You can find the list of options accepted by Ajv from its documentation. The formats object passed to Ajv will be merged with additional OpenAPI formats supported by this library.

validate(method: Operation, path: string): RequestHandler

Returns an express middleware function for the given operation. The operation matching the given method and path has to be defined in the OpenAPI document or this method throws.

The middleware validates the incoming request according to the parameters and requestBody fields defined in the Operation Object. If the validation fails the next express function is called with an ValidationError.

See the Parameter Object and Request Body Object sections of the OpenAPI specification for details how to define schemas for operations.

method must be one of the valid operations of an OpenAPI Path Item Object: "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace".

RequestHandler is an express middleware function with the signature (req: Request, res: Response, next: NextFunction): any;.

match(options: MatchOptions = { allowNoMatch: false }): RequestHandler

Returns an express middleware function which calls validate() based on the request method and path. Using this function removes the need to specify validate() middleware for each express endpoint individually.

match() throws an error if matching route specification is not found. This ensures all requests are validated.

Use match({ allowNoMatch: true}) if you want to skip validation for routes that are not mentioned in the OpenAPI schema. Use with caution: It allows requests to be handled without any validation.

The following examples achieve the same result:

// Using validate()
app.post("/echo", validator.validate("post", "/echo"), (req, res, next) => {
  res.json({ output: req.body.input });
});

// Using match()
app.use(validator.match());
app.post("/echo", (req, res, next) => {
  res.json({ output: req.body.input });
});

validateResponse(method: Operation, path: string): (res: any) => void

Creates a function for the given operation that can be used to validate responses. Response validation is meant to be used in tests and not in production code. See below for example usage.

For documentation of the method and path parameters see validate.

res is expected to have the shape { statusCode: number, body: {}, headers: {}}. The statusCode field can also be called status and the body field can be called data. This means that response objects from most request libraries should work out of the box.

If validation fails the validation function throws a ValidationError. Otherwise it returns undefined.

Example usage when using Jest and SuperTest:

import { OpenApiValidator } from "express-openapi-validate";
import fs from "fs";
import jsYaml from "js-yaml";
import request from "supertest";
import app from "./app";

const openApiDocument = jsYaml.safeLoad(
  fs.readFileSync("openapi.yaml", "utf-8"),
);
const validator = new OpenApiValidator(openApiDocument);

test("/echo responses", async () => {
  const validateResponse = validator.validateResponse("post", "/echo");
  let res = await request(app).post("/echo").send({});
  expect(validateResponse(res)).toBeUndefined();

  res = await request(app).post("/echo").send({ input: "Hello!" });
  expect(validateResponse(res)).toBeUndefined();
});

class ValidationError extends Error

const { ValidationError } = require("express-openapi-validate");

This error is thrown by OpenApiValidator#validate when the request validation fails. It contains useful information about why the validation failed in human-readable format in the .message field and in machine-readable format in the .data array.

You can catch this error in your express error handler and handle it specially. You probably want to log the validation error and pass the errors to the client.

message: string

Human-readable error message about why the validation failed.

statusCode: number = 400

This field is always set to 400. You can check this field in your express error handler to decide what status code to send to the client when errors happen.

data: ErrorObject[]

Machine-readable array of validation errors. Ajv Error Objects documentation contains a list of the fields in ErrorObject.

express-openapi-validate's People

Contributors

aautio avatar bemisguided avatar codeclown avatar greenkeeper[bot] avatar greenkeeperio-bot avatar hilzu avatar mochiya98 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

Watchers

 avatar  avatar  avatar

express-openapi-validate's Issues

Response validation does not work properly for nullable arrays

I have a type: array field in response schema with nullable: true. But I get

Error while validating response: response.body.chain.places.places should be array

Response validation for null works if I change type: array to type: object in schema.

Does not validate a route with 3 required parameters in url

Hi,
When I try to validate a url with 3 url parameters which is required I get an error saying that I have to include the urlparameter.

Example:

/foo/{fooId}/bar/{barId}/foobar/{foobarId}

swagger.file:

/v1/foo/{fooId}/bar/{barId}/foobar/{foobarId}:
parameters:
  - $ref: '#/components/parameters/fooIdPathInput'
  - $ref: '#/components/parameters/barIdPathInput'
  # - $ref: '#/components/parameters/foobarIdPathInput'  // does not work with required with 3 params
  - name: foobarId
    in: path
    required: false   # <---- Only works if I set this to false (this is the 3. parameter)
    schema:
      type: string
      format: uuid

Set default QueryString parameters values in Express req object

Hello,

Could it be possible that the library set default value(s) defined in parameters?

Today, with the version 0.4.4 of the lib. If I use the example below:

 /users:
    get:
      description: |
        Returns all users from the referential
      operationId: findUsers
      parameters:
        - name: limit
          in: query
          description: Number of items to retrieve per page. Default is 5, maximum is 50.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 50
            default: 5

And this implementation :

const validator = new OpenApiValidator(openApiDocument, {ajvOptions: { coerceTypes: true }});

router.get('/', validator.validate("get", "/users"), function(req, res) {
  console.log(req.query); //the object is empty... I expect the param `limit` to be equal to 5
  res.send('success');
});

Is this feature is out of scope of the library? Is it a bug?

Thank you in advance!

Warning on nodeJs console "schema $id ignored..."

I use express-openapi-validate v0.5.1 and since a while (cannot tell exactly), I get a warning in node console:

schema $id ignored https://github.com/ajv-validator/ajv/blob/master/lib/definition_schema.js
meta-schema not available

This warning is issued while calling the validator with a single option to ignore the OAS spec V3 keyword:

const { OpenApiValidator } = require("express-openapi-validate");
const validator = new OpenApiValidator(openApiDocument, {
    ajvOptions: {
        nullable: true,
    },
});

The document could be any document. To reproduce the problem, we can use the PetStore document attached to the end of this issue.
The url "https://github.com/ajv-validator/ajv/blob/master/lib/definition_schema.js" is no longer available on ajv's Github repo. The problem could be probably solved by upgrading the ajv version in your package.

Thanks!

PetStore document:

openApiDocument = {
    openapi: "3.0.0",
    info: {
        version: "3.0.0",
        title: "Swagger Petstore",
    },
    servers: [
        {
            url: "http://petstore.swagger.io/v1",
        },
    ],
    paths: {
        "/pets": {
            get: {
                summary: "List all pets",
                operationId: "listPets",
                tags: ["pets"],
                parameters: [
                    {
                        name: "limit",
                        in: "query",
                        description: "How many items to return at one time (max 100)",
                        required: false,
                        schema: {
                            type: "integer",
                            format: "int32",
                        },
                    },
                ],
                responses: {
                    200: {
                        description: "A paged array of pets",
                        headers: {
                            "x-next": {
                                description: "A link to the next page of responses",
                                schema: {
                                    type: "string",
                                },
                            },
                        },
                        content: {
                            "application/json": {
                                schema: {
                                    $ref: "#/components/schemas/Pets",
                                },
                            },
                        },
                    },
                    default: {
                        description: "unexpected error",
                        content: {
                            "application/json": {
                                schema: {
                                    $ref: "#/components/schemas/Error",
                                },
                            },
                        },
                    },
                },
            },
            post: {
                summary: "Create a pet",
                operationId: "createPets",
                tags: ["pets"],
                responses: {
                    201: {
                        description: "Null response",
                    },
                    default: {
                        description: "unexpected error",
                        content: {
                            "application/json": {
                                schema: {
                                    $ref: "#/components/schemas/Error",
                                },
                            },
                        },
                    },
                },
            },
        },
        "/pets/{petId}": {
            get: {
                summary: "Info for a specific pet",
                operationId: "showPetById",
                tags: ["pets"],
                parameters: [
                    {
                        name: "petId",
                        in: "path",
                        required: true,
                        description: "The id of the pet to retrieve",
                        schema: {
                            type: "string",
                        },
                    },
                ],
                responses: {
                    200: {
                        description: "Expected response to a valid request",
                        content: {
                            "application/json": {
                                schema: {
                                    $ref: "#/components/schemas/Pet",
                                },
                            },
                        },
                    },
                    default: {
                        description: "unexpected error",
                        content: {
                            "application/json": {
                                schema: {
                                    $ref: "#/components/schemas/Error",
                                },
                            },
                        },
                    },
                },
            },
        },
    },
    components: {
        schemas: {
            Pet: {
                type: "object",
                required: ["id", "name"],
                properties: {
                    id: {
                        type: "integer",
                        format: "int64",
                    },
                    name: {
                        type: "string",
                    },
                    tag: {
                        type: "string",
                    },
                },
            },
            Pets: {
                type: "array",
                items: {
                    $ref: "#/components/schemas/Pet",
                },
            },
            Error: {
                type: "object",
                required: ["code", "message"],
                properties: {
                    code: {
                        type: "integer",
                        format: "int32",
                    },
                    message: {
                        type: "string",
                    },
                },
            },
        },
    },
};

Invalid request validation

I'm getting the following error for request validation:

error while validating request: request should have required property '.headers'

The stack I am using is:

  • express 4.17.1
  • express-openapi-validate 0.5.1
  • OpenAPI spec 3.0.1
  • NodeJS 15.12.0
  • ES6 implementation

Doing some research I narrowed it down to the following code part of the validate function:

const reqToValidate = {
        ...req,
        cookies: req.cookies
          ? { ...req.cookies, ...req.signedCookies }
          : undefined,
      };

Which compiles to:

const reqToValidate = Object.assign({}, req, { cookies: req.cookies
                    ? Object.assign({}, req.cookies, req.signedCookies) : undefined });

The problem here is express uses accessors get/set for the headers, as a result the property headers is not present as part of reqToValidate.

As a quick fix the issue I implemented the following:

const validator = this.openApiValidator.validate(method, path);
return (req, res, next) => {
    const reqToValidate = Object.assign({}, req, {
        cookies: req.cookies
            ? Object.assign({}, req.cookies, req.signedCookies) : undefined
    });
   
    if (!reqToValidate.hasOwnProperty('headers')) {
        reqToValidate.headers = req.headers;
    }
    return validator(reqToValidate, res, next);
};

which works pretty well.

Thanks,
~Q

Automatically find correct validator for an endpoint

Using the req.route it should be possible to find the correct validator without the API consumer needing to pass in the method and path explicitly. This would make creating the validation middleware less verbose and remove the need to repeat the method and path in the validate call.

The bad side is that we can detect a missing validator only when the endpoint gets its first call and not when the endpoint is defined like with the explicit API. The cost of creating validators is also moved to call-time from application startup which might be a good or bad thing (application starts faster but the first calls to validated endpoints are a bit slower).

Even with these downsides it might be worth it to add an API like this.

// Something like this
app.post("/echo", validator.find(), (req, res, next) => {
  res.json({ output: req.body.input });
});

// instead of this (but keep this as a fallback)
app.post("/echo", validator.validate("post", "/echo"), (req, res, next) => {
  res.json({ output: req.body.input });
});

ValidationError: Error while validating request: request should have required property '.headers'

My current project is in node v14. When I tried upgrading it to v16, I am getting following error from the api.

ValidationError: Error while validating request: request should have required property '.headers'
at validate ($home/app/node_modules/express-openapi-validate/dist/OpenApiValidator.js:105:29)
at Layer.handle [as handle_request] ($home/app/node_modules/express/lib/router/layer.js:95:5)
at next ($home/app/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch ($home/app/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] ($home/app/node_modules/express/lib/router/layer.js:95:5)
at $home/app/node_modules/express/lib/router/index.js:281:22
at param ($home/app/node_modules/express/lib/router/index.js:354:14)
at param ($home/app/node_modules/express/lib/router/index.js:365:14)
at param ($home/app/node_modules/express/lib/router/index.js:365:14)
at Function.process_params ($home/app/node_modules/express/lib/router/index.js:410:3)

ajvOptions.unknownFormats options generates an error in the 0.6.0/0.6.1 update

My model has a field defined like this:

           ip:
             type: string
             format: ip
             pattern: ^((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))$
             example: 127.0.0.1

And I create a validator in my tests like this:

            validator = new OpenApiValidator(json, {
                ajvOptions: {
                    unknownFormats: 'ignore'
                }
            });

Passing above option still gets passed to ajv but now generates an error:
Uncaught Error: unknown format "ip" ignored in schema at path

Using this instead:

            validator = new OpenApiValidator(json, {
                unknownFormats: ['ip'],
            });

Generates the same error.

Validating form data uploads fails

Swagger 2.0 supports form data validation, but I had an issue with express-openapi-validate. It throws the following error:

Error: Unrecognized parameter location=formData
    at parameterLocationToRequestField (/usr/src/app/node_modules/express-openapi-validate/dist/parameters.js:32:11)

In ./src/parameters.ts, we can see that the allowed types are "header", "path", "cookie" and "query".

Could you confirm if it's possible to validate form data at all?

Best regards

Doesn't support get parameters

Request: GET /properties/5bd3d2ed73ffdf075808e70f

Ran console.debug(this._document, ["paths", path]) on dist/OpenApiValidator.js:171 and got:

paths:
   { '/': { get: [Object] },
     '/users': { post: [Object] },
     '/users/login': { post: [Object] },
     '/users/logout': { head: [Object] },
     '/properties': { post: [Object], patch: [Object] },
     '/properties/{id}': { get: [Object] },
     '/messages': { post: [Object] } },
  components:
   { securitySchemes: { BearerAuth: [Object] },
     schemas: { User: [Object], Message: [Object], Property: [Object] },
     responses: { BadRequest: [Object], InternalError: [Object] } } }

[ 'paths', '/properties/5bd3d2ed73ffdf075808e70f' ]

Stacktrace:

Error: Path=/properties/5bd3d2ed73ffdf075808e70f not found from OpenAPI document
    at OpenApiValidator._getPathItemObject ([redacted]/node_modules/express-openapi-validate/dist/OpenApiValidator.js:174:15)
    at OpenApiValidator.validate ([redacted]/node_modules/express-openapi-validate/dist/OpenApiValidator.js:76:37)
    at api.use.args ([redacted]/src/web.js:135:36)
    at Layer.handle [as handle_request] ([redacted]/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix ([redacted]/node_modules/express/lib/router/index.js:317:13)
    at [redacted]/node_modules/express/lib/router/index.js:284:7
    at Function.process_params ([redacted]/node_modules/express/lib/router/index.js:335:12)
    at next ([redacted]/node_modules/express/lib/router/index.js:275:10)
    at jsonParser ([redacted]/node_modules/body-parser/lib/types/json.js:109:7)
    at Layer.handle [as handle_request] ([redacted]/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix ([redacted]/node_modules/express/lib/router/index.js:317:13)
    at [redacted]/node_modules/express/lib/router/index.js:284:7
    at Function.process_params ([redacted]/node_modules/express/lib/router/index.js:335:12)
    at next ([redacted]/node_modules/express/lib/router/index.js:275:10)
    at multerMiddleware ([redacted]/node_modules/multer/lib/make-middleware.js:18:41)
    at Layer.handle [as handle_request] ([redacted]/node_modules/express/lib/router/layer.js:95:5)

From what it seems, this will never support any kind of get parameters. Does express-openapi-validate
support some means of using get parameters or does this still need to be implemented?

Does not handle additionalProperties

I've been using your library to validate requests, and it's been working great except for when it comes to additionalProperties.

The two methods I've tried of getting this to work are:

  1. Specify in the the request object that:
    required:
    -additionalProperties
  2. Specify in the referenced object within additionalProperties that each field is required.

With the first method, I always get failed validation because it is looking for an "additionalProperties" key. On the second method, if I don't include one of the fields that's required in the additionalProperties, I do not get a validation error.

Beautifying errrors

Hello,

I started using the package and I see that the errors are not very user friendly, are there plans to beautify the errors? or may I take the liberty and attempt to add beautified ajv errors?

Unable to validate integer parameter in path

I'm having trouble getting a correct validation for a parameter in path. I have the following in my API spec.

/v1/tick/{sequence}:
    get:
      parameters:
        - in: path
          name: sequence
          schema:
            type: integer
          description: The sequence number of the tick
          required: true
      tags:
        - Tick
      summary: Get a tick by sequence number
      operationId: GetSingleTick
      responses:
        "200":
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/tick'

And this is the setup of my express router:

// app.js
const tickRouter = require('./routes/tick');
app.use('/v1/tick', tickRouter);
// routes/tick.js
router.get('/:sequence', validator.validate('get', '/v1/tick/{sequence}'), (req, res, next) => {
  // Get the tick from DB and return
});

But when I make a call to the endpoint like this:

localhost:3000/v1/tick/290487

I get the following error saying that the parameter must be an integer:

{
    "error": {
        "name": "ValidationError",
        "message": "Error while validating request: request/params/sequence must be integer",
        "data": [
            {
                "instancePath": "/params/sequence",
                "schemaPath": "#/properties/params/properties/sequence/type",
                "keyword": "type",
                "params": {
                    "type": "integer"
                },
                "message": "must be integer"
            }
        ]
    }
}

Is this a bug? Or am I missing something important here?

match() should throw if no matching validator is found

As per discussion in #63...

  • similarly to validate(), match() should throw an exception if the request doesn't match any spec in the OpenAPI Schema
  • and this behaviour should be toggleable via an option, e.g. match({ allowNoMatch: true })

This is a breaking change compared to current behaviour.

RangeError: Maximum call stack size exceeded

Using a large YAML file (6,305 lines, 198K), however it fails searching for schema located at the end of the paths section. I ran the yaml through another validator to verify the format was correct, validated fine. Data validation worked fine until I added the last path and schema definitions then problem started to occur.
Attached are openapi 3.0 schema, and text file w/error message.

RangeError-MaxCallStack.txt
openapi.yaml.zip

allOf nesting not being validated?

Hi,

We have complex object roughly like:

    SearchResponseObject:
      description: "Combination response object for search"
      allOf:
        - $ref: "#/components/schemas/ApiResponseCommonValues"
        - type: object
          required:
            - details
          properties:
            details:
              type: object
              required:
                - xRequestId
                - result
              properties:
                xRequestId:
                  $ref: "#/components/schemas/xRequestId"
                result:
                  allOf:
                    - $ref: "#/components/schemas/StandardSearchResponseObject"
                      type: object
                      required:
                        - results
                      properties:
                        results:
                          type: array
                          items:
                            $ref: "#/components/schemas/SearchResultsItem"
    SearchResultsItem:
      description: "Individual Search Result"
      type: object
      required:
        - id
        - score
        - name
      properties:
        id:
          type: integer
          description: 'Id'
        score:
          type: number
          description: 'Search result score'
        name:
          type: string
          description: 'Name of the thing'
    200SearchResponse:
      description: "Search success response."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/SearchResponseObject"
 "/search":
    get:
      description: Search
      parameters:
        - name: "q"
          in: "query"
          required: false
          schema:
            type: string
      responses:
        200:
          $ref: "#/components/responses/200SearchResponse"
        400:
          $ref: "#/components/responses/400BadRequestResponse"

What we're finding is that the validator is not finding issues when the criteria in SearchResponseObject is not met. Are there any known issues with this kind of structure, or limitations on the depth that the validator will travel?

Thanks,

The 0.6.0 update breaks support for the example keyword

The openapi spec still supports the example keyword (https://swagger.io/docs/specification/adding-examples/)

But my tests fail with Uncaught Error: strict mode: unknown keyword: "example" after the latest update

Here is a relevant section of my spec:

/** 
 * @openapi
 *
 * components:
 *   schemas:
 *     Device:
 *        type: object
 *        properties:
 *          guid:
 *            type: string
 *            format: uuid
 *            pattern: ^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}$
 *            example: 00000000-0000-0000-0000-000000000000
 *          clientId:
 *            type: integer
 *            example: 1
 *          description:
 *            type: string
 *            example: Kingston 8gb
 *          deployment_date:
 *            type: string
 *            format: date-time
 *          facility:
 *            type: string
 *            example: HQ
 *          room:
 *            type: string
 *            example: Lobby
 *          location:
 *            type: string
 *            example: Countertop
 *          time
```zone:
 *            type: string
 *            example: America/Toronto
 *          enabled:
 *            type: boolean
 *          active:
 *            type: boolean
 *          offnetwork:
 *            type: boolean
 *        example:
 *          guid: 00000000-0000-0000-0000-000000000000
 *          clientId: 1
 *          description: Kingston 8gb
 *          facility: HQ
 *          room: Lobby
 *          location: Countertop
 *          enabled: true
 *          active: false
 *          offnetwork: false
 *     DeviceResponse:
 *       allOf:
 *       - $ref: '#/components/schemas/Device'
 *       - type: object
 *         required:
 *         - createdAt
 *         - updatedAt
 *         properties:
 *           createdAt:
 *             type: string
 *             format: date-time
 *           updatedAt:
 *             type: string
 *             format: date-time
*/

OpenApiValidator's match middleware doesn't parse path params

If an express route includes any path parameters (/foo/:bar, which the swagger represents as /foo/{bar}), and the match middle ware is configured, when the OpenAPIValidator validate method is called, the req.params are empty, whereas we expect req.params = { bar: '[steel|...]' } ...

It seems as though match has its own routing logic perhaps that doesn't include the step to parse the path parameters the same way the express route logic does?

In any event this seems like an issue that should either be fixed or the documentation changed to not use the match middleware if a route includes any parameters.

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml
  • The new Node.js version is in-range for the engines in 1 of your package.json files, so that was left alone

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


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 🌴

Validate response schema

To fully comply with openapi the API should also return responses according to what is defined in the spec.

I can think of two scenarios or use cases for response validation:

  1. Validate response schema in tests (best if easy to slide in to any test)
it('returns foobar name', () =>
  agent
    .get('/api/foobar')
    .expect(200)
    .then(validator.validateResponse('get', '/api/foobar'))
    .then(res => {
      assert.equal(res.name, 'Sir Smith');
    })
);
  1. Validate responses in runtime.
  • Either return a server error status code if response is not valid (probably not the best idea)
  • Or, just log a warning to console whenever an invalid response is served so devs can notice it and act upon (probably a better idea), while app still serves the response to the client
app.use(validator.logInvalidResponses());

Extra query parameters don't give an error

Hello,
It seems that we are unable to detect extra parameters in the query of a request.
For example:
GET /pets?foo=bar passes without error even if the parameter "foo" is not defined in my OAS 3 spec.
Is it normal behavior? Is there any option to set in the library or ajv in order to get a sort of "additionalProperties : false" for the query parameters?
Thanks

OpenApiValidator's match middleware causes memory leak in release 0.60 onward due to Ajv v7 changes

The code path implicated in the memory leak is this usage of this.validate inside the middleware that is returned by match, because this.validate internally is calling ajv's compile.

this.validate(method, match.path)(req, res, next);

Per AJV's issues and documentation, a significant change was made between v6->v7 where it is expected for consumers to only call compile once. A potential solution would be to evaluate this.validate(method, match.path) as part of the initialization of the middleware, for each path, and cache the result once. Or, lazily initialize the cache on the first invocation. It sounds like they intend for this cache to be available to the application globally. This way in the middleware it's just retrieving the outcome from the cache rather than recompiling the schemas on every request, thus causing the memory leak.

Another potential solution mentioned in the issue discussion is using the ajv option addUsedSchema: false, but it's not the recommended solution, and per the discussion it sounds like there may be shortcomings of using it.
ajv-validator/ajv#1413
https://ajv.js.org/guide/managing-schemas.html

They also provided this blog post discussing the different strategies:
https://www.poberezkin.com/posts/2021-02-11-ajv-version-7-big-changes-and-improvements.html#caching-compiled-schemas

Path Params and QueryString parameters only support type string

Express only parses both Path Params and Query String parameters as strings. This limits the ability to use the various supported types – specifically number and boolean being the most applicable – as the validator will fail if these provided parameters do not exactly match the JavaScript type.

I am happy to look at implementing this but would like to socialize the paths to essentially loosen the rules to accomadate Express here.

Not validating security or securitySchema objects

Doesn't seem to support OAS 3.0 security and securitySchema objects. I've tried various configuration, but never seems to validate.

Components object:
securitySchemes: ApiKeyAuth: type: apiKey in: header name: x-api-key headers: 'x-api-key': description: API access key schema: type: "string" required: true

Operation object:
'/channels/{channelUuid}/groups/{groupUuid}': get: summary: Get a single group for a channel organization operationId: getGroup tags: - Groups security: - x-api-key: - apiKey

Schema composition breaks with additionalProperties: false

Schema composition doesn't appear to work with additionalProperties: false.

openapi.yaml

openapi: 3.0.1
components:
  schemas:
    Pet:
      type: object
      properties: 
        name:
          type: string
        age:
          type: integer
      required: 
        - name
        - age
      additionalProperties: false
    Dog:
      allOf: 
        - $ref: '#/components/schemas/Pet'
        - type: object
          properties:
            bark:
              type: boolean
          required:
            - bark
      additionalProperties: false
      
paths:
  /simple:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Pet'
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
  /complex:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Dog'
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object

Simple schema

  1. Hitting /simple with the following works as expected.
{
    "name": "hunter",
    "age": 12,
}
  1. Including an extra property foo in the request...
{
    "name": "hunter",
    "age": 12,
    "foo": true
}

...returns an error as expected because additionalProperties: false is set on the Pet schema.

{
    "error": {
        "name": "ValidationError",
        "message": "Error while validating request: request.body should NOT have additional properties",
        "data": [
            {
                "keyword": "additionalProperties",
                "dataPath": ".body",
                "schemaPath": "#/properties/body/additionalProperties",
                "params": {
                    "additionalProperty": "foo"
                },
                "message": "should NOT have additional properties"
            }
        ]
    }
}

Complex schema with composition

On the other hand, hitting /complex with the following...

{
    "name": "hunter",
    "age": 12,
    "bark": true
}

...returns an unexpected error.

{
    "error": {
        "name": "ValidationError",
        "message": "Error while validating request: request.body should NOT have additional properties",
        "data": [
            {
                "keyword": "additionalProperties",
                "dataPath": ".body",
                "schemaPath": "#/properties/body/additionalProperties",
                "params": {
                    "additionalProperty": "name"
                },
                "message": "should NOT have additional properties"
            }
        ]
    }
}

oas with circular reference

Hello, i have a problem:

my openapi spec has circular reference, so i am getting Maximum call stack size exceeded because of it in walkSchema function of schema-utils.js.

Is there a way to make express-openapi-validate work with oas with circular reference?

i have something like this:

Characteristic:
  type: object
  description: Provides the value of a given characteristic
  required:
    - name
  properties:
    id:
      type: string
      description: Unique identifier of the characteristic
    name:
      type: string
      description: Name of the characteristic
    nested:
      type: array
      items:
        $ref: '#/components/schemas/Characteristics'


Characteristics:
  type: array
  items:
    $ref: '#/components/schemas/Characteristic'

Form-style validation

It doesn't look like validating parameters that are passed like this is supported:

      parameters:
        - in: query
          name: filter
          required: true
          schema:
            type: object
            properties:
              petid:
                type: integer
                example: 12334
              petname:
                type: string
                example: cuty
              petowner:
                type: string
                example: John
            additionalProperties: false
            oneOf:
              - required: [petid]
              - required: [petname]
              - required: [petowner]
          style: form
          explode: true

Validation problem while parameters uses a $ref

Here is my schema:

openapi: 3.0.1
info:
  title: My API
  version: '1.0.0'

servers:
  - url: http://localhost:3000/api/v1
    description: Development localhost server

paths:
  /cities:
    get:
      summary: Returns a list of cities.
      description: Returns a list of cities matching the request parameters.
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/perPage'
        - name: postalCode
          in: query
          description: Postal code.
          required: false
          schema:
            $ref: '#/components/schemas/City/properties/postalCode'
      responses:
        '200':    # status code
          description: A JSON array of cities
          content:
            application/json:
              schema: 
                type: array
                items: 
                  $ref: '#/components/schemas/City'

components:
  schemas:
    City:
      properties:
        name:
          type: string
          maxLength: 128
        postalCode:
          type: string
          minLength: 5
          maxLength: 5
          pattern: '^\d{5}$'
  parameters:
    page:
      name: page
      in: query
      description: Page number.
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    perPage:
      name: perPage
      in: query
      description: Number of items per page.
      required: false
      schema:
        type: integer
        minimum: 1
        default: 100

Here is my router code:

router
  .route('/')
  .get(validator.validate('get', '/cities'), async (req, res, next) => {
    try {
      res.json(await cityService.list(req.query));
    } catch (error) {
      next(error);
    }
  })
;

and my validator.js:

const fs = require('fs');
const { OpenApiValidator } = require('express-openapi-validate');
const jsYaml = require('js-yaml');
var path = require('path');

const openApiDocument = jsYaml.safeLoad(
  fs.readFileSync(path.join(__dirname, '..', 'api.oas3.yaml'), 'utf-8')
);

module.exports = new OpenApiValidator(openApiDocument);

When I try to access my API at http://localhost:3000/api/v1/cities?page=1&perPage=2&postalCode=32165 I have the following error:

{"code":500,"message":"Error while validating request: request.query.page should be integer","stack":"ValidationError: Error while validating request: request.query.page should be integer\n    at validate (/app/node_modules/express-openapi-validate/dist/OpenApiValidator.js:107:29)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at next (/app/node_modules/express/lib/router/route.js:137:13)\n    at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at /app/node_modules/express/lib/router/index.js:281:22\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)\n    at Function.handle (/app/node_modules/express/lib/router/index.js:174:3)\n    at router (/app/node_modules/express/lib/router/index.js:47:12)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)\n    at /app/node_modules/express/lib/router/index.js:284:7\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)\n    at Function.handle (/app/node_modules/express/lib/router/index.js:174:3)\n    at router (/app/node_modules/express/lib/router/index.js:47:12)\n    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n    at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)\n    at /app/node_modules/express/lib/router/index.js:284:7\n    at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)\n    at next (/app/node_modules/express/lib/router/index.js:275:10)"}

Numeric query parameters

Integer parameters like the one listed below will never match. It seems express passes all query parameters as strings. We could try ParseInt and isNaN on integers fields to convert them to a valid type.

      parameters:
        - in: query
          name: offset
          schema:
            type: integer
            minimum: 0
            default: 0
          example: 0
          description: Offset your search results. Used for pagination.

On a side note default doesn't seem to work.

Thanks again, hopefully I haven't missed anything this time. :P

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.