oas-normalize 7.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,26 +1,35 @@
1
- OpenAPI 3.x or Swagger 2.0? YAML or JSON? URL, path, string or object? Who cares! It just works.
1
+ <p align="center">
2
+ <a href="https://npm.im/oas-normalize">
3
+ <img src="https://user-images.githubusercontent.com/33762/200434622-23946869-1965-46f8-8deb-f284b8d0b92c.png" alt="oas-normalize" />
4
+ </a>
5
+ </p>
2
6
 
3
- This module uses a bunch of other great modules to do the heavy lifting, and normalizes everything!
7
+ <p align="center">
8
+ Tooling for converting, valiating, and parsing OpenAPI, Swagger, and Postman API definitions
9
+ </p>
4
10
 
5
- [![Build](https://github.com/readmeio/oas-normalize/workflows/CI/badge.svg)](https://github.com/readmeio/oas-normalize/) [![](https://img.shields.io/npm/v/oas-normalize)](https://npm.im/oas-normalize)
11
+ <p align="center">
12
+ <a href="https://npm.im/oas-normalize"><img src="https://img.shields.io/npm/v/oas-normalize.svg?style=for-the-badge" alt="NPM Version"></a>
13
+ <a href="https://npm.im/oas-normalize"><img src="https://img.shields.io/node/v/oas-normalize.svg?style=for-the-badge" alt="Node Version"></a>
14
+ <a href="https://npm.im/oas-normalize"><img src="https://img.shields.io/npm/l/oas-normalize.svg?style=for-the-badge" alt="MIT License"></a>
15
+ <a href="https://github.com/readmeio/oas-normalize"><img src="https://img.shields.io/github/workflow/status/readmeio/oas-normalize/CI.svg?style=for-the-badge" alt="Build status"></a>
16
+ </p>
6
17
 
7
- [![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.com)
8
-
9
- # Install
18
+ ## Installation
10
19
 
11
20
  ```bash
12
- npm install oas-normalize --save
21
+ npm install oas-normalize
13
22
  ```
14
23
 
15
- # Usage
24
+ ## Usage
16
25
 
17
26
  ```javascript
18
27
  import OASNormalize from 'oas-normalize';
19
28
  // const { default: OASNormalize } = require('oas-normalize'); // If you're using CJS.
20
29
 
21
30
  const oas = new OASNormalize(
22
- // Or a string, pathname, JSON blob, whatever
23
31
  'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore-expanded.yaml'
32
+ // ...or a string, path, JSON blob, whatever you've got.
24
33
  );
25
34
 
26
35
  oas
@@ -34,7 +43,55 @@ oas
34
43
  });
35
44
  ```
36
45
 
37
- # Errors
46
+ ### `#bundle()`
47
+
48
+ > **Note**
49
+ >
50
+ > Because Postman collections don't support `$ref` pointers, this method will automatically upconvert a Postman collection to OpenAPI if supplied one.
51
+
52
+ Bundle up the given API definition, resolving any external `$ref` pointers in the process.
53
+
54
+ ```js
55
+ await oas.bundle().then(definition => {
56
+ console.log(definition);
57
+ });
58
+ ```
59
+
60
+ ### `#deref()`
61
+
62
+ > **Note**
63
+ >
64
+ > Because Postman collections don't support `$ref` pointers, this method will automatically upconvert a Postman collection to OpenAPI if supplied one.
65
+
66
+ Dereference the given API definition, resolving all `$ref` pointers in the process.
67
+
68
+ ```js
69
+ await oas.deref().then(definition => {
70
+ console.log(definition);
71
+ });
72
+ ```
73
+
74
+ ### `#validate({ convertToLatest?: boolean })`
75
+
76
+ Validate and optionally convert to OpenAPI, a given API definition. This supports Swagger 2.0, OpenAPI 3.x API definitions as well as Postman 2.x collections.
77
+
78
+ Please note that if you've supplied a Postman collection to the library it will **always** be converted to OpenAPI, using [postman-to-openapi](https://github.com/joolfe/postman-to-openapi), and we will only validate resulting OpenAPI definition.
79
+
80
+ ```js
81
+ await oas.validate().then(definition => {
82
+ console.log(definition);
83
+ });
84
+ ```
85
+
86
+ #### Options
87
+
88
+ <!-- prettier-ignore-start -->
89
+ | Option | Type | Description |
90
+ | :--- | :--- | :--- |
91
+ | `convertToLatest` | Boolean | By default `#validate` will not upconvert Swagger API definitions to OpenAPI so if you wish for this to happen, supply `true`. |
92
+ <!-- prettier-ignore-end -->
93
+
94
+ #### Error Handling
38
95
 
39
96
  For validation errors, when available, you'll get back an object:
40
97
 
@@ -55,35 +112,9 @@ For validation errors, when available, you'll get back an object:
55
112
 
56
113
  `message` is almost always there, but `path` is less dependable.
57
114
 
58
- # Helper Functions
59
-
60
- > **Note:** All of these functions are promise-driven.
61
-
62
- If you want some more functionality, you can do anything here:
63
-
64
- <!--
65
- Prettier's table formatting sucks, hence the ignore block below.
66
- -->
67
- <!-- prettier-ignore-start -->
68
- | Function | What it does |
69
- | :--- | :--- |
70
- | `.load()` | Just load the file, valid or not, as JSON |
71
- | `.bundle()` | Bring together all files into one JSON blob (but retain `$ref` pointers) |
72
- | `.deref()` | Resolve `$ref` pointers |
73
- | `.validate([convertToLatest?])` | Validate the whole thing! |
74
- <!-- prettier-ignore-end -->
75
-
76
- # Other Little Features
77
-
78
- ### Always Return OpenAPI 3.x
79
-
80
- If you want `.validate()` to always return an OpenAPI 3.x definition, supply `true` as its argument:
81
-
82
- ```js
83
- OASNormalize.validate(true).then(...);
84
- ```
115
+ ### Options
85
116
 
86
- ### Enable Local Paths
117
+ ##### Enable local paths
87
118
 
88
119
  For security reasons, you need to opt into allowing fetching by a local path. To enable it supply the `enablePaths` option to the class instance:
89
120
 
@@ -91,7 +122,7 @@ For security reasons, you need to opt into allowing fetching by a local path. To
91
122
  const oas = new OASNormalize('./petstore.json', { enablePaths: true });
92
123
  ```
93
124
 
94
- ### Colorized errors
125
+ ##### Colorized errors
95
126
 
96
127
  If you wish errors from `.validate()` to be styled and colorized, supply `colorizeErrors: true` to your instance of `OASNormalize`:
97
128
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  import type { OpenAPI } from 'openapi-types';
2
+ import * as utils from './lib/utils';
2
3
  export declare type Options = {
3
4
  colorizeErrors?: boolean;
4
5
  enablePaths?: boolean;
5
6
  };
7
+ export declare const isAPIDefinition: typeof utils.isAPIDefinition;
8
+ export declare const getAPIDefinitionType: typeof utils.getAPIDefinitionType;
6
9
  export default class OASNormalize {
7
10
  cache: {
8
11
  bundle?: false | OpenAPI.Document;
@@ -13,10 +16,12 @@ export default class OASNormalize {
13
16
  opts: Options;
14
17
  type: boolean | string;
15
18
  constructor(file: any, opts?: Options);
19
+ /**
20
+ * @private
21
+ */
16
22
  load(): Promise<any>;
17
23
  /**
18
- * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
19
- * the process.
24
+ * Bundle up the given API definition, resolving any external `$ref` pointers in the process.
20
25
  *
21
26
  */
22
27
  bundle(): Promise<import("openapi-types").OpenAPIV2.Document<{}> | import("openapi-types").OpenAPIV3.Document<{}> | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
@@ -57,7 +62,7 @@ export default class OASNormalize {
57
62
  components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
58
63
  }>, "components">)>;
59
64
  /**
60
- * Dereference the given OpenAPI or Swagger.
65
+ * Dereference the given API definition.
61
66
  *
62
67
  */
63
68
  deref(): Promise<import("openapi-types").OpenAPIV2.Document<{}> | import("openapi-types").OpenAPIV3.Document<{}> | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
@@ -98,14 +103,10 @@ export default class OASNormalize {
98
103
  components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
99
104
  }>, "components">)>;
100
105
  /**
101
- * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
102
- * OpenAPI in the process if you wish.
103
- *
104
- */
105
- validate(convertToLatest?: boolean): Promise<any>;
106
- /**
107
- * Retrieve the OpenAPI or Swagger version of the current API definition.
106
+ * Validate, and potentially convert to OpenAPI, a given API definition.
108
107
  *
109
108
  */
110
- version(): Promise<string>;
109
+ validate(opts?: {
110
+ convertToLatest?: boolean;
111
+ }): Promise<any>;
111
112
  }
package/dist/index.js CHANGED
@@ -73,11 +73,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
73
73
  return (mod && mod.__esModule) ? mod : { "default": mod };
74
74
  };
75
75
  exports.__esModule = true;
76
+ exports.getAPIDefinitionType = exports.isAPIDefinition = void 0;
76
77
  var fs_1 = __importDefault(require("fs"));
77
78
  var openapi_parser_1 = __importDefault(require("@readme/openapi-parser"));
78
79
  var node_fetch_1 = __importDefault(require("node-fetch"));
80
+ var postman_to_openapi_1 = __importDefault(require("postman-to-openapi"));
79
81
  var swagger2openapi_1 = __importDefault(require("swagger2openapi"));
80
82
  var utils = __importStar(require("./lib/utils"));
83
+ exports.isAPIDefinition = utils.isAPIDefinition;
84
+ exports.getAPIDefinitionType = utils.getAPIDefinitionType;
81
85
  var OASNormalize = /** @class */ (function () {
82
86
  function OASNormalize(file, opts) {
83
87
  this.file = file;
@@ -89,7 +93,9 @@ var OASNormalize = /** @class */ (function () {
89
93
  deref: false
90
94
  };
91
95
  }
92
- // Internal API for the most part
96
+ /**
97
+ * @private
98
+ */
93
99
  OASNormalize.prototype.load = function () {
94
100
  return __awaiter(this, void 0, void 0, function () {
95
101
  var resolve, _a, resp, contents;
@@ -133,8 +139,7 @@ var OASNormalize = /** @class */ (function () {
133
139
  });
134
140
  };
135
141
  /**
136
- * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
137
- * the process.
142
+ * Bundle up the given API definition, resolving any external `$ref` pointers in the process.
138
143
  *
139
144
  */
140
145
  OASNormalize.prototype.bundle = function () {
@@ -144,6 +149,15 @@ var OASNormalize = /** @class */ (function () {
144
149
  if (this.cache.bundle)
145
150
  return [2 /*return*/, Promise.resolve(this.cache.bundle)];
146
151
  return [2 /*return*/, this.load()
152
+ .then(function (schema) {
153
+ // Though Postman collections don't support `$ref` pointers for us to bundle we'll still
154
+ // upconvert it to an OpenAPI definition file so our returned dataset is always one of
155
+ // those for a Postman dataset.
156
+ if (utils.isPostman(schema)) {
157
+ return (0, postman_to_openapi_1["default"])(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse);
158
+ }
159
+ return schema;
160
+ })
147
161
  .then(function (schema) { return openapi_parser_1["default"].bundle(schema); })
148
162
  .then(function (bundle) {
149
163
  _this.cache.bundle = bundle;
@@ -153,7 +167,7 @@ var OASNormalize = /** @class */ (function () {
153
167
  });
154
168
  };
155
169
  /**
156
- * Dereference the given OpenAPI or Swagger.
170
+ * Dereference the given API definition.
157
171
  *
158
172
  */
159
173
  OASNormalize.prototype.deref = function () {
@@ -163,6 +177,15 @@ var OASNormalize = /** @class */ (function () {
163
177
  if (this.cache.deref)
164
178
  return [2 /*return*/, Promise.resolve(this.cache.deref)];
165
179
  return [2 /*return*/, this.load()
180
+ .then(function (schema) {
181
+ // Though Postman collections don't support `$ref` pointers for us to dereference we'll
182
+ // still upconvert it to an OpenAPI definition file so our returned dataset is always one
183
+ // of those for a Postman dataset.
184
+ if (utils.isPostman(schema)) {
185
+ return (0, postman_to_openapi_1["default"])(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse);
186
+ }
187
+ return schema;
188
+ })
166
189
  .then(function (schema) { return openapi_parser_1["default"].dereference(schema); })
167
190
  .then(function (dereferenced) {
168
191
  _this.cache.deref = dereferenced;
@@ -172,54 +195,58 @@ var OASNormalize = /** @class */ (function () {
172
195
  });
173
196
  };
174
197
  /**
175
- * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
176
- * OpenAPI in the process if you wish.
198
+ * Validate, and potentially convert to OpenAPI, a given API definition.
177
199
  *
178
200
  */
179
- OASNormalize.prototype.validate = function (convertToLatest) {
180
- if (convertToLatest === void 0) { convertToLatest = false; }
201
+ OASNormalize.prototype.validate = function (opts) {
202
+ if (opts === void 0) { opts = { convertToLatest: false }; }
181
203
  return __awaiter(this, void 0, void 0, function () {
182
- var colorizeErrors;
204
+ var convertToLatest, colorizeErrors;
183
205
  var _this = this;
184
206
  return __generator(this, function (_a) {
207
+ convertToLatest = opts.convertToLatest;
185
208
  colorizeErrors = this.opts.colorizeErrors;
186
- return [2 /*return*/, this.load().then(function (schema) { return __awaiter(_this, void 0, void 0, function () {
209
+ return [2 /*return*/, this.load()
210
+ .then(function (schema) { return __awaiter(_this, void 0, void 0, function () {
211
+ return __generator(this, function (_a) {
212
+ if (!utils.isPostman(schema)) {
213
+ return [2 /*return*/, schema];
214
+ }
215
+ return [2 /*return*/, (0, postman_to_openapi_1["default"])(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse)];
216
+ });
217
+ }); })
218
+ .then(function (schema) { return __awaiter(_this, void 0, void 0, function () {
187
219
  var baseVersion, clonedSchema;
188
220
  return __generator(this, function (_a) {
189
- baseVersion = parseInt(utils.version(schema), 10);
190
- if (baseVersion === 1) {
191
- return [2 /*return*/, Promise.reject(new Error('Swagger v1.2 is unsupported.'))];
221
+ if (!utils.isSwagger(schema) && !utils.isOpenAPI(schema)) {
222
+ return [2 /*return*/, Promise.reject(new Error('The supplied API definition is unsupported.'))];
192
223
  }
193
- else if (baseVersion === 2 || baseVersion === 3) {
194
- clonedSchema = JSON.parse(JSON.stringify(schema));
195
- return [2 /*return*/, openapi_parser_1["default"]
196
- .validate(clonedSchema, {
197
- validate: {
198
- colorizeErrors: colorizeErrors
199
- }
200
- })
201
- .then(function () {
202
- if (!convertToLatest) {
203
- return schema;
204
- }
205
- return swagger2openapi_1["default"].convertObj(schema, { anchors: true }).then(function (options) {
206
- return options.openapi;
207
- });
208
- })["catch"](function (err) { return Promise.reject(err); })];
224
+ else if (utils.isSwagger(schema)) {
225
+ baseVersion = parseInt(schema.swagger, 10);
226
+ if (baseVersion === 1) {
227
+ return [2 /*return*/, Promise.reject(new Error('Swagger v1.2 is unsupported.'))];
228
+ }
209
229
  }
210
- return [2 /*return*/, Promise.reject(new Error('The supplied API definition is unsupported.'))];
230
+ clonedSchema = JSON.parse(JSON.stringify(schema));
231
+ return [2 /*return*/, openapi_parser_1["default"]
232
+ .validate(clonedSchema, {
233
+ validate: {
234
+ colorizeErrors: colorizeErrors
235
+ }
236
+ })
237
+ .then(function () {
238
+ if (!convertToLatest) {
239
+ return schema;
240
+ }
241
+ return swagger2openapi_1["default"]
242
+ .convertObj(schema, { anchors: true })
243
+ .then(function (options) { return options.openapi; });
244
+ })["catch"](function (err) { return Promise.reject(err); })];
211
245
  });
212
246
  }); })];
213
247
  });
214
248
  });
215
249
  };
216
- /**
217
- * Retrieve the OpenAPI or Swagger version of the current API definition.
218
- *
219
- */
220
- OASNormalize.prototype.version = function () {
221
- return this.load().then(function (schema) { return utils.version(schema); });
222
- };
223
250
  return OASNormalize;
224
251
  }());
225
252
  exports["default"] = OASNormalize;
@@ -1,21 +1,47 @@
1
- import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
1
  /**
3
- * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
2
+ * Determine if a given variable is a `Buffer`.
4
3
  *
5
4
  */
6
- export declare function version(schema: OpenAPIV2.Document & OpenAPIV3.Document & OpenAPIV3_1.Document): string;
5
+ export declare function isBuffer(obj: any): any;
7
6
  /**
8
- * Determine if a given variable is a `Buffer`.
7
+ * Determine the type of a given variable. Returns `false` if unrecognized.
9
8
  *
10
9
  */
11
- export declare function isBuffer(obj: any): any;
10
+ export declare function getType(obj: any): false | "path" | "json" | "buffer" | "string-json" | "string-yaml" | "url";
11
+ /**
12
+ * Determine if a given schema if an OpenAPI definition.
13
+ *
14
+ */
15
+ export declare function isOpenAPI(schema: Record<string, unknown>): boolean;
16
+ /**
17
+ * Determine if a given schema is a Postman collection.
18
+ *
19
+ * Unfortunately the Postman schema spec doesn't have anything like `openapi` or `swagger` for us
20
+ * to look at but it does require that `info` and `item` be present and as `item` doesn't exist in
21
+ * OpenAPI or Swagger we can use the combination of those two properties to determine if what we
22
+ * have is a Postman collection.
23
+ *
24
+ * @see {@link https://schema.postman.com/json/collection/v2.0.0/collection.json}
25
+ * @see {@link https://schema.postman.com/json/collection/v2.1.0/collection.json}
26
+ */
27
+ export declare function isPostman(schema: Record<string, unknown>): boolean;
28
+ /**
29
+ * Determine if a given schema if an Swagger definition.
30
+ *
31
+ */
32
+ export declare function isSwagger(schema: Record<string, unknown>): boolean;
12
33
  /**
13
34
  * Convert a YAML blob or stringified JSON object into a JSON object.
14
35
  *
15
36
  */
16
37
  export declare function stringToJSON(string: string | Record<string, unknown>): any;
17
38
  /**
18
- * Determine the type of a given variable. Returns `false` if unrecognized.
39
+ * Determine if a given schema is an API definition that we can support.
19
40
  *
20
41
  */
21
- export declare function getType(obj: any): false | "path" | "json" | "buffer" | "string-json" | "string-yaml" | "url";
42
+ export declare function isAPIDefinition(schema: Record<string, unknown>): boolean;
43
+ /**
44
+ * Retrieve the type of API definition that a given schema is.
45
+ *
46
+ */
47
+ export declare function getAPIDefinitionType(schema: Record<string, unknown>): "unknown" | "openapi" | "postman" | "swagger";
package/dist/lib/utils.js CHANGED
@@ -3,16 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  exports.__esModule = true;
6
- exports.getType = exports.stringToJSON = exports.isBuffer = exports.version = void 0;
6
+ exports.getAPIDefinitionType = exports.isAPIDefinition = exports.stringToJSON = exports.isSwagger = exports.isPostman = exports.isOpenAPI = exports.getType = exports.isBuffer = void 0;
7
7
  var js_yaml_1 = __importDefault(require("js-yaml"));
8
- /**
9
- * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
10
- *
11
- */
12
- function version(schema) {
13
- return schema.swagger || schema.openapi;
14
- }
15
- exports.version = version;
16
8
  /**
17
9
  * Determine if a given variable is a `Buffer`.
18
10
  *
@@ -24,20 +16,6 @@ function isBuffer(obj) {
24
16
  obj.constructor.isBuffer(obj));
25
17
  }
26
18
  exports.isBuffer = isBuffer;
27
- /**
28
- * Convert a YAML blob or stringified JSON object into a JSON object.
29
- *
30
- */
31
- function stringToJSON(string) {
32
- if (typeof string === 'object') {
33
- return string;
34
- }
35
- else if (string.match(/^\s*{/)) {
36
- return JSON.parse(string);
37
- }
38
- return js_yaml_1["default"].load(string);
39
- }
40
- exports.stringToJSON = stringToJSON;
41
19
  /**
42
20
  * Determine the type of a given variable. Returns `false` if unrecognized.
43
21
  *
@@ -65,3 +43,73 @@ function getType(obj) {
65
43
  return false;
66
44
  }
67
45
  exports.getType = getType;
46
+ /**
47
+ * Determine if a given schema if an OpenAPI definition.
48
+ *
49
+ */
50
+ function isOpenAPI(schema) {
51
+ return !!schema.openapi;
52
+ }
53
+ exports.isOpenAPI = isOpenAPI;
54
+ /**
55
+ * Determine if a given schema is a Postman collection.
56
+ *
57
+ * Unfortunately the Postman schema spec doesn't have anything like `openapi` or `swagger` for us
58
+ * to look at but it does require that `info` and `item` be present and as `item` doesn't exist in
59
+ * OpenAPI or Swagger we can use the combination of those two properties to determine if what we
60
+ * have is a Postman collection.
61
+ *
62
+ * @see {@link https://schema.postman.com/json/collection/v2.0.0/collection.json}
63
+ * @see {@link https://schema.postman.com/json/collection/v2.1.0/collection.json}
64
+ */
65
+ function isPostman(schema) {
66
+ return !!schema.info && !!schema.item;
67
+ }
68
+ exports.isPostman = isPostman;
69
+ /**
70
+ * Determine if a given schema if an Swagger definition.
71
+ *
72
+ */
73
+ function isSwagger(schema) {
74
+ return !!schema.swagger;
75
+ }
76
+ exports.isSwagger = isSwagger;
77
+ /**
78
+ * Convert a YAML blob or stringified JSON object into a JSON object.
79
+ *
80
+ */
81
+ function stringToJSON(string) {
82
+ if (typeof string === 'object') {
83
+ return string;
84
+ }
85
+ else if (string.match(/^\s*{/)) {
86
+ return JSON.parse(string);
87
+ }
88
+ return js_yaml_1["default"].load(string);
89
+ }
90
+ exports.stringToJSON = stringToJSON;
91
+ /**
92
+ * Determine if a given schema is an API definition that we can support.
93
+ *
94
+ */
95
+ function isAPIDefinition(schema) {
96
+ return isOpenAPI(schema) || isPostman(schema) || isSwagger(schema);
97
+ }
98
+ exports.isAPIDefinition = isAPIDefinition;
99
+ /**
100
+ * Retrieve the type of API definition that a given schema is.
101
+ *
102
+ */
103
+ function getAPIDefinitionType(schema) {
104
+ if (isOpenAPI(schema)) {
105
+ return 'openapi';
106
+ }
107
+ else if (isPostman(schema)) {
108
+ return 'postman';
109
+ }
110
+ else if (isSwagger(schema)) {
111
+ return 'swagger';
112
+ }
113
+ return 'unknown';
114
+ }
115
+ exports.getAPIDefinitionType = getAPIDefinitionType;
Binary file
Binary file
package/docs/logo.png ADDED
Binary file
package/package.json CHANGED
@@ -1,26 +1,27 @@
1
1
  {
2
2
  "name": "oas-normalize",
3
- "version": "7.1.0",
4
- "description": "OpenAPI 3.x or Swagger 2.0? YAML or JSON? URL, path, string or object? Who cares! It just works.",
3
+ "version": "8.0.0",
4
+ "description": "Tooling for converting, valiating, and parsing OpenAPI, Swagger, and Postman API definitions",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "engines": {
8
8
  "node": ">=14"
9
9
  },
10
- "tags": [
10
+ "keywords": [
11
11
  "api",
12
+ "apidoc",
12
13
  "apis",
14
+ "documentation",
15
+ "microservice",
16
+ "postman",
17
+ "oai",
18
+ "oas",
13
19
  "openapi",
14
- "swagger",
20
+ "openapi document",
15
21
  "openapi initiative",
16
- "openapi specification",
17
22
  "openapi spec",
18
- "openapi document",
19
- "oai",
20
- "oas",
21
- "apidoc",
22
- "microservice",
23
- "documentation"
23
+ "openapi specification",
24
+ "swagger"
24
25
  ],
25
26
  "repository": {
26
27
  "type": "git",
@@ -45,17 +46,17 @@
45
46
  "js-yaml": "^4.1.0",
46
47
  "node-fetch": "^2.6.1",
47
48
  "openapi-types": "^12.0.0",
49
+ "postman-to-openapi": "^2.7.1",
48
50
  "swagger2openapi": "^7.0.8"
49
51
  },
50
52
  "devDependencies": {
51
- "@readme/eslint-config": "^10.0.0",
52
- "@readme/oas-examples": "^5.1.1",
53
+ "@readme/eslint-config": "^10.1.3",
54
+ "@readme/oas-examples": "^5.8.1",
53
55
  "@types/jest": "^28.1.6",
54
56
  "@types/js-yaml": "^4.0.5",
55
57
  "@types/node-fetch": "^2.6.2",
56
58
  "eslint": "^8.21.0",
57
59
  "jest": "^28.0.3",
58
- "jet": "^0.8.1",
59
60
  "nock": "^13.2.4",
60
61
  "prettier": "^2.7.1",
61
62
  "ts-jest": "^28.0.7",
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import fs from 'fs';
4
4
 
5
5
  import openapiParser from '@readme/openapi-parser';
6
6
  import fetch from 'node-fetch';
7
+ import postmanToOpenAPI from 'postman-to-openapi';
7
8
 
8
9
  import converter from 'swagger2openapi';
9
10
 
@@ -14,6 +15,9 @@ export type Options = {
14
15
  enablePaths?: boolean;
15
16
  };
16
17
 
18
+ export const isAPIDefinition = utils.isAPIDefinition;
19
+ export const getAPIDefinitionType = utils.getAPIDefinitionType;
20
+
17
21
  export default class OASNormalize {
18
22
  cache: {
19
23
  bundle?: false | OpenAPI.Document;
@@ -44,7 +48,9 @@ export default class OASNormalize {
44
48
  };
45
49
  }
46
50
 
47
- // Internal API for the most part
51
+ /**
52
+ * @private
53
+ */
48
54
  async load() {
49
55
  if (this.cache.load) return Promise.resolve(this.cache.load);
50
56
 
@@ -82,14 +88,23 @@ export default class OASNormalize {
82
88
  }
83
89
 
84
90
  /**
85
- * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
86
- * the process.
91
+ * Bundle up the given API definition, resolving any external `$ref` pointers in the process.
87
92
  *
88
93
  */
89
94
  async bundle() {
90
95
  if (this.cache.bundle) return Promise.resolve(this.cache.bundle);
91
96
 
92
97
  return this.load()
98
+ .then(schema => {
99
+ // Though Postman collections don't support `$ref` pointers for us to bundle we'll still
100
+ // upconvert it to an OpenAPI definition file so our returned dataset is always one of
101
+ // those for a Postman dataset.
102
+ if (utils.isPostman(schema)) {
103
+ return postmanToOpenAPI(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse);
104
+ }
105
+
106
+ return schema;
107
+ })
93
108
  .then(schema => openapiParser.bundle(schema))
94
109
  .then(bundle => {
95
110
  this.cache.bundle = bundle;
@@ -98,13 +113,23 @@ export default class OASNormalize {
98
113
  }
99
114
 
100
115
  /**
101
- * Dereference the given OpenAPI or Swagger.
116
+ * Dereference the given API definition.
102
117
  *
103
118
  */
104
119
  async deref() {
105
120
  if (this.cache.deref) return Promise.resolve(this.cache.deref);
106
121
 
107
122
  return this.load()
123
+ .then(schema => {
124
+ // Though Postman collections don't support `$ref` pointers for us to dereference we'll
125
+ // still upconvert it to an OpenAPI definition file so our returned dataset is always one
126
+ // of those for a Postman dataset.
127
+ if (utils.isPostman(schema)) {
128
+ return postmanToOpenAPI(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse);
129
+ }
130
+
131
+ return schema;
132
+ })
108
133
  .then(schema => openapiParser.dereference(schema))
109
134
  .then(dereferenced => {
110
135
  this.cache.deref = dereferenced;
@@ -113,19 +138,31 @@ export default class OASNormalize {
113
138
  }
114
139
 
115
140
  /**
116
- * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
117
- * OpenAPI in the process if you wish.
141
+ * Validate, and potentially convert to OpenAPI, a given API definition.
118
142
  *
119
143
  */
120
- async validate(convertToLatest = false) {
144
+ async validate(opts: { convertToLatest?: boolean } = { convertToLatest: false }) {
145
+ const convertToLatest = opts.convertToLatest;
121
146
  const colorizeErrors = this.opts.colorizeErrors;
122
147
 
123
- return this.load().then(async schema => {
124
- const baseVersion = parseInt(utils.version(schema), 10);
148
+ return this.load()
149
+ .then(async schema => {
150
+ if (!utils.isPostman(schema)) {
151
+ return schema;
152
+ }
153
+
154
+ return postmanToOpenAPI(JSON.stringify(schema), null, { outputFormat: 'json' }).then(JSON.parse);
155
+ })
156
+ .then(async schema => {
157
+ if (!utils.isSwagger(schema) && !utils.isOpenAPI(schema)) {
158
+ return Promise.reject(new Error('The supplied API definition is unsupported.'));
159
+ } else if (utils.isSwagger(schema)) {
160
+ const baseVersion = parseInt(schema.swagger, 10);
161
+ if (baseVersion === 1) {
162
+ return Promise.reject(new Error('Swagger v1.2 is unsupported.'));
163
+ }
164
+ }
125
165
 
126
- if (baseVersion === 1) {
127
- return Promise.reject(new Error('Swagger v1.2 is unsupported.'));
128
- } else if (baseVersion === 2 || baseVersion === 3) {
129
166
  /**
130
167
  * `openapiParser.validate()` dereferences schemas at the same time as validation and does
131
168
  * not give us an option to disable this. Since all we already have a dereferencing method
@@ -146,22 +183,11 @@ export default class OASNormalize {
146
183
  return schema;
147
184
  }
148
185
 
149
- return converter.convertObj(schema, { anchors: true }).then((options: { openapi: OpenAPI.Document }) => {
150
- return options.openapi;
151
- });
186
+ return converter
187
+ .convertObj(schema, { anchors: true })
188
+ .then((options: { openapi: OpenAPI.Document }) => options.openapi);
152
189
  })
153
190
  .catch(err => Promise.reject(err));
154
- }
155
-
156
- return Promise.reject(new Error('The supplied API definition is unsupported.'));
157
- });
158
- }
159
-
160
- /**
161
- * Retrieve the OpenAPI or Swagger version of the current API definition.
162
- *
163
- */
164
- version() {
165
- return this.load().then(schema => utils.version(schema));
191
+ });
166
192
  }
167
193
  }
package/src/lib/utils.ts CHANGED
@@ -1,15 +1,5 @@
1
- import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
-
3
1
  import YAML from 'js-yaml';
4
2
 
5
- /**
6
- * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
7
- *
8
- */
9
- export function version(schema: OpenAPIV2.Document & OpenAPIV3.Document & OpenAPIV3_1.Document) {
10
- return schema.swagger || schema.openapi;
11
- }
12
-
13
3
  /**
14
4
  * Determine if a given variable is a `Buffer`.
15
5
  *
@@ -23,20 +13,6 @@ export function isBuffer(obj: any) {
23
13
  );
24
14
  }
25
15
 
26
- /**
27
- * Convert a YAML blob or stringified JSON object into a JSON object.
28
- *
29
- */
30
- export function stringToJSON(string: string | Record<string, unknown>) {
31
- if (typeof string === 'object') {
32
- return string;
33
- } else if (string.match(/^\s*{/)) {
34
- return JSON.parse(string);
35
- }
36
-
37
- return YAML.load(string);
38
- }
39
-
40
16
  /**
41
17
  * Determine the type of a given variable. Returns `false` if unrecognized.
42
18
  *
@@ -61,3 +37,72 @@ export function getType(obj: any) {
61
37
 
62
38
  return false;
63
39
  }
40
+
41
+ /**
42
+ * Determine if a given schema if an OpenAPI definition.
43
+ *
44
+ */
45
+ export function isOpenAPI(schema: Record<string, unknown>) {
46
+ return !!schema.openapi;
47
+ }
48
+
49
+ /**
50
+ * Determine if a given schema is a Postman collection.
51
+ *
52
+ * Unfortunately the Postman schema spec doesn't have anything like `openapi` or `swagger` for us
53
+ * to look at but it does require that `info` and `item` be present and as `item` doesn't exist in
54
+ * OpenAPI or Swagger we can use the combination of those two properties to determine if what we
55
+ * have is a Postman collection.
56
+ *
57
+ * @see {@link https://schema.postman.com/json/collection/v2.0.0/collection.json}
58
+ * @see {@link https://schema.postman.com/json/collection/v2.1.0/collection.json}
59
+ */
60
+ export function isPostman(schema: Record<string, unknown>): boolean {
61
+ return !!schema.info && !!schema.item;
62
+ }
63
+
64
+ /**
65
+ * Determine if a given schema if an Swagger definition.
66
+ *
67
+ */
68
+ export function isSwagger(schema: Record<string, unknown>) {
69
+ return !!schema.swagger;
70
+ }
71
+
72
+ /**
73
+ * Convert a YAML blob or stringified JSON object into a JSON object.
74
+ *
75
+ */
76
+ export function stringToJSON(string: string | Record<string, unknown>) {
77
+ if (typeof string === 'object') {
78
+ return string;
79
+ } else if (string.match(/^\s*{/)) {
80
+ return JSON.parse(string);
81
+ }
82
+
83
+ return YAML.load(string);
84
+ }
85
+
86
+ /**
87
+ * Determine if a given schema is an API definition that we can support.
88
+ *
89
+ */
90
+ export function isAPIDefinition(schema: Record<string, unknown>) {
91
+ return isOpenAPI(schema) || isPostman(schema) || isSwagger(schema);
92
+ }
93
+
94
+ /**
95
+ * Retrieve the type of API definition that a given schema is.
96
+ *
97
+ */
98
+ export function getAPIDefinitionType(schema: Record<string, unknown>) {
99
+ if (isOpenAPI(schema)) {
100
+ return 'openapi';
101
+ } else if (isPostman(schema)) {
102
+ return 'postman';
103
+ } else if (isSwagger(schema)) {
104
+ return 'swagger';
105
+ }
106
+
107
+ return 'unknown';
108
+ }