Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(oas-normalize): reworking how .validate() works, new .convert() method #920

Merged
merged 9 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Install deps and run tests
run: npm cit
- name: Install deps
run: npm ci

- name: Run tests
run: npm test

- name: Run benchmarks
run: npm run bench
Comment on lines +36 to +37
Copy link
Member Author

@erunion erunion Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be rad if there were a github action to post in a PR comment if the benchmark results show any impacts, positive or neg, to performance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeahh 100%. Like i said in slack, i think you'd have to store the benchmark results somewhere and compare the current run to a known baseline or something.


lint:
runs-on: ubuntu-latest
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"scripts": {
"alex": "alex .",
"attw": "npx lerna run attw --stream",
"bench": "vitest bench",
"build": "npx lerna run build --stream",
"clean": "npx lerna clean",
"lint": "npm run lint:types && npm run lint:js && npm run prettier",
Expand Down
162 changes: 95 additions & 67 deletions packages/oas-normalize/README.md
erunion marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

<p align="center">
Tooling for converting, validating, and parsing OpenAPI, Swagger, and Postman API definitions
Tooling for converting, validating, and parsing OpenAPI, Swagger, and Postman API definitions.
</p>

<p align="center">
Expand All @@ -27,126 +27,154 @@ npm install oas-normalize

## Usage

```javascript
```ts
import OASNormalize from 'oas-normalize';
// const { default: OASNormalize } = require('oas-normalize'); // If you're using CJS.

const oas = new OASNormalize(
'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore-expanded.yaml',
// ...or a string, path, JSON blob, whatever you've got.
// ...or a JSON object, YAML, a file path, stringified JSON, whatever you have.
);

oas
await oas
.validate()
.then(definition => {
// Definition will always be JSON, and valid.
console.log(definition);
.then(() => {
// The API definition is valid!
})
.catch(err => {
console.log(err);
console.error(err);
});
```

### `#bundle()`
> [!WARNING]
> Support for Postman collections is experimental. If you've supplied a Postman collection to the library, it will **always** be converted to OpenAPI, using [`@readme/postman-to-openapi`](https://npm.im/@readme/postman-to-openapi) before doing any bundling, validating, etc.

> **Note**
>
> Because Postman collections don't support `$ref` pointers, this method will automatically upconvert a Postman collection to OpenAPI if supplied one.
### `.load()`
Copy link
Member Author

@erunion erunion Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.load() has long been silently marked as private with a @private JSDoc but we actually use it in a number of apps of ours so might as well make it actually public and documented.


Load and retrive the API definition that `oas-normalize` was initialized with. Every method of `oas-normalize` utilizes this internally however if you would like to retrieve the original API _definition_ supplied (for example if all you had was a URL, a file path, or a buffer), you can use `.load()` to automatically resolve and return its contents.

```ts
const file = await oas.load();
console.log(file);
```

### `.bundle()`

Bundle up the given API definition, resolving any external `$ref` pointers in the process.

```js
await oas.bundle().then(definition => {
console.log(definition);
});
```ts
const definition = await oas.bundle();
console.log(definition);
```

### `#deref()`
### `.convert()`

> **Note**
>
> Because Postman collections don't support `$ref` pointers, this method will automatically upconvert a Postman collection to OpenAPI if supplied one.
Convert a given API definition into an OpenAPI definition JSON object.

```ts
await oas
.convert()
.then(definition => {
// Definition will always be an OpenAPI JSON object, regardless if a
// Swagger definition, Postman collection, or even YAML was supplied.
console.log(definition);
})
.catch(err => {
console.error(err);
});
```

### `.deref()`

Dereference the given API definition, resolving all `$ref` pointers in the process.

```js
await oas.deref().then(definition => {
console.log(definition);
});
```ts
const definition = await oas.bundle();
console.log(definition);
```

### `#validate({ convertToLatest?: boolean })`
### `.validate()`

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.
Validate a given API definition. This supports Swagger 2.0 and OpenAPI 3.x API definitions, as well as Postman 2.x collections.

Please note that if you've supplied a Postman collection to the library it will **always** be converted to OpenAPI, using [@readme/postman-to-openapi](https://npm.im/@readme/postman-to-openapi), and we will only validate resulting OpenAPI definition.

```js
await oas.validate().then(definition => {
console.log(definition);
});
```ts
try {
await oas.validate();
// The API definition is valid!
} catch (err) {
console.error(err);
}
```

#### Options
#### Error Handling

<!-- prettier-ignore-start -->
| Option | Type | Description |
| :--- | :--- | :--- |
| `convertToLatest` | Boolean | By default `#validate` will not upconvert Swagger API definitions to OpenAPI so if you wish for this to happen, supply `true`. |
<!-- prettier-ignore-end -->
All thrown validation error messages that direct the user to the line(s) where their errors are present:

#### Error Handling
```
OpenAPI schema validation failed.

For validation errors, when available, you'll get back an object:

```js
{
"details": [
// Ajv pathing errors. For example:
/* {
"instancePath": "/components/securitySchemes/tlsAuth",
"schemaPath": "#/properties/securitySchemes/patternProperties/%5E%5Ba-zA-Z0-9%5C.%5C-_%5D%2B%24/oneOf",
"keyword": "oneOf",
"params": { "passingSchemas": null },
"message": "must match exactly one schema in oneOf"
}, */
]
}
REQUIRED must have required property 'url'

7 | },
8 | "servers": [
> 9 | {
| ^ ☹️ url is missing here!
10 | "urll": "http://petstore.swagger.io/v2"
11 | }
12 | ],
```

`message` is almost always there, but `path` is less dependable.
However if you would like to programatically access this information the `SyntaxError` error that is thrown contains a `details` array of [AJV](https://npm.im/ajv) errors:
erunion marked this conversation as resolved.
Show resolved Hide resolved

```json
[
{
"instancePath": "/servers/0",
"schemaPath": "#/required",
"keyword": "required",
"params": { "missingProperty": "url" },
"message": "must have required property 'url'",
},
{
"instancePath": "/servers/0",
"schemaPath": "#/additionalProperties",
"keyword": "additionalProperties",
"params": { "additionalProperty": "urll" },
"message": "must NOT have additional properties",
},
];
```

### `#version()`
### `.version()`

Load and retrieve version information about a supplied API definition.

```js
await oas.version().then(({ specification, version }) => {
console.log(specification); // openapi
console.log(version); // 3.1.0
});
```ts
const { specification, version } = await oas.version();

console.log(specification); // openapi
console.log(version); // 3.1.0
```

### Options

##### Enable local paths

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:
For security reasons, you need to opt into allowing fetching by a local path. To enable this supply the `enablePaths` option to the class instance:

```js
```ts
const oas = new OASNormalize('./petstore.json', { enablePaths: true });
```

##### Colorized errors

If you wish errors from `.validate()` to be styled and colorized, supply `colorizeErrors: true` to your instance of `OASNormalize`:
If you wish errors from `.validate()` to be styled and colorized, supply `colorizeErrors: true` to the class instance:

```js
```ts
const oas = new OASNormalize('https://example.com/petstore.json', {
colorizeErrors: true,
});
```

Error messages will look like such:
When enabled thrown validation error messages will now resemble the following:

<img src="https://user-images.githubusercontent.com/33762/137796648-7e1157c2-cee4-466e-9129-dd2a743dd163.png" width="600" />
1 change: 1 addition & 0 deletions packages/oas-normalize/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
},
"scripts": {
"attw": "attw --pack --format table-flipped",
"bench": "echo 'Please run benchmarks from the root!' && exit 1",
"build": "tsup",
"lint": "npm run lint:types && npm run lint:js",
"lint:js": "eslint . --ext .js,.ts --ignore-path ../../.gitignore",
Expand Down
Loading
Loading