Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

v4 UnhandledPromiseRejectionWarning: Error: openapi.validator: args.apiDoc was invalid. #551

Closed
waynebloss opened this issue Mar 3, 2021 · 4 comments

Comments

@waynebloss
Copy link

waynebloss commented Mar 3, 2021

Describe the bug

Cannot handle the promise created in openApiValidator in src/index.ts.

To Reproduce

Install the middleware with a bad apiSpec. Since I was specifically testing how to catch this error in v4, I created a bad apiSpec with an incorrectly formed operations.parameters field (e.g. parameters: { q: "string" },).

If you pass validateApiSpec: true you get the error in the title. Otherwise, you get TypeError: undefined is not a function. In both cases, there is no output saying what is invalid in the apiSpec.

Actual behavior

An uncatchable UnhandledPromiseRejectionWarning occurs and depending on the value of validateApiSpec, could be quite difficult for some people to debug.

Expected behavior
Be able to handle the promise like in v3 when you called OpenApiValidator.install(app).then().catch()....

Examples and context

Full reproduction.

const apiSpec = {
  openapi: '3.0.3',
  info: {
    title: 'The API',
    version: '1.0.0',
    description: 'Welcome to the API.'
  },
  servers: [ { url: 'http://localhost:54321/v1', description: 'Running' } ],
  components: {
    securitySchemes: {
      AuthJWT: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        description: 'Enter a JSON Web Token (JWT) to be sent with each request in the HTTP **Authorization** header.'
      }
    }
  },
  security: [ { AuthJWT: [] } ],
  paths: {
    '/dev/hello/echo': {
      get: {
        'x-eov-operation-id': 'echo',
        security: [],
        summary: 'Responds with the request.',
        description: '',
        responses: { '200': { description: 'OK' } },
        parameters: { q: 'string' }, // <-- THE INCORRECT BIT
        // parameters: [{in: 'query', name: 'q', required: false, schema: { type: 'string' }, allowEmptyValue: false}],
        'x-eov-operation-handler': 'dev/hello',
        tags: [ 'dev/hello' ]
      }
    },
    '/dev/hello/err': {
      get: {
        'x-eov-operation-id': 'err',
        security: [],
        summary: 'Responds with an error.',
        description: '',
        responses: { '500': { description: 'Error' } },
        'x-eov-operation-handler': 'dev/hello',
        tags: [ 'dev/hello' ]
      }
    }
  },
  tags: [ { name: 'dev/hello', description: 'API introduction' } ]
};
app.use(
  OpenApiValidator.middleware({
    apiSpec,
    validateApiSpec: true,
  }),
);

Workaround

Basically, reproduce the code in src/index.ts.

import { OpenApiValidator } from "express-openapi-validator/dist/openapi.validator";
import { OpenApiSpecLoader } from "express-openapi-validator/dist/framework/openapi.spec.loader";

export async function start() {
  const validator = new OpenApiValidator({
    apiSpec,
    validateApiSpec: true,
  });
  let starting = new OpenApiSpecLoader({
    apiDoc: cloneDeep(validator.options.apiSpec),
    validateApiSpec: validator.options.validateApiSpec,
    $refParser: validator.options.$refParser,
  })
    .load()
    .then(spec => {
      app.use(validator.installMiddleware(Promise.resolve(spec)));
    });
  if (__DEV__) {
    // Catch development errors, print help for invalid Open-API specifications.
    // Continue starting the server so developers can get API_URL/swagger.json.
    starting = starting.catch(writeDeveloperErrorHelp);
  }
  // ...
  await starting;

With writeDeveloperErrorHelp in place I get a nice error printout and the server continues to startup and run:

openapi.validator: Validating schema
openapi.validator: validation errors [
  {
    "keyword": "type",
    "dataPath": ".paths['/dev/hello/echo']['get'].parameters",
    "schemaPath": "#/properties/parameters/type",
    "params": {
      "type": "array"
    },
    "message": "should be array"
  }
]

Error: openapi.validator: args.apiDoc was invalid.  See the output.
    at OpenAPIFramework.initialize (/PROJECT/node_modules/express-openapi-validator/dist/framework/index.js:32:23)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at OpenApiSpecLoader.discoverRoutes (/PROJECT/node_modules/express-openapi-validator/dist/framework/openapi.spec.loader.js:26:39)
    at Object.start (/PROJECT/src/services/server.ts:191:3)

If you got `openapi.validator` errors:

1. Go to http://localhost:54321/swagger.json
2. Copy the contents of the file.
3. Go to https://editor.swagger.io/
4. Paste the contents of the file. Yes, convert to YAML.
5. See if this helps you locate the error. Ignore errors about unique operationIds. Error line #'s go to objects that contain the invalid property, the don't always point to the invalid property itself...

Running  http://localhost:54321
API      http://localhost:54321/v1
API Docs http://localhost:54321/v1/docs
API Spec http://localhost:54321/swagger.json

Server started.

Full error report when validateApiSpec is true

(node:889467) UnhandledPromiseRejectionWarning: Error: openapi.validator: args.apiDoc was invalid.  See the output.
    at OpenAPIFramework.initialize (/PROJECT/node_modules/express-openapi-validator/dist/framework/index.js:32:23)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at OpenApiSpecLoader.discoverRoutes (/PROJECT/node_modules/express-openapi-validator/dist/framework/openapi.spec.loader.js:26:39)
(node:889467) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:889467) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Full error report when validateApiSpec is false

TypeError: undefined is not a function
    at Object.visitApi (/PROJECT/node_modules/express-openapi-validator/dist/framework/openapi.spec.loader.js:40:102)
    at OpenAPIFramework.initialize (/PROJECT/node_modules/express-openapi-validator/dist/framework/index.js:41:21)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at OpenApiSpecLoader.discoverRoutes (/PROJECT/node_modules/express-openapi-validator/dist/framework/openapi.spec.loader.js:26:39)
    at Object.start (/PROJECT/src/services/server.ts:190:3)
@cdimascio
Copy link
Owner

cdimascio commented Mar 7, 2021

@waynebloss fixed in v.4.12.4

it should now work as expected in the default case i.e. whre validateApiSpec is true

in the case, where one opts to set validateApiSpec to false, errors will propagate appropriately. that said, express-openapi-validator expects a valid spec. disabling validation can lead to unexpected results when a spec is invalid. I added a warning to the README to ensure folks know the risk that's assumed by disabling validation.

@waynebloss
Copy link
Author

@cdimascio Thank you.

This fixes the unhandled promise rejection, but with this change I'm wondering how I can programatically detect the error that happens when validateApiSpec is true and the spec is invalid?

Specifically, I'd like to find out about this error once at startup so I can print an extended help message for other devs.

@waynebloss
Copy link
Author

@cdimascio If you're not interested in doing anything for this and you'd rather see a pull request I can do that.

I'm wondering what would be acceptable. A new option to add to OpenApiValidatorOpts, named something like onValidateApiSpecError?

If you don't want to add anything, that's fine too - I can certainly continue to just re-implement function openapiValidator from your src/index.ts file.

@waynebloss
Copy link
Author

Actually, for my own personal project which is the only project using v4 of this express-openapi-validator right now - I don't need the extra help message. I was mostly concerned with the junior devs where I work. So, if you just don't even want to think about it at all right now I'm fine with that too.

Thanks for your excellent work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment