From d86c1df82a29871704dc8515100d4baac5f1ea67 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Thu, 30 Jan 2025 12:20:52 -0800 Subject: [PATCH 1/2] feat(breaking): dropping support for callback handling --- README.md | 10 +- docs/README.md | 45 +--- docs/openapi-parser.md | 71 +++--- docs/refs.md | 2 +- lib/index.d.ts | 209 +++++------------- lib/index.js | 18 +- package-lock.json | 3 +- package.json | 4 +- .../awaited-behavior-error.yaml} | 0 .../awaited-behavior/awaited-behavior.test.ts | 50 +++++ .../awaited-behavior.yaml} | 0 .../bundled.js | 0 .../dereferenced.js | 0 .../parsed.js | 0 .../callbacks-promises.test.ts | 90 -------- 15 files changed, 155 insertions(+), 347 deletions(-) rename test/specs/{callbacks-promises/callbacks-promises-error.yaml => awaited-behavior/awaited-behavior-error.yaml} (100%) create mode 100644 test/specs/awaited-behavior/awaited-behavior.test.ts rename test/specs/{callbacks-promises/callbacks-promises.yaml => awaited-behavior/awaited-behavior.yaml} (100%) rename test/specs/{callbacks-promises => awaited-behavior}/bundled.js (100%) rename test/specs/{callbacks-promises => awaited-behavior}/dereferenced.js (100%) rename test/specs/{callbacks-promises => awaited-behavior}/parsed.js (100%) delete mode 100644 test/specs/callbacks-promises/callbacks-promises.test.ts diff --git a/README.md b/README.md index 189c9055..5bd2d074 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ - Parses Swagger specs in **JSON** or **YAML** format - Validates against the [Swagger 2.0 schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v2.0/schema.json), [OpenAPI 3.0 Schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.0/schema.json), or [OpenAPI 3.1 Schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.1/schema.json) -- [Resolves](https://apitools.dev/swagger-parser/docs/swagger-parser.html#resolveapi-options-callback) all `$ref` pointers, including external files and URLs -- Can [bundle](https://apitools.dev/swagger-parser/docs/swagger-parser.html#bundleapi-options-callback) all your Swagger files into a single file that only has _internal_ `$ref` pointers -- Can [dereference](https://apitools.dev/swagger-parser/docs/swagger-parser.html#dereferenceapi-options-callback) all `$ref` pointers, giving you a normal JavaScript object that's easy to work with +- [Resolves](https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#resolveapi-options-callback) all `$ref` pointers, including external files and URLs +- Can [bundle](https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#bundleapi-options-callback) all your Swagger files into a single file that only has _internal_ `$ref` pointers +- Can [dereference](https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#dereferenceapi-options-callback) all `$ref` pointers, giving you a normal JavaScript object that's easy to work with - **[Tested](https://github.com/readmeio/openapi-parser/actions)** in Node.js and all modern web browsers on Mac, Windows, and Linux - Tested on **[over 1,500 real-world APIs](https://apis.guru/browse-apis/)** from Google, Microsoft, Facebook, Spotify, etc. - Supports [circular references](https://apitools.dev/swagger-parser/docs/#circular-refs), nested references, back-references, and cross-references @@ -71,6 +71,10 @@ import OpenAPIParser from '@readme/openapi-parser'; ## Differences from `@apidevtools/swagger-parser` +The methods on `@readme/openapi-parser`, unlike `@apidevtools/swagger-parser`, do not support callbacks. + +### Error messages + `@apidevtools/swagger-parser` returns schema validation errors as the raw error stack from Ajv. For example: diff --git a/docs/README.md b/docs/README.md index c084ffab..291ad291 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,7 +3,6 @@ ## Things to Know - [Class methods vs. Instance methods](#class-methods-vs-instance-methods) -- [Callbacks vs. Promises](#callbacks-vs-promises) - [Circular references](#circular-refs) ## Classes & Methods @@ -12,11 +11,11 @@ - [`api` property](openapi-parser.md#api) - [`$refs` property](openapi-parser.md#refs) -- [`validate()` method](openapi-parser.md#validateapi-options-callback) -- [`dereference()` method](openapi-parser.md#dereferenceapi-options-callback) -- [`bundle()` method](openapi-parser.md#bundleapi-options-callback) -- [`parse()` method](openapi-parser.md#parseapi-options-callback) -- [`resolve()` method](openapi-parser.md#resolveapi-options-callback) +- [`validate()` method](openapi-parser.md#validateapi-options) +- [`dereference()` method](openapi-parser.md#dereferenceapi-options) +- [`bundle()` method](openapi-parser.md#bundleapi-options) +- [`parse()` method](openapi-parser.md#parseapi-options) +- [`resolve()` method](openapi-parser.md#resolveapi-options) #### [The `$Refs` class](refs.md) @@ -44,37 +43,7 @@ let parser = new OpenAPIParser(); parser.validate('my-api.yaml'); ``` -The difference is that in the second example you now have a reference to `parser`, which means you can access the results ([`parser.api`](openapi-parser.md#api-object) and [`parser.$refs`](openapi-parser.md#refs)) anytime you want, rather than just in the callback function. - -### Callbacks vs. Promises - -Many people prefer `async`/`await` or [Promise](http://javascriptplayground.com/blog/2015/02/promises/) syntax instead of callbacks. Swagger Parser allows you to use whichever one you prefer. - -If you pass a callback function to any method, then the method will call the callback using the Node.js error-first convention. If you do _not_ pass a callback function, then the method will return a Promise. - -The following two examples are equivalent: - -```javascript -// Callback syntax -OpenAPIParser.validate(mySchema, (err, api) => { - if (err) { - // Error - } else { - // Success - } -}); -``` - -```javascript -try { - // async/await syntax - let api = await OpenAPIParser.validate(mySchema); - - // Success -} catch (err) { - // Error -} -``` +The difference is that in the second example you now have a reference to `parser`, which means you can access the results ([`parser.api`](openapi-parser.md#api-object) and [`parser.$refs`](openapi-parser.md#refs)) anytime you want. ### Circular $Refs @@ -84,7 +53,7 @@ You can disable circular references by setting the [`dereference.circular`](opti Or you can choose to just ignore circular references altogether by setting the [`dereference.circular`](options.md) option to `"ignore"`. In this case, all non-circular references will still be dereferenced as normal, but any circular references will remain in the schema. -Another option is to use the [`bundle`](openapi-parser.md#bundleapi-options-callback) method rather than the [`dereference`](openapi-parser.md#dereferenceapi-options-callback) method. Bundling does _not_ result in circular references, because it simply converts _external_ `$ref` pointers to _internal_ ones. +Another option is to use the [`bundle`](openapi-parser.md#bundleapi-options) method rather than the [`dereference`](openapi-parser.md#dereferenceapi-options) method. Bundling does _not_ result in circular references, because it simply converts _external_ `$ref` pointers to _internal_ ones. ```javascript "person": { diff --git a/docs/openapi-parser.md b/docs/openapi-parser.md index 4933111b..534da43e 100644 --- a/docs/openapi-parser.md +++ b/docs/openapi-parser.md @@ -9,15 +9,15 @@ This is the default export of Swagger Parser. You can create instances of this c ##### Methods -- [`validate()`](#validateapi-options-callback) -- [`dereference()`](#dereferenceapi-options-callback) -- [`bundle()`](#bundleapi-options-callback) -- [`parse()`](#parseapi-options-callback) -- [`resolve()`](#resolveapi-options-callback) +- [`validate()`](#validateapi-options) +- [`dereference()`](#dereferenceapi-options) +- [`bundle()`](#bundleapi-options) +- [`parse()`](#parseapi-options) +- [`resolve()`](#resolveapi-options) ### `api` -The `api` property is the parsed/bundled/dereferenced Swagger API object. This is the same value that is passed to the callback function (or Promise) when calling the [`parse`](#parseapi-options-callback), [`bundle`](#bundleapi-options-callback), or [`dereference`](#dereferenceapi-options-callback) methods. +The `api` property is the parsed/bundled/dereferenced Swagger API object. This is the same value that is passed when calling the [`parse`](#parseapi-options), [`bundle`](#bundleapi-options), or [`dereference`](#dereferenceapi-options) methods. ```javascript let parser = new SwaggerParser(); @@ -35,7 +35,7 @@ api === parser.api; // => true The `$refs` property is a [`$Refs`](refs.md) object, which lets you access all of the externally-referenced files in the API, as well as easily get and set specific values in the schema using JSON pointers. -This is the same value that is passed to the callback function (or Promise) when calling the [`resolve`](#resolveapi-options-callback) method. +This is the same value that is passed when calling the [`resolve`](#resolveapi-options) method. ```javascript let parser = new SwaggerParser(); @@ -47,27 +47,24 @@ await parser.dereference('my-api.json'); parser.$refs.paths(); // => ["my-api.json"] ``` -### `validate(api, [options], [callback])` +### `validate(api, [options])` - **api** (_required_) - `string` or `object`
- A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options-callback) method for more info. + A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options) method for more info. - **options** (_optional_) - `object`
See [options](options.md) for the full list of options -- **callback** (_optional_) - `function(err, api)`
- A callback that will receive the dereferenced and validated [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). - - **Return Value:** `Promise`
- See [Callbacks vs. Promises](README.md#callbacks-vs-promises) + The dereferenced and validated [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). Validates the Swagger API against the [Swagger 2.0 schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v2.0/schema.json), [OpenAPI 3.0 Schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.0/schema.json), or [OpenAPI 3.1 Schema](https://github.com/OAI/OpenAPI-Specification/blob/main/schemas/v3.1/schema.json). If [the `validate.spec` option](options.md#validate-options) is enabled, then this method also validates against the [Swagger 2.0 spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md). The specification validator will catch some things that aren't covered by the Swagger 2.0 Schema, such as duplicate parameters, invalid MIME types, etc. -If validation fails, then an error will be passed to the callback function, or the Promise will reject. Either way, the error will contain information about why the API is invalid. +If validation fails, then the Promise will be rejected. Either way, the error will contain information about why the API is invalid. -This method calls [`dereference`](#dereferenceapi-options-callback) internally, so the returned [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object) is fully dereferenced. +This method calls [`dereference`](#dereferenceapi-options) internally, so the returned [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object) is fully dereferenced. ```javascript try { @@ -78,23 +75,20 @@ try { } ``` -### `dereference(api, [options], [callback])` +### `dereference(api, [options])` - **api** (_required_) - `string` or `object`
- A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options-callback) method for more info. + A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options) method for more info. - **options** (_optional_) - `object`
See [options](options.md) for the full list of options -- **callback** (_optional_) - `function(err, api)`
- A callback that will receive the dereferenced [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). - - **Return Value:** `Promise`
- See [Callbacks vs. Promises](README.md#callbacks-vs-promises) + The dereferenced [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). Dereferences all `$ref` pointers in the Swagger API, replacing each reference with its resolved value. This results in a [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object) that does not contain _any_ `$ref` pointers. Instead, it's a normal JavaScript object tree that can easily be crawled and used just like any other JavaScript object. This is great for programmatic usage, especially when using tools that don't understand JSON references. -The `dereference` method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of [circular references](README.md#circular-refs), so be careful if you intend to serialize the API using [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Consider using the [`bundle`](#bundleapi-options-callback) method instead, which does not create circular references. +The `dereference` method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of [circular references](README.md#circular-refs), so be careful if you intend to serialize the API using [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Consider using the [`bundle`](#bundleapi-options) method instead, which does not create circular references. ```javascript let api = await SwaggerParser.dereference('my-api.yaml'); @@ -104,21 +98,18 @@ let api = await SwaggerParser.dereference('my-api.yaml'); console.log(api.definitions.person.properties.firstName); // => {type: "string"} ``` -### `bundle(api, [options], [callback])` +### `bundle(api, [options])` - **api** (_required_) - `string` or `object`
- A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options-callback) method for more info. + A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options) method for more info. - **options** (_optional_) - `object`
See [options](options.md) for the full list of options -- **callback** (_optional_) - `function(err, api)`
- A callback that will receive the bundled [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). - -- **Return Value:** `Promise`
- See [Callbacks vs. Promises](README.md#callbacks-vs-promises) +- **Return Value:** `Promise`
+ The bundled [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object). -Bundles all referenced files/URLs into a single api that only has _internal_ `$ref` pointers. This lets you split-up your API however you want while you're building it, but easily combine all those files together when it's time to package or distribute the API to other people. The resulting API size will be small, since it will still contain _internal_ JSON references rather than being [fully-dereferenced](#dereferenceapi-options-callback). +Bundles all referenced files/URLs into a single api that only has _internal_ `$ref` pointers. This lets you split-up your API however you want while you're building it, but easily combine all those files together when it's time to package or distribute the API to other people. The resulting API size will be small, since it will still contain _internal_ JSON references rather than being [fully-dereferenced](#dereferenceapi-options). This also eliminates the risk of [circular references](README.md#circular-refs), so the API can be safely serialized using [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). @@ -127,7 +118,7 @@ let api = await SwaggerParser.bundle('my-api.yaml'); console.log(api.definitions.person); // => {$ref: "#/definitions/schemas~1person.yaml"} ``` -### `parse(api, [options], [callback])` +### `parse(api, [options])` - **api** (_required_) - `string` or `object`
A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. @@ -137,13 +128,10 @@ console.log(api.definitions.person); // => {$ref: "#/definitions/schemas~1person - **options** (_optional_) - `object`
See [options](options.md) for the full list of options -- **callback** (_optional_) - `function(err, api)`
- A callback that will receive the parsed [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or an error. - - **Return Value:** `Promise`
- See [Callbacks vs. Promises](README.md#callbacks-vs-promises) + The parsed [Swagger object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or a rejected error. -> This method is used internally by other methods, such as [`bundle`](#bundleapi-options-callback) and [`dereference`](#dereferenceapi-options-callback). You probably won't need to call this method yourself. +> This method is used internally by other methods, such as [`bundle`](#bundleapi-options) and [`dereference`](#dereferenceapi-options). You probably won't need to call this method yourself. Parses the given Swagger API (in JSON or YAML format), and returns it as a JavaScript object. This method **does not** resolve `$ref` pointers or dereference anything. It simply parses _one_ file and returns it. @@ -152,21 +140,18 @@ let api = await SwaggerParser.parse('my-api.yaml'); console.log('API name: %s, Version: %s', api.info.title, api.info.version); ``` -### `resolve(api, [options], [callback])` +### `resolve(api, [options])` - **api** (_required_) - `string` or `object`
- A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options-callback) method for more info. + A [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#swagger-object), or the file path or URL of your Swagger API. See the [`parse`](#parseapi-options) method for more info. - **options** (_optional_) - `object`
See [options](options.md) for the full list of options -- **callback** (_optional_) - `function(err, $refs)`
- A callback that will receive a [`$Refs`](refs.yaml) object. - - **Return Value:** `Promise`
- See [Callbacks vs. Promises](README.md#callbacks-vs-promises) + A [`$Refs`](refs.yaml) object. -> This method is used internally by other methods, such as [`bundle`](#bundleapi-options-callback) and [`dereference`](#dereferenceapi-options-callback). You probably won't need to call this method yourself. +> This method is used internally by other methods, such as [`bundle`](#bundleapi-options) and [`dereference`](#dereferenceapi-options). You probably won't need to call this method yourself. Resolves all JSON references (`$ref` pointers) in the given Swagger API. If it references any other files/URLs, then they will be downloaded and resolved as well (unless `options.$refs.external` is false). This method **does not** dereference anything. It simply gives you a [`$Refs`](refs.yaml) object, which is a map of all the resolved references and their values. diff --git a/docs/refs.md b/docs/refs.md index abde8af0..0a8bc088 100644 --- a/docs/refs.md +++ b/docs/refs.md @@ -1,6 +1,6 @@ # `$Refs` class -When you call the [`resolve`](openapi-parser.md#resolveschema-options-callback) method, the value that gets passed to the callback function (or Promise) is a `$Refs` object. This same object is accessible via the [`parser.$refs`](openapi-parser.md#refs) property of `OpenAPIParser` objects. +When you call the [`resolve`](openapi-parser.md#resolveschema-options) method, the value that is returned is a `$Refs` object. This same object is accessible via the [`parser.$refs`](openapi-parser.md#refs) property of `OpenAPIParser` objects. This object is a map of JSON References and their resolved values. It also has several convenient helper methods that make it easy for you to navigate and manipulate the JSON References. diff --git a/lib/index.d.ts b/lib/index.d.ts index 666b6a89..03febcbf 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -5,22 +5,22 @@ export = OpenAPIParser; /** * This is the default export of Swagger Parser. You can creates instances of this class using new OpenAPIParser(), or you can just call its static methods. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md */ declare class OpenAPIParser { /** - * The `api` property is the parsed/bundled/dereferenced OpenAPI definition. This is the same value that is passed to the callback function (or Promise) when calling the parse, bundle, or dereference methods. + * The `api` property is the parsed/bundled/dereferenced OpenAPI definition. This is the same value that is returned when calling the parse, bundle, or dereference methods. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#api + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#api */ public api: OpenAPI.Document; /** * The $refs property is a `$Refs` object, which lets you access all of the externally-referenced files in the OpenAPI definition, as well as easily get and set specific values in the OpenAPI definition using JSON pointers. * - * This is the same value that is passed to the callback function (or Promise) when calling the `resolve` method. + * This is the same value that is returned when calling the `resolve` method. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#refs + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#refs */ public $refs: OpenAPIParser.$Refs; @@ -28,24 +28,14 @@ declare class OpenAPIParser { * Parses, dereferences, and validates the given Swagger API. * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#validateapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#validateapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the dereferenced OpenAPI definition */ - public validate(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public validate( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public validate( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public validate(api: string | OpenAPI.Document): void; + public validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public validate(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public validate(api: string | OpenAPI.Document): Promise; public validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public validate( @@ -58,24 +48,14 @@ declare class OpenAPIParser { * Parses, dereferences, and validates the given Swagger API. * Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#validateapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#validateapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the dereferenced OpenAPI definition */ - public static validate(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public static validate( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public static validate( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public static validate(api: string | OpenAPI.Document): void; + public static validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public static validate(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public static validate(api: string | OpenAPI.Document): Promise; public static validate(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public static validate( @@ -89,24 +69,14 @@ declare class OpenAPIParser { * * The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the API definition using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#dereferenceapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#dereferenceapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the dereferenced OpenAPI definition */ - public dereference(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public dereference( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public dereference( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public dereference(api: string | OpenAPI.Document): void; + public dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public dereference(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public dereference(api: string | OpenAPI.Document): Promise; public dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public dereference( @@ -120,24 +90,14 @@ declare class OpenAPIParser { * * The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the API definition using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#dereferenceapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#dereferenceapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the dereferenced OpenAPI definition */ - public static dereference(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public static dereference( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public static dereference( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public static dereference(api: string | OpenAPI.Document): void; + public static dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public static dereference(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public static dereference(api: string | OpenAPI.Document): Promise; public static dereference(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public static dereference( @@ -151,24 +111,14 @@ declare class OpenAPIParser { * * This also eliminates the risk of circular references, so the API definition can be safely serialized using `JSON.stringify()`. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#bundleapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#bundleapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the bundled API definition object */ - public bundle(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public bundle( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public bundle( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public bundle(api: string | OpenAPI.Document): void; + public bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public bundle(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public bundle(api: string | OpenAPI.Document): Promise; public bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public bundle( @@ -182,24 +132,14 @@ declare class OpenAPIParser { * * This also eliminates the risk of circular references, so the API definition can be safely serialized using `JSON.stringify()`. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#bundleapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#bundleapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive the bundled API definition object */ - public static bundle(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public static bundle( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public static bundle( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public static bundle(api: string | OpenAPI.Document): void; + public static bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public static bundle(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public static bundle(api: string | OpenAPI.Document): Promise; public static bundle(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public static bundle( @@ -213,24 +153,14 @@ declare class OpenAPIParser { * * Parses the given OpenAPI definition file (in JSON or YAML format), and returns it as a JavaScript object. This method `does not` resolve `$ref` pointers or dereference anything. It simply parses one file and returns it. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#parseapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#parseapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. The path can be absolute or relative. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page. * @param options (optional) - * @param callback (optional) A callback that will receive the parsed OpenAPI definition object, or an error */ - public parse(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public parse( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public parse( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public parse(api: string | OpenAPI.Document): void; + public parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public parse(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public parse(api: string | OpenAPI.Document): Promise; public parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public parse( @@ -244,24 +174,14 @@ declare class OpenAPIParser { * * Parses the given OpenAPI definition file (in JSON or YAML format), and returns it as a JavaScript object. This method `does not` resolve `$ref` pointers or dereference anything. It simply parses one file and returns it. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#parseapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#parseapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. The path can be absolute or relative. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page. * @param options (optional) - * @param callback (optional) A callback that will receive the parsed OpenAPI definition object, or an error */ - public static parse(api: string | OpenAPI.Document, callback: OpenAPIParser.ApiCallback): void; - public static parse( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; - public static parse( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.ApiCallback, - ): void; + public static parse(api: string | OpenAPI.Document): void; + public static parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public static parse(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public static parse(api: string | OpenAPI.Document): Promise; public static parse(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public static parse( @@ -275,24 +195,14 @@ declare class OpenAPIParser { * * Resolves all JSON references (`$ref` pointers) in the given OpenAPI definition file. If it references any other files/URLs, then they will be downloaded and resolved as well. This method **does not** dereference anything. It simply gives you a `$Refs` object, which is a map of all the resolved references and their values. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#resolveapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#resolveapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive a `$Refs` object */ - public resolve(api: string | OpenAPI.Document, callback: OpenAPIParser.$RefsCallback): void; - public resolve( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.$RefsCallback, - ): void; - public resolve( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.$RefsCallback, - ): void; + public resolve(api: string | OpenAPI.Document): void; + public resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public resolve(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public resolve(api: string | OpenAPI.Document): Promise; public resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public resolve( @@ -306,24 +216,14 @@ declare class OpenAPIParser { * * Resolves all JSON references (`$ref` pointers) in the given OpenAPI definition file. If it references any other files/URLs, then they will be downloaded and resolved as well. This method **does not** dereference anything. It simply gives you a `$Refs` object, which is a map of all the resolved references and their values. * - * See https://apitools.dev/swagger-parser/docs/swagger-parser.html#resolveapi-options-callback + * See https://github.com/readmeio/openapi-parser/blob/main/docs/openapi-parser.md#resolveapi-options * * @param api An OpenAPI definition, or the file path or URL of an OpenAPI definition. See the `parse` method for more info. * @param options (optional) - * @param callback (optional) A callback that will receive a `$Refs` object */ - public static resolve(api: string | OpenAPI.Document, callback: OpenAPIParser.$RefsCallback): void; - public static resolve( - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.$RefsCallback, - ): void; - public static resolve( - baseUrl: string, - api: string | OpenAPI.Document, - options: OpenAPIParser.Options, - callback: OpenAPIParser.$RefsCallback, - ): void; + public static resolve(api: string | OpenAPI.Document): void; + public static resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; + public static resolve(baseUrl: string, api: string | OpenAPI.Document, options: OpenAPIParser.Options): void; public static resolve(api: string | OpenAPI.Document): Promise; public static resolve(api: string | OpenAPI.Document, options: OpenAPIParser.Options): Promise; public static resolve( @@ -335,11 +235,8 @@ declare class OpenAPIParser { // eslint-disable-next-line no-redeclare declare namespace OpenAPIParser { - export type ApiCallback = (err: Error | null, api?: OpenAPI.Document) => any; - export type $RefsCallback = (err: Error | null, $refs?: $Refs) => any; - /** - * See https://apitools.dev/swagger-parser/docs/options.html + * See https://github.com/readmeio/openapi-parser/blob/main/docs/options.md */ export interface Options { /** @@ -502,24 +399,24 @@ declare namespace OpenAPIParser { } /** - * When you call the resolve method, the value that gets passed to the callback function (or Promise) is a $Refs object. This same object is accessible via the parser.$refs property of OpenAPIParser objects. + * When you call the resolve method, the value that is returned is a $Refs object. This same object is accessible via the parser.$refs property of OpenAPIParser objects. * * This object is a map of JSON References and their resolved values. It also has several convenient helper methods that make it easy for you to navigate and manipulate the JSON References. * - * See https://apitools.dev/swagger-parser/docs/refs.html + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md */ export class $Refs { /** * This property is true if the API definition contains any circular references. You may want to check this property before serializing the dereferenced API definition as JSON, since JSON.stringify() does not support circular references by default. * - * See https://apitools.dev/swagger-parser/docs/refs.html#circular + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#circular */ public circular: boolean; /** * Returns the paths/URLs of all the files in your API definition (including the main API definition file). * - * See https://apitools.dev/swagger-parser/docs/refs.html#pathstypes + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#pathstypes * * @param types (optional) Optionally only return certain types of paths ("file", "http", etc.) */ @@ -528,7 +425,7 @@ declare namespace OpenAPIParser { /** * Returns a map of paths/URLs and their correspond values. * - * See https://apitools.dev/swagger-parser/docs/refs.html#valuestypes + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#valuestypes * * @param types (optional) Optionally only return values from certain locations ("file", "http", etc.) */ @@ -537,7 +434,7 @@ declare namespace OpenAPIParser { /** * Returns `true` if the given path exists in the OpenAPI definition; otherwise, returns `false` * - * See https://apitools.dev/swagger-parser/docs/refs.html#existsref + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#existsref * * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash */ @@ -546,7 +443,7 @@ declare namespace OpenAPIParser { /** * Gets the value at the given path in the OpenAPI definition. Throws an error if the path does not exist. * - * See https://apitools.dev/swagger-parser/docs/refs.html#getref + * See https://github.com/readmeio/openapi-parser/blob/main/docs/refs.md#getref * * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash */ diff --git a/lib/index.js b/lib/index.js index ad30fdc2..6c7d193f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,7 +3,6 @@ const { ono } = require('@jsdevtools/ono'); const $RefParser = require('@readme/json-schema-ref-parser'); const dereference = require('@readme/json-schema-ref-parser/lib/dereference'); const normalizeArgs = require('@readme/json-schema-ref-parser/lib/normalize-args'); -const maybe = require('call-me-maybe'); const Options = require('./options'); const util = require('./util'); @@ -52,10 +51,9 @@ Object.defineProperty(OpenAPIParser.prototype, 'api', { * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. * @param {ParserOptions} [options] - Options that determine how the API is parsed - * @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object. * @returns {Promise} - The returned promise resolves with the parsed API object. */ -OpenAPIParser.prototype.parse = async function (path, api, options, callback) { +OpenAPIParser.prototype.parse = async function (path, api, options) { const args = normalizeArgs(arguments); args.options = new Options(args.options); @@ -106,9 +104,9 @@ OpenAPIParser.prototype.parse = async function (path, api, options, callback) { } // Looks good! - return maybe(args.callback, Promise.resolve(schema)); + return Promise.resolve(schema); } catch (err) { - return maybe(args.callback, Promise.reject(err)); + return Promise.reject(err); } }; @@ -119,10 +117,9 @@ OpenAPIParser.prototype.parse = async function (path, api, options, callback) { * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. * @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated - * @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object. * @returns {Promise} - The returned promise resolves with the parsed API object. */ -OpenAPIParser.validate = function (path, api, options, callback) { +OpenAPIParser.validate = function (path, api, options) { const Class = this; const instance = new Class(); return instance.validate.apply(instance, arguments); @@ -135,10 +132,9 @@ OpenAPIParser.validate = function (path, api, options, callback) { * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`. * @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated - * @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object. * @returns {Promise} - The returned promise resolves with the parsed API object. */ -OpenAPIParser.prototype.validate = async function (path, api, options, callback) { +OpenAPIParser.prototype.validate = async function (path, api, options) { const me = this; const args = normalizeArgs(arguments); args.options = new Options(args.options); @@ -178,9 +174,9 @@ OpenAPIParser.prototype.validate = async function (path, api, options, callback) validateSpec(me.api); } - return maybe(args.callback, Promise.resolve(me.schema)); + return Promise.resolve(me.schema); } catch (err) { - return maybe(args.callback, Promise.reject(err)); + return Promise.reject(err); } }; diff --git a/package-lock.json b/package-lock.json index a9471a55..61c0acdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,7 @@ "@readme/json-schema-ref-parser": "^1.2.0", "@readme/openapi-schemas": "^3.1.0", "ajv": "^8.12.0", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.1" + "ajv-draft-04": "^1.0.0" }, "devDependencies": { "@readme/eslint-config": "^14.1.2", diff --git a/package.json b/package.json index c6f39c6f..d4bcc244 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "name": "James Messinger", "url": "https://jamesmessinger.com" }, - "homepage": "https://apitools.dev/swagger-parser/", "repository": { "type": "git", "url": "https://github.com/readmeio/openapi-parser.git" @@ -51,8 +50,7 @@ "@readme/json-schema-ref-parser": "^1.2.0", "@readme/openapi-schemas": "^3.1.0", "ajv": "^8.12.0", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.1" + "ajv-draft-04": "^1.0.0" }, "peerDependencies": { "openapi-types": ">=7" diff --git a/test/specs/callbacks-promises/callbacks-promises-error.yaml b/test/specs/awaited-behavior/awaited-behavior-error.yaml similarity index 100% rename from test/specs/callbacks-promises/callbacks-promises-error.yaml rename to test/specs/awaited-behavior/awaited-behavior-error.yaml diff --git a/test/specs/awaited-behavior/awaited-behavior.test.ts b/test/specs/awaited-behavior/awaited-behavior.test.ts new file mode 100644 index 00000000..0970aacf --- /dev/null +++ b/test/specs/awaited-behavior/awaited-behavior.test.ts @@ -0,0 +1,50 @@ +import { describe, it, expect } from 'vitest'; + +import OpenAPIParser from '../../..'; +import path from '../../utils/path'; + +import bundledAPI from './bundled'; +import dereferencedAPI from './dereferenced'; +import parsedAPI from './parsed'; + +function getSchema(method: string) { + switch (method) { + case 'parse': + return parsedAPI; + case 'dereference': + case 'validate': + return dereferencedAPI; + case 'bundle': + return bundledAPI; + default: + throw new Error('Unrecognized schema method called.'); + } +} + +describe('awaited behavior', () => { + describe.each(['parse', 'resolve', 'dereference', 'bundle', 'validate'])('%s method', method => { + it('should resolve upon a success', async () => { + const parser = new OpenAPIParser(); + const result = await parser[method](path.rel('specs/awaited-behavior/awaited-behavior.yaml')); + + expect(result).to.be.an('object'); + expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/awaited-behavior/awaited-behavior.yaml')]); + + if (method === 'resolve') { + expect(result).to.equal(parser.$refs); + } else { + expect(result).to.equal(parser.schema); + + // Make sure the API was parsed correctly + const expected = getSchema(method); + expect(result).to.deep.equal(expected); + } + }); + + it('should reject upon a failure', async () => { + await expect( + OpenAPIParser[method](path.rel('specs/awaited-behavior/awaited-behavior-error.yaml')), + ).rejects.toThrow(SyntaxError); + }); + }); +}); diff --git a/test/specs/callbacks-promises/callbacks-promises.yaml b/test/specs/awaited-behavior/awaited-behavior.yaml similarity index 100% rename from test/specs/callbacks-promises/callbacks-promises.yaml rename to test/specs/awaited-behavior/awaited-behavior.yaml diff --git a/test/specs/callbacks-promises/bundled.js b/test/specs/awaited-behavior/bundled.js similarity index 100% rename from test/specs/callbacks-promises/bundled.js rename to test/specs/awaited-behavior/bundled.js diff --git a/test/specs/callbacks-promises/dereferenced.js b/test/specs/awaited-behavior/dereferenced.js similarity index 100% rename from test/specs/callbacks-promises/dereferenced.js rename to test/specs/awaited-behavior/dereferenced.js diff --git a/test/specs/callbacks-promises/parsed.js b/test/specs/awaited-behavior/parsed.js similarity index 100% rename from test/specs/callbacks-promises/parsed.js rename to test/specs/awaited-behavior/parsed.js diff --git a/test/specs/callbacks-promises/callbacks-promises.test.ts b/test/specs/callbacks-promises/callbacks-promises.test.ts deleted file mode 100644 index b3b353d0..00000000 --- a/test/specs/callbacks-promises/callbacks-promises.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { describe, it, expect } from 'vitest'; - -import OpenAPIParser from '../../..'; -import path from '../../utils/path'; - -import bundledAPI from './bundled'; -import dereferencedAPI from './dereferenced'; -import parsedAPI from './parsed'; - -function getSchema(method: string) { - switch (method) { - case 'parse': - return parsedAPI; - case 'dereference': - case 'validate': - return dereferencedAPI; - case 'bundle': - return bundledAPI; - default: - throw new Error('Unrecognized schema method called.'); - } -} - -describe('Callback & Promise syntax', () => { - describe.each(['parse', 'resolve', 'dereference', 'bundle', 'validate'])('%s method', method => { - it('should call the callback function upon success', () => { - return new Promise((resolve, reject) => { - const parser = new OpenAPIParser(); - parser[method](path.rel('specs/callbacks-promises/callbacks-promises.yaml'), (err, result) => { - try { - expect(err).to.equal(null); - expect(result).to.be.an('object'); - expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/callbacks-promises/callbacks-promises.yaml')]); - - if (method === 'resolve') { - expect(result).to.equal(parser.$refs); - } else { - expect(result).to.equal(parser.schema); - - // Make sure the API was parsed correctly - const expected = getSchema(method); - expect(result).to.deep.equal(expected); - } - resolve(true); - } catch (e) { - reject(e); - } - }); - }); - }); - - it('should call the callback function upon failure', () => { - return new Promise((resolve, reject) => { - OpenAPIParser[method](path.rel('specs/callbacks-promises/callbacks-promises-error.yaml'), (err, result) => { - try { - expect(err).to.be.an.instanceOf(SyntaxError); - expect(result).to.equal(undefined); - resolve(true); - } catch (e) { - reject(e); - } - }); - }); - }); - - it('should resolve the Promise upon success', () => { - const parser = new OpenAPIParser(); - return parser[method](path.rel('specs/callbacks-promises/callbacks-promises.yaml')).then(result => { - expect(result).to.be.an('object'); - expect(parser.$refs.paths()).to.deep.equal([path.abs('specs/callbacks-promises/callbacks-promises.yaml')]); - - if (method === 'resolve') { - expect(result).to.equal(parser.$refs); - } else { - expect(result).to.equal(parser.schema); - - // Make sure the API was parsed correctly - const expected = getSchema(method); - expect(result).to.deep.equal(expected); - } - }); - }); - - it('should reject the Promise upon failure', async () => { - await expect( - OpenAPIParser[method](path.rel('specs/callbacks-promises/callbacks-promises-error.yaml')), - ).rejects.toThrow(SyntaxError); - }); - }); -}); From 139d169546afcded97bc97f01e50c77e50f32b11 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Thu, 30 Jan 2025 13:15:49 -0800 Subject: [PATCH 2/2] chore: modernizing a few bits of code --- lib/index.js | 138 ++++++++++++++++----------------- lib/validators/spec/openapi.js | 4 +- lib/validators/spec/swagger.js | 8 +- 3 files changed, 71 insertions(+), 79 deletions(-) diff --git a/lib/index.js b/lib/index.js index 6c7d193f..8b95bba4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -57,57 +57,53 @@ OpenAPIParser.prototype.parse = async function (path, api, options) { const args = normalizeArgs(arguments); args.options = new Options(args.options); - try { - const schema = await $RefParser.prototype.parse.call(this, args.path, args.schema, args.options); - - if (schema.swagger) { - // Verify that the parsed object is a Swagger API - if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid Swagger API definition.`); - } else if (typeof schema.swagger === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.'); - } else if (typeof schema.info.version === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); - } else if (schema.swagger !== '2.0') { - throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`); - } - } else { - // Verify that the parsed object is an OpenAPI definition - if (schema.openapi === undefined || schema.info === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); - } else if (schema.paths === undefined) { - if (supported31Versions.includes(schema.openapi)) { - if (schema.webhooks === undefined) { - throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); - } - } else { + const schema = await $RefParser.prototype.parse.call(this, args.path, args.schema, args.options); + + if (schema.swagger) { + // Verify that the parsed object is a Swagger API + if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid Swagger API definition.`); + } else if (typeof schema.swagger === 'number') { + // This is a very common mistake, so give a helpful error message + throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.'); + } else if (typeof schema.info.version === 'number') { + // This is a very common mistake, so give a helpful error message + throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); + } else if (schema.swagger !== '2.0') { + throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`); + } + } else { + // Verify that the parsed object is an OpenAPI definition + if (schema.openapi === undefined || schema.info === undefined) { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); + } else if (schema.paths === undefined) { + // An OpenAPI 3.1 definition must have either `paths` or `webhooks`. If it has neither then + // it's invalid. + if (supported31Versions.includes(schema.openapi)) { + if (schema.webhooks === undefined) { throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); } - } else if (typeof schema.openapi === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('OpenAPI version number must be a string (e.g. "3.0.0") not a number.'); - } else if (typeof schema.info.version === 'number') { - // This is a very common mistake, so give a helpful error message - throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); - } else if (supportedVersions.indexOf(schema.openapi) === -1) { - throw ono.syntax( - `Unsupported OpenAPI version: ${schema.openapi}. ` + - `Swagger Parser only supports versions ${supportedVersions.join(', ')}`, - ); + } else { + throw ono.syntax(`${args.path || 'Supplied schema'} is not a valid OpenAPI definition.`); } - - // This is an OpenAPI v3 schema, check if the "servers" have any relative paths and - // fix them if the content was pulled from a web resource - util.fixOasRelativeServers(schema, args.path); + } else if (typeof schema.openapi === 'number') { + // This is a very common mistake, so give a helpful error message + throw ono.syntax('OpenAPI version number must be a string (e.g. "3.0.0") not a number.'); + } else if (typeof schema.info.version === 'number') { + // This is a very common mistake, so give a helpful error message + throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.'); + } else if (!supportedVersions.includes(schema.openapi)) { + throw ono.syntax( + `Unsupported OpenAPI version: ${schema.openapi}. This library only supports versions ${supportedVersions.join(', ')}`, + ); } - // Looks good! - return Promise.resolve(schema); - } catch (err) { - return Promise.reject(err); + // This is an OpenAPI v3 schema, check if the "servers" have any relative paths and + // fix them if the content was pulled from a web resource + util.fixOasRelativeServers(schema, args.path); } + + return schema; }; /** @@ -146,38 +142,34 @@ OpenAPIParser.prototype.validate = async function (path, api, options) { args.options.dereference.circular = 'ignore'; } - try { - await this.dereference(args.path, args.schema, args.options); - - // Restore the original options, now that we're done dereferencing - args.options.dereference.circular = circular$RefOption; - - if (args.options.validate.schema) { - // Validate the API against the Swagger schema - // NOTE: This is safe to do, because we haven't dereferenced circular $refs yet - validateSchema(me.api, args.options); - - if (me.$refs.circular) { - if (circular$RefOption === true) { - // The API has circular references, - // so we need to do a second-pass to fully-dereference it - dereference(me, args.options); - } else if (circular$RefOption === false) { - // The API has circular references, and they're not allowed, so throw an error - throw ono.reference('The API contains circular references'); - } - } - } + await this.dereference(args.path, args.schema, args.options); - if (args.options.validate.spec) { - // Validate the API against the Swagger spec - validateSpec(me.api); + // Restore the original options, now that we're done dereferencing + args.options.dereference.circular = circular$RefOption; + + if (args.options.validate.schema) { + // Validate the API against the Swagger schema + // NOTE: This is safe to do, because we haven't dereferenced circular $refs yet + validateSchema(me.api, args.options); + + if (me.$refs.circular) { + if (circular$RefOption === true) { + // The API has circular references, + // so we need to do a second-pass to fully-dereference it + dereference(me, args.options); + } else if (circular$RefOption === false) { + // The API has circular references, and they're not allowed, so throw an error + throw ono.reference('The API contains circular references'); + } } + } - return Promise.resolve(me.schema); - } catch (err) { - return Promise.reject(err); + if (args.options.validate.spec) { + // Validate the API against the Swagger spec + validateSpec(me.api); } + + return me.schema; }; /** diff --git a/lib/validators/spec/openapi.js b/lib/validators/spec/openapi.js index 1103d3ae..ab7ba46c 100644 --- a/lib/validators/spec/openapi.js +++ b/lib/validators/spec/openapi.js @@ -26,7 +26,7 @@ function validateSpec(api) { const path = api.paths[pathName]; const pathId = `/paths${pathName}`; - if (path && pathName.indexOf('/') === 0) { + if (path && pathName.startsWith('/')) { validatePath(api, path, pathId, operationIds); } }); @@ -69,7 +69,7 @@ function validatePath(api, path, pathId, operationIds) { if (operation) { const declaredOperationId = operation.operationId; if (declaredOperationId) { - if (operationIds.indexOf(declaredOperationId) === -1) { + if (!operationIds.includes(declaredOperationId)) { operationIds.push(declaredOperationId); } else { throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); diff --git a/lib/validators/spec/swagger.js b/lib/validators/spec/swagger.js index 2e81739e..d23d98d4 100644 --- a/lib/validators/spec/swagger.js +++ b/lib/validators/spec/swagger.js @@ -23,7 +23,7 @@ function validateSpec(api) { const path = api.paths[pathName]; const pathId = `/paths${pathName}`; - if (path && pathName.indexOf('/') === 0) { + if (path && pathName.startsWith('/')) { validatePath(api, path, pathId, operationIds); } }); @@ -58,7 +58,7 @@ function validatePath(api, path, pathId, operationIds) { if (operation) { const declaredOperationId = operation.operationId; if (declaredOperationId) { - if (operationIds.indexOf(declaredOperationId) === -1) { + if (!operationIds.includes(declaredOperationId)) { operationIds.push(declaredOperationId); } else { throw ono.syntax(`Validation failed. Duplicate operation id '${declaredOperationId}'`); @@ -281,7 +281,7 @@ function validateResponse(code, response, responseId) { if (response.schema) { const validTypes = schemaTypes.concat('file'); - if (validTypes.indexOf(response.schema.type) === -1) { + if (!validTypes.includes(response.schema.type)) { throw ono.syntax( `Validation failed. ${responseId} has an invalid response schema type (${response.schema.type})`, ); @@ -299,7 +299,7 @@ function validateResponse(code, response, responseId) { * @param {string[]} validTypes - An array of the allowed schema types */ function validateSchema(schema, schemaId, validTypes) { - if (validTypes.indexOf(schema.type) === -1) { + if (!validTypes.includes(schema.type)) { throw ono.syntax(`Validation failed. ${schemaId} has an invalid type (${schema.type})`); }