From 1afa3cbb06c7bfbf137b51a3add4a89650b8265d Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Mon, 23 Nov 2020 15:39:27 +0100 Subject: [PATCH 01/10] Disabled testing and sentiment analysis for draft --- .github/workflows/pull-request-testing.yml | 1 + .github/workflows/sentiment-analysis.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/pull-request-testing.yml b/.github/workflows/pull-request-testing.yml index 75185d900..4cff6c92a 100644 --- a/.github/workflows/pull-request-testing.yml +++ b/.github/workflows/pull-request-testing.yml @@ -6,6 +6,7 @@ on: jobs: release: + if: github.event.pull_request.draft == false name: 'Run tests' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/sentiment-analysis.yml b/.github/workflows/sentiment-analysis.yml index ac5c945ff..5bebd026c 100644 --- a/.github/workflows/sentiment-analysis.yml +++ b/.github/workflows/sentiment-analysis.yml @@ -23,6 +23,7 @@ on: - edited jobs: test: + if: github.event.pull_request.draft == false name: Checking sentiments runs-on: ubuntu-latest steps: From baa0fdaf6e5acbe0c8740e86598d329487601932 Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Mon, 23 Nov 2020 15:41:09 +0100 Subject: [PATCH 02/10] Revert "Disabled testing and sentiment analysis for draft" This reverts commit 1afa3cbb06c7bfbf137b51a3add4a89650b8265d. --- .github/workflows/pull-request-testing.yml | 1 - .github/workflows/sentiment-analysis.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/pull-request-testing.yml b/.github/workflows/pull-request-testing.yml index 4cff6c92a..75185d900 100644 --- a/.github/workflows/pull-request-testing.yml +++ b/.github/workflows/pull-request-testing.yml @@ -6,7 +6,6 @@ on: jobs: release: - if: github.event.pull_request.draft == false name: 'Run tests' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/sentiment-analysis.yml b/.github/workflows/sentiment-analysis.yml index 5bebd026c..ac5c945ff 100644 --- a/.github/workflows/sentiment-analysis.yml +++ b/.github/workflows/sentiment-analysis.yml @@ -23,7 +23,6 @@ on: - edited jobs: test: - if: github.event.pull_request.draft == false name: Checking sentiments runs-on: ubuntu-latest steps: From 78b6ffa61ed7a5378c05134c1b0739938e0069ab Mon Sep 17 00:00:00 2001 From: "jonas-lt@live.dk" Date: Wed, 14 Jun 2023 14:51:44 +0200 Subject: [PATCH 03/10] change readme --- README.md | 484 ++++++++++++++++++++++++++---------- docs/migrations/v1-to-v2.md | 284 +++++++++++++++++++++ 2 files changed, 639 insertions(+), 129 deletions(-) create mode 100644 docs/migrations/v1-to-v2.md diff --git a/README.md b/README.md index 9ea0b5913..6e499c4a3 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,92 @@ -
-
- AsyncAPI logo -
- JavaScript Parser -
-

- Use this package to parse and validate AsyncAPI documents —either YAML or JSON— in your Node.js or browser application. Updated bundle for the browser is always attached to the GitHub Release. -

+[![AsyncAPI JavaScript Parser](./assets/logo.png)](https://www.asyncapi.com) + +Use this package to validate and parse AsyncAPI documents —either YAML or JSON— in your Node.js or browser application. +Validation is powered by [Spectral](https://github.com/stoplightio/spectral). +Updated bundle for the browser is always attached to the GitHub Release. ![npm](https://img.shields.io/npm/v/@asyncapi/parser?style=for-the-badge) ![npm](https://img.shields.io/npm/dt/@asyncapi/parser?style=for-the-badge) -> :warning: This package doesn't support AsyncAPI 1.x anymore. We recommend to upgrade to the latest AsyncAPI version using the [AsyncAPI converter](https://github.com/asyncapi/converter-js). If you need to convert documents on the fly, you may use the [Node.js](https://github.com/asyncapi/converter-js) or [Go](https://github.com/asyncapi/converter-go) converters. +> **Warning** +> This package doesn't support AsyncAPI 1.x anymore. We recommend to upgrade to the latest AsyncAPI version using the [AsyncAPI converter](https://github.com/asyncapi/converter-js). If you need to convert documents on the fly, you may use the [Node.js](https://github.com/asyncapi/converter-js) or [Go](https://github.com/asyncapi/converter-go) converters. + +> **Warning** +> This package has rewrote the Model API (old one) to [Intent API](https://github.com/asyncapi/parser-api). If you still need to use the old API, read the [Convert to the old API](#convert-to-the-old-api) section. + +> **Note** +> Read the [migration guide from v1 to v2](./docs/migration-guide-v2.md). -- [Install](#install) +- [Installation](#installation) +- [Usage](#usage) - [Examples](#examples) - * [Example passing inline AsyncAPI](#example-passing-inline-asyncapi) - * [Example passing a URL](#example-passing-a-url) + * [Example with parsing](#example-with-parsing) + * [Example with validation](#example-with-validation) * [Example using Avro schemas](#example-using-avro-schemas) * [Example using OpenAPI schemas](#example-using-openapi-schemas) * [Example using RAML data types](#example-using-raml-data-types) + * [Example with performing actions on HTTP source](#example-with-performing-actions-on-http-source) + * [Example with performing actions on file source](#example-with-performing-actions-on-file-source) + * [Example with stringify and unstringify parsed document](#example-with-stringify-and-unstringify-parsed-document) - [API documentation](#api-documentation) -- [Using in the browser](#using-in-the-browser) -- [Custom message parsers](#custom-message-parsers) -- [Error types](#error-types) +- [Using in the browser/SPA applications](#using-in-the-browserspa-applications) +- [Custom schema parsers](#custom-schema-parsers) + * [Official supported custom schema parsers](#official-supported-custom-schema-parsers) - [Custom extensions](#custom-extensions) - [Circular references](#circular-references) +- [Stringify](#stringify) +- [Convert to the old API](#convert-to-the-old-api) +- [Notes](#notes) + * [Using with Webpack](#using-with-webpack) + * [Testing with [Jest](https://jestjs.io/)](#testing-with-jesthttpsjestjsio) - [Develop](#develop) - [Contributing](#contributing) - [Contributors](#contributors) -## Install +## Installation -``` +```bash npm install @asyncapi/parser +yarn add @asyncapi/parser ``` +The parser by default supports AsyncAPI Schema Format and JSON Schema Format for schemas. For additional formats, check [Custom schema parsers](#custom-schema-parsers) section. + +## Usage + +The package exposes the main class `Parser`, which has two main functions: + +- `validate()` - function that validates the passed AsyncAPI document. Returns array of all possible errors against the validation conditions. +- `parse()` - function that validates the passed AsyncAPI document, and then if it's valid, parses the input. It returns an object that contains: + - `document` object, which is an parsed AsyncAPI document with [`AsyncAPIDocumentInterface`](./src/models/asyncapi.ts) API. If the schema is invalid against the validation conditions, the field has `undefined` value. + - `diagnostics` array that contains all possible errors against the validation conditions. +- `registerSchemaParser()` - function that registers custom schema parsers. For more info, please check [Custom schema parsers](#custom-schema-parsers) section. + +Natively `Parser` class does not contain methods that operate on the source (AsyncAPI document) from a file or URL. However, the package exposes utils that make this possible: + +```ts +import { fromURL, fromFile } from '@asyncapi/parser'; +``` + +Check out the [examples](#examples) of using the above mentioned functionalities. + ## Examples -### Example passing inline AsyncAPI +### Example with parsing -```js -const parser = require('@asyncapi/parser'); +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); -const doc = await parser.parse(` - asyncapi: '2.1.0' +const { document } = await parser.parse(` + asyncapi: '2.4.0' info: - title: Example + title: Example AsyncAPI specification version: '0.1.0' channels: example-channel: @@ -69,149 +104,337 @@ const doc = await parser.parse(` format: date-time `); -console.log(doc.info().title()); -// => Example +if (document) { + // => Example AsyncAPI specification + console.log(document.info().title()); +} ``` -### Example passing a URL +### Example with validation -```js -const parser = require('@asyncapi/parser'); +```ts +import { Parser } from '@asyncapi/parser'; -const doc = await parser.parseFromUrl('https://my.server.com/example-asyncapi.yaml'); +const parser = new Parser(); -console.log(doc.info().title()); -// => Example +// One of the diagnostics will contain an error regarding an unsupported version of AsyncAPI (2.1.37) +const diagnostics = await parser.validate(` + asyncapi: '2.1.37' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); ``` -### Example using Avro schemas +### [Example using Avro schemas](#custom-schema-parsers) Head over to [asyncapi/avro-schema-parser](https://www.github.com/asyncapi/avro-schema-parser) for more information. -### Example using OpenAPI schemas +### [Example using OpenAPI schemas](#custom-schema-parsers) Head over to [asyncapi/openapi-schema-parser](https://www.github.com/asyncapi/openapi-schema-parser) for more information. -### Example using RAML data types +### [Example using RAML data types](#custom-schema-parsers) Head over to [asyncapi/raml-dt-schema-parser](https://www.github.com/asyncapi/raml-dt-schema-parser) for more information. +### Example with performing actions on HTTP source + +```ts +import { Parser, fromURL } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document, diagnostics } = await fromURL(parser, 'https://example.com/').parse(); +``` + +### Example with performing actions on file source + +```ts +import { Parser, fromFile } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document, diagnostics } = await fromFile(parser, './asyncapi.yaml').parse(); +``` + +### [Example with stringify and unstringify parsed document](#stringify) + +```ts +import { Parser, stringify, unstringify } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document } = await parser.parse(` + asyncapi: '2.4.0' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); + +if (document) { + // stringify function returns string type + const stringifiedDocument = stringify(document); + // unstringify function returns new AsyncAPIDocument instance + const unstringifiedDocument = unstringify(stringifiedDocument); +} +``` + ## API documentation -The parser API is generally structured the same way as the AsyncAPI specification, with additional support functions such as `has()`. The parser uses wrapped functions to get the properties stored in JSON. This means in order to get the info object you would use the function `doc.info()` and to get the title inside the info object you call `doc.info().title()`. +Parser-JS API implements a global API definition for all AsyncAPI parser implementations known as the [Parser-API](https://github.com/asyncapi/parser-api). This API is designed having in mind developer experience and resiliency to breaking changes. + +The following table shows a compatibility matrix between this parser, and the [Parser-API](https://github.com/asyncapi/parser-api), as well as the AsyncAPI spec version supported by each release of this parser. + +| Parser-JS | Parser-API | Spec 2.x | +|-----------|----------------------------------------------------------------------|----------| +| 2.x | [1.x](https://github.com/asyncapi/parser-api/blob/master/docs/v1.md) | ✓ | + +- `✓` Fully supported version. +- `-` The AsyncAPI Spec version has features the Parser-JS can't use but the rest are fully supported. +- Empty means not supported version. -See [API documentation](/API.md) for more example and full API reference information. +Additionally to all the methods declared in the [Parser-API](https://github.com/asyncapi/parser-api), this parser might introduce some helper functions like: -## Using in the browser +- `json()` which returns the JSON object of the given object. It is possible to pass as an argument the name of a field in an object and retrieve corresponding value. +- `jsonPath()` which returns the JSON Path of the given object. +- `meta()` which returns the metadata of a given object, like a parsed AsyncAPI Document. -The package contains a built-in version of the parser, which is created via [`browserify`](https://github.com/browserify/browserify). To use it, you need to import the parser into the HTML file as below: +## Using in the browser/SPA applications + +The package contains a built-in version of the parser. To use it, you need to import the parser into the HTML file as below: ```html - + ``` -Or, if you want to use a parser in a JS application of the SPA kind, import the parser as shown below: +Or, if you want to use the parser in a JS SPA-type application where you have a predefined bundler configuration that is difficult to change (e.g. you use [`create-react-app`](https://github.com/facebook/create-react-app)) then you can import the parser as below: ```js -import '@asyncapi/parser/dist/bundle'; +import Parser from '@asyncapi/parser/browser'; -const parser = window['AsyncAPIParser']; -... +const parser = new Parser(); +const { document, diagnostics } = parser.parse(...); ``` -Otherwise, if your application is bundled via bundlers like `webpack`, you can import the parser like a regular package: +> **Note** +> Using the above code, we import the entire bundled parser into application. This may result in a duplicate code in the final application bundle, only if the application uses the same dependencies what the parser. If, on the other hand, you want to have the smallest bundle as possible, we recommend using the following import and properly configure bundler. + +Otherwise, if your application is bundled via bundlers like `webpack` and you can configure it, you can import the parser like a regular package: ```js -import parser from '@asyncapi/parser'; +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); +const { document, diagnostics } = parser.parse(...); ``` -## Custom message parsers - -AsyncAPI doesn't enforce one schema format for messages. You can have payload of your messages described with OpenAPI, Avro, etc. This parser by default parses only AsyncAPI schema format. You can extend it by creating a custom parser and registering it withing the parser: - -1. Create custom parser module that exports two functions: - - ```js - module.exports = { - /* - * message {Object} is the object containing AsyncAPI Message property - * defaultSchemaFormat {String} information about the default schema format mime type - * schemaFormat {String} information about custom schemaFormat mime type provided in AsyncAPI Document - * fileFormat {String} information if provided AsyncAPI Document was JSON or YAML - * parsedAsyncAPIDocument {Object} Full AsyncAPI Document parsed into Object - * pathToPayload {String} path of the message passed to the parser, relative to the root of AsyncAPI Document - */ - parse: ({ message, defaultSchemaFormat, originalAsyncAPIDocument, schemaFormat, fileFormat, parsedAsyncAPIDocument, pathToPayload }) => { /* custom parsing logic */ }, - getMimeTypes: () => [ - '//mime types that will be used as the `schemaFormat` property of the message to specify its mime type', - 'application/vnd.custom.type;version=1.0.0', - 'application/vnd.custom.type+json;version=1.0.0', - ] +> **Note** +> The package uses some native NodeJS modules underneath. If you are building a front-end application you can find more information about the correct configuration for Webpack [here](#webpack). + +In case you just want to check out the latest `bundle.js` without installing the package, we publish one on each GitHub release. You can find it under [this link to the latest release](https://github.com/asyncapi/parser-js/releases/latest/download/bundle.js). + +## Custom schema parsers + +AsyncAPI doesn't enforce one schema format. The payload of the messages can be described with OpenAPI (3.0.0), Avro, etc. This parser by default parses only [AsyncAPI Schema Format](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject) (superset of JSON Schema Format). We can extend it by creating a custom parser and registering it within the parser: + +1. Create custom parser module that exports three functions: + + - `validate` - function that validates (its syntax) used schema. + - `parse` - function that parses the given schema to the [AsyncAPI Schema Format](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject). + - `getMimeTypes` - function that returns the list of mime types that will be used as the `schemaFormat` property to determine the mime type of a given schema. + + Example: + + ```ts + export default { + validate(input) { ... }, + parse(input) { ... }, + getMimeTypes() { + return [ + 'application/vnd.custom.type;version=1.0.0', + 'application/vnd.custom.type+json;version=1.0.0', + ] + } } ``` -2. Before parsing an AsyncAPI document with a parser, register the additional custom schema parser: +2. Before parsing/validating an AsyncAPI document with a parser, register the additional custom schema parser: - ```js - const myCustomParser = require('mycustomParser'); + ```ts + import { Parser } from '@asyncapi/parser'; + import myCustomSchemaParser from './my-custom-schema-parser'; - parser.registerSchemaParser(myCustomParser); + const parser = new Parser(); + parser.registerSchemaParser(myCustomSchemaParser); ``` -## Error types +### Official supported custom schema parsers + +In AsyncAPI Initiative we support below custom schema parsers. To install them, run below comamnds: + +- [Avro schema](https://www.github.com/asyncapi/avro-schema-parser): -This package throws a bunch of different error types. All errors contain a `type` (prefixed by this repo URL) and a `title` field. The following table describes all the errors and the extra fields they include: + ```bash + npm install @asyncapi/avro-schema-parser + yarn add @asyncapi/avro-schema-parser + ``` -|Type|Extra Fields|Description| -|---|---|---| -|`null-or-falsey-document`| None | The AsyncAPI document is null or a JS "falsey" value. -|`invalid-document-type`| None | The AsyncAPI document is not a string nor a JS object. -|`invalid-json`| `detail`, `location` | The AsyncAPI document is not valid JSON. -|`invalid-yaml`| `detail`, `location` | The AsyncAPI document is not valid YAML. -|`impossible-to-convert-to-json`|`detail`|Internally, this parser only handles JSON so it tries to immediately convert the YAML to JSON. This error means this process failed. -|`missing-asyncapi-field`|`parsedJSON`|The AsyncAPI document doesn't have the mandatory `asyncapi` field. -|`unsupported-version`|`detail`, `parsedJSON`, `validationErrors`|The version of the `asyncapi` field is not supported. Typically, this means that you're using a version below 2.0.0. -|`dereference-error`|`parsedJSON`, `refs`|This means the parser tried to resolve and dereference $ref's and the process failed. Typically, this means the $ref it's pointing to doesn't exist. -|`unexpected-error`|`parsedJSON`|We have our code covered with try/catch blocks and you should never see this error. If you see it, please open an issue to let us know. -|`validation-errors`|`parsedJSON`, `validationErrors`|The AsyncAPI document contains errors. See `validationErrors` for more information. -|`impossible-to-register-parser`| None | Registration of custom message parser failed. -|`schema-validation-errors`| `parsedJSON`, `validationErrors` | Schema of the payload provided in the AsyncAPI document is not valid with AsyncAPI schema format. -|`fetch-url-error`| None | The URL provided for fetching AsynAPI document is invalid. +- [OpenAPI (3.0.0) Schema Object](https://www.github.com/asyncapi/openapi-schema-parser): -For more information about the `ParserError` class, [check out the documentation](./API.md#new_ParserError_new). + ```bash + npm install @asyncapi/openapi-schema-parser + yarn add @asyncapi/openapi-schema-parser + ``` + +- [RAML data type](https://www.github.com/asyncapi/raml-dt-schema-parser): + + ```bash + npm install @asyncapi/raml-dt-schema-parser + yarn add @asyncapi/raml-dt-schema-parser + ``` + + > **Note** + > That custom parser works only in the NodeJS environment. Do not use it in browser applications! ## Custom extensions -The parser uses custom extensions to define additional information about the spec. Each has a different purpose but all of them are there to make it much easier to work with the AsyncAPI document. These extensions are prefixed with `x-parser-`. The following extensions are used : +The parser uses custom extensions to define additional information about the spec. Each has a different purpose but all of them are there to make it much easier to work with the AsyncAPI document. These extensions are prefixed with `x-parser-`. The following extensions are used: + - `x-parser-spec-parsed` is used to specify if the AsyncAPI document is already parsed by the parser. Property `x-parser-spec-parsed` is added to the root of the document with the `true` value. -- `x-parser-message-parsed` is used to specify if the message is already parsed by the message parser. Property `x-parser-message-parsed` is added to the root of the document with the `true` value. -- `x-parser-message-name` is used to specify the name of the message if it is not provided. For messages without names, the parser generates anonymous names. Property `x-parser-message-name` is added to a message object with a value that follows this pattern: ``. This value is returned by `message.uid()` when regular `name` property is not present. +- `x-parser-api-version` is used to specify which version of the [Parser-API](https://github.com/asyncapi/parser-api) the parsed AsyncAPI document uses. Property `x-parser-api-version` is added to the root of the document with the `1` value if the parsed document uses [Parser-API](https://github.com/asyncapi/parser-api) in the `v1` version or `0` if document uses old `parser-js` API. +- `x-parser-message-name` is used to specify the name of the message if it is not provided. For messages without names, the parser generates anonymous names. Property `x-parser-message-name` is added to a message object with a value that follows this pattern: ``. This value is returned by `message.id()` (`message.uid()` in the [old API](#convert-to-the-old-api)) when regular `name` property is not present. +- `x-parser-original-payload` holds the original payload of the message. You can use different formats for payloads with the AsyncAPI documents and the parser converts them to. For example, it converts payload described with Avro schema to AsyncAPI schema. The original payload is preserved in the extension. +- [`x-parser-circular`](#circular-references). + +In addition, the [`convertToOldAPI()` function](#convert-to-the-old-api) which converts new API to an old one adds additional extensions: + +- `x-parser-message-parsed` is used to specify if the message is already parsed by the message parser. Property `x-parser-message-parsed` is added to the message object with the `true` value. - `x-parser-schema-id` is used to specify the ID of the schema if it is not provided. For schemas without IDs, the parser generates anonymous names. Property `x-parser-schema-id` is added to every object of a schema with a value that follows this pattern: ``. This value is returned by `schema.uid()` when regular `$id` property is not present. - `x-parser-original-traits` is where traits are stored after they are applied on the AsyncAPI document. The reason is because the original `traits` property is removed. - `x-parser-original-schema-format` holds information about the original schema format of the payload. You can use different schema formats with the AsyncAPI documents and the parser converts them to AsyncAPI schema. This is why different schema format is set, and the original one is preserved in the extension. -- `x-parser-original-payload` holds the original payload of the message. You can use different formats for payloads with the AsyncAPI documents and the parser converts them to. For example, it converts payload described with Avro schema to AsyncAPI schema. The original payload is preserved in the extension. -- [`x-parser-circular`](#circular-references) -- [`x-parser-circular-props`](#circular-references) + +> **Warning** +> All extensions added by the parser (including all properties) should be retrieved using special functions. Names of extensions and their location may change, and their eventual changes will not be announced. ## Circular references Parser dereferences all circular references by default. In addition, to simplify interactions with the parser, the following is added: -- `x-parser-circular` property is added to the root of the AsyncAPI document to indicate that the document contains circular references. Tooling developer that doesn't want to support circular references can use the `hasCircular()` function to check the document and provide a proper message to the user. -- `x-parser-circular` property is added to every schema of array type that is circular. To check if schema is circular or not, you should use `isCircular()` function on a Schema model like `document.components().schema('RecursiveSelf').properties()['selfChildren'].isCircular()`. -- `x-parser-circular-props` property is added to every schema of object type with a list of properties that are circular. To check if a schema has properties with circular references, you should use `hasCircularProps()` function. To get a list of properties with circular references, you should use `circularProps()` function. -## Develop +- `x-parser-circular` property is added to the root of the AsyncAPI document to indicate that the document contains circular references. In old API the Parser exposes `hasCircular()` function to check if given AsyncAPI document has circular references. +- `isCircular()` function is added to the [Schema Model](./src/models/schema.ts) to determine if a given schema is circular with respect to previously occurring schemas in the JSON tree. + +## Stringify -1. Write code and tests. -1. Make sure all tests pass `npm test` -1. Make sure code is well formatted and secure `npm run lint` +Converting a parsed document to a string may be necessary when saving the parsed document to a database, or similar situations where you need to parse the document just once and then reuse it, for optimisation cases. + +For that, the Parser supports the ability to stringify a parsed AsyncAPI document through the `stringify` function exposed by package. This method differs from the native `JSON.stringify(...json)` implementation, in that every reference that occurs (at least twice throughout the document) is converted into a [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) path with a `$ref:` prefix: + +```json +{ + "foo": "$ref:$.some.path.to.the.bar" +} +``` + +To parse a stringified document into an AsyncAPIDocument instance, you must use the `unstringify` function (also exposed by package). It isn't compatible with the native `JSON.parse()` method. It replaces the given references pointed by the [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) path, with an `$ref:` prefix to the original objects. + +A few advantages of this solution: + +- The string remains as small as possible due to the use of [JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901). +- All references (also circular) are preserved. + +Check [example](#example-with-stringify-and-unstringify-parsed-documentstringify). + +## Convert to the old API + +Version `2.0.0` of package introduced a lot of breaking changes, including changing the API of the returned parsed document (parser uses [New API](https://github.com/asyncapi/parser-api)). Due to the fact that a large part of the AsyncAPI tooling ecosystem uses a Parser with the old API and rewriting the tool for the new one can be time-consuming and difficult, the package exposes the `convertToOldAPI()` function to convert new API to old one: + +```js +import { Parser, convertToOldAPI } from '@asyncapi/parser'; + +const parser = new Parser(); +const { document } = parser.parse(...); +const oldAsyncAPIDocument = convertToOldAPI(document); +``` + +> **Warning** +> The old api will be supported only for a certain period of time. The target date for turning off support of the old API is around the end of January 2023. + +## Notes + +### Using with Webpack + +Versions `<5` of Webpack should handle bundling without problems. Due to the fact that Webpack 5 no longer does fallbacks to native NodeJS modules by default we need to install `buffer` package and add fallbacks: + +```js +{ + resolve: { + fallback: { + "fs": false, + "path": false, + "util": false, + "buffer": require.resolve("buffer/"), + } + } +} +``` + +### Testing with [Jest](https://jestjs.io/) + +Using a Parser in an application that is tested using [Jest](https://jestjs.io/), there will probably an error like: + +```bash +Cannot find module 'nimma/legacy' from 'node_modules/@stoplight/spectral-core/dist/runner/runner.js +``` + +It's a problem with Jest, which cannot understand [NodeJS's package exports](https://nodejs.org/api/packages.html). To fix that, should be [enabled ESM support in Jest](https://jestjs.io/docs/ecmascript-modules) or set an appropriate Jest's `moduleNameMapper` config: + +```js +moduleNameMapper: { + '^nimma/legacy$': '/node_modules/nimma/dist/legacy/cjs/index.js', + '^nimma/(.*)': '/node_modules/nimma/dist/cjs/$1', +}, +``` + +## Develop -Release regenerates API documentation and browser bundle, so you do not have to regenerate it manually with `npm run docs` and `npm run prepublishOnly`. +1. Make sure you are using Node.js 16 or higher and npm 8 or higher +2. Write code and tests. +3. Make sure all tests pass `npm test` +4. Make sure code is well formatted and secure `npm run lint` ## Contributing @@ -225,29 +448,32 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + +

Fran Méndez

💬 🐛 💻 📖 🤔 🚧 🔌 👀 ⚠️

Lukasz Gornicki

💬 🐛 💻 📖 🤔 🚧 👀 ⚠️

Jonas Lagoni

💬 🐛 💻 🤔 👀

Maciej Urbańczyk

🐛 💻 👀

Juan Mellado

💻

James Crowley

💻

raisel melian

💻

danielchu

🚇 💻

Jürgen B.

💻

Viacheslav Turovskyi

⚠️

Khuda Dad Nomani

💻 🐛

Aayush Kumar Sahu

⚠️

Jordan Tucker

⚠️ 💻

vishesh13byte

⚠️

Elakya

💻

Fran Méndez

💬 🐛 💻 📖 🤔 🚧 🔌 👀 ⚠️

Lukasz Gornicki

💬 🐛 💻 📖 🤔 🚧 👀 ⚠️

Jonas Lagoni

💬 🐛 💻 🤔 👀

Maciej Urbańczyk

🐛 💻 👀

Juan Mellado

💻

James Crowley

💻

raisel melian

💻

danielchu

🚇 💻

Jürgen B.

💻

Viacheslav Turovskyi

⚠️ 💻

Khuda Dad Nomani

💻 🐛 ⚠️

Aayush Kumar Sahu

⚠️

Jordan Tucker

⚠️ 💻

vishesh13byte

⚠️

Elakya

💻

Dominik Schwank

🐛 💻
diff --git a/docs/migrations/v1-to-v2.md b/docs/migrations/v1-to-v2.md new file mode 100644 index 000000000..e6fc4969f --- /dev/null +++ b/docs/migrations/v1-to-v2.md @@ -0,0 +1,284 @@ +# Migrating from v1 to v2 + +## TL;DR + +- The parser API has been rewritten from the ground up. It's now based on user intents instead of a 1:1 map to the spec. [Read more](#new-intent-api). +- Source code has been rewritten in TypeScript and ESM. This makes the package fully [tree-shakable](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking). [Read more](#typescript-and-esm). +- The parser is now exposed as a class, making it possible to have multiple parser instances with different configurations. [Read more](#parser-class). +- It validates AsyncAPI documents using [Spectral](https://github.com/stoplightio/spectral). This enables better validation and introduces linting capabilities. [Read more](#spectral). +- Introduces validation without parsing via the `.validate()` method. [Read more](#validate-method). +- The `.parse()` method now also returns diagnostics (errors, warnings, and bits of advice). [Read more](#parse-method). +- The `parseFromUrl()` method has been deprecated in favor of the `fromURL` standalone function. [Read more](#parseFromUrl-is-now-deprecated). +- Custom parsers now have a `validate()` method. [Read more](#new-custom-schema-parser-interface). +- The way to pass custom `$ref` resolvers has changed. [Read more](#custom-reference-resolvers). +- Stringify and unstringify the parsed document is now a separate functionality. [Read more](#standalone-stringify-and-unstringify-functionality). +- If there is a need to use an old version of the API, the Parser provides the `convertToOldAPI` functionality. [Read more](#convert-new-api-to-the-old-one). + +### New Intent API + +The old Document API based 1:1 on AsyncAPI Specification has been rewritten to support the new so-called [Intent API](https://github.com/asyncapi/parser-api), which makes it easier to operate on the document and retrieve collections of needed objects from the document. Due to the fact that the new API does not stick firmly to AsyncAPI Specification, it allows for better integration with future versions of AsyncAPI. User does not need to know the new API against the new version of AsyncAPI - all methods will remain the same with the inclusion of new/deprecated fields. Also new API has been enhanced with some additional functionality like retrieving the JSON Path of the object using the `.jsonPath()` method etc. + +### TypeScript and ESM + +The source code of the package has been rewritten to ESM (EcmaScript modules). This enables [tree-shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) and much better integration in browser-based environments. In addition, the package exposes a `cjs` directory containing the CommonJS version of the parser, for compatibility with older environments. Both versions have support for Typescript types. + +### Parser class + +The v1 version of package had only one instance of the Parser which was global. The new version exposes the Parser class, so now it's possible to have several different instances of Parser with different configurations. + +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document } = await parser.parse(`...AsyncAPI document`, ...?options); +``` + +### Powered by [Spectral](https://github.com/stoplightio/spectral) + +[Spectral](https://github.com/stoplightio/spectral) is an open-source API linting tool by [Stoplight](https://stoplight.io/) that allows users to create custom style guides to enforce validations on API design automatically. This allows for better validation and showing all errors in one validation process - the old parser had a huge problem with this. In addition, there is the ability to show good practices though like defining `operationId` for each operation, or enforcing things that are optional but required for a project or company (API governance). Together with [Stoplight](https://stoplight.io/), we have an official partnership and will be improving integration as well as [Spectral](https://github.com/stoplightio/spectral) itself for better quality validation of AsyncAPI documents. + +### `.parse()` method + +The function `.parse()` returns not only parsed AsyncAPi `document` but also possible `diagnostics` array based on validation configuration: + +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document, diagnostics } = await parser.parse(` + asyncapi: '2.4.0' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); + +if (document) { + // => Example AsyncAPI specification + console.log(document.info().title()); +} +``` + +### `.validate()` method + +New version of Parser brings new `.validate()` method (exposed by Parser class) which only validates the AsyncAPI document based on [Spectral](https://github.com/stoplightio/spectral) configuration. This is a useful feature for cases when we only need to know if given AsyncAPI document is valid or not. + +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); + +// One of the diagnostics will contain an error regarding an unsupported version of AsyncAPI (2.1.37) +const diagnostics = await parser.validate(` + asyncapi: '2.1.37' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); +``` + +### `parseFromUrl()` is now deprecated + +The `parseFromUrl()` function has been deprecated in favour of the new `fromURL` function: + +```ts +import { Parser, fromURL } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document } = fromURL(parser, 'https://my.server.com/example-asyncapi.yaml', ...?fetching options).parse(); +// `validate` function is also available: +// fromURL(parser, ...).validate(); +``` + +> **Note** +> Parser exposes similar function to handle file system sources - `fromFile`. + +### Custom reference resolvers + +With the v1 version there was an option to pass a custom resolvers to the reference resolver ([json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)), which allowed custom logic for retrieving reference data - e.g. for mapping data located on the external resources to local data (used by `--map-base-url` flag for [Generator](https://github.com/asyncapi/generator#cli-usage)'s CLI). The current logic is similar, but differs in a few things: + +- custom resolver have to be passes in the `Parser`'s constructor, in the `__unstable.resolver.resolvers` array. +- must define what schema it supports, such as `https`, or `file`. + +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser({ + __unstable: { + resolver: { + resolvers: [ + { + schema: 'customProtocol', + read(uri) { + if (uri.path() === '/someRef') { + return '{"someRef": "value"}'; + } + return '{"anotherRef": "value"}'; + }, + } + ] + } + } +}); + +const asyncApiDocument = { + asyncapi: '2.5.0', + info: { + title: 'Example AsyncApi document', + version: '1.0', + }, + channels: { + someChannel: { + publish: { + operationId: 'publish', + message: { + payload: { + $ref: 'customProtocol:///someRef' + } + } + }, + subscribe: { + operationId: 'subscribe', + message: { + payload: { + $ref: 'customProtocol:///anotherRef' + } + } + }, + } + }, +}; +const { document } = await parser.parse(asyncApiDocument); +// document.json().channels.someChannel.publish.message.payload == { "someRef": "value" }; +// document.json().channels.someChannel.subscribe.message.payload == { "anotherRef": "value" }; +``` + +> **Note** +> As [Spectral](https://github.com/stoplightio/spectral) natively does not support multiple custom resolvers, `Parser` implements wrapper for existing logic in [Spectral](https://github.com/stoplightio/spectral), and due to that fact, custom resolvers need to be passed with `__unstable.resolver.resolvers` array. If [Spectral](https://github.com/stoplightio/spectral) will support out in the box multiple custom resolvers we will include this logic permanently in the `Parser`, currently it is in the "unstable" phase. + +### New Custom Schema Parser interface + +The custom parser interface has been changed to shape including methods: + +- `validate` (**NEW FUNCTION**) - function that validates (its syntax) used schema. +- `parse` - function that parses the given schema to the [AsyncAPI Schema Format](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject). +- `getMimeTypes` - function that returns the list of mime types that will be used as the `schemaFormat` property to determine the mime type of a given schema. + +Example: + +```ts +export default { + validate(input) { ... }, + parse(input) { ... }, + getMimeTypes() { + return [ + 'application/vnd.custom.type;version=1.0.0', + 'application/vnd.custom.type+json;version=1.0.0', + ] + } +} +``` + +Before parsing/validating an AsyncAPI document with a parser, register the additional custom schema parser like in old parser: + +```ts +import { Parser } from '@asyncapi/parser'; +import myCustomSchemaParser from './my-custom-schema-parser'; + +const parser = new Parser(); +parser.registerSchemaParser(myCustomSchemaParser); +``` + +### Standalone stringify and unstringify functionality + +The ability to stringify and unstringify (parsing of stringified AsyncAPI document) was possible as methods in the old `AsyncAPiDocument` instance. Currently, both functions are standalone: + +```ts +import { Parser, stringify, unstringify } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document } = await parser.parse(` + asyncapi: '2.4.0' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); + +if (document) { + // stringify function returns string type + const stringifiedDocument = stringify(document); + // unstringify function returns new AsyncAPIDocument instance + const unstringifiedDocument = unstringify(stringifiedDocument); +} +``` + +### Convert new API to the old one + +Due to the fact that a large part of the AsyncAPI tooling ecosystem uses a Parser with the old API and rewriting the tool for the new one can be time-consuming and difficult, the package exposes the `convertToOldAPI()` function to convert new API to old one: + +```js +import { Parser, convertToOldAPI } from '@asyncapi/parser'; + +const parser = new Parser(); +const { document } = parser.parse(...); +const oldAsyncAPIDocument = convertToOldAPI(document); +``` + +> **Warning** +> The old api will be supported only for a certain period of time. The target date for turning off support of the old API is around the end of March 2023. + +If there is a need to convert an existing instance of the old API to the new one, the library provides the `convertToNewAPI` function: + +```js +import { Parser, convertToNewAPI } from '@asyncapi/parser'; + +const newAsyncAPIDocument = convertToNewAPI(oldDocument); +``` + +> **Warning** +> Due to the fact that the new Parser validates the document more restrictively, it may be that a parsed document in the old way will not work 100% in accordance with the new API. From 07cd948ccbf784ae8cca212ece26d363b9c40fce Mon Sep 17 00:00:00 2001 From: "jonas-lt@live.dk" Date: Wed, 14 Jun 2023 14:57:37 +0200 Subject: [PATCH 04/10] changed link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 279dccbd2..3e9d601e3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Updated bundle for the browser is always attached to the GitHub Release. > This package has rewrote the Model API (old one) to [Intent API](https://github.com/asyncapi/parser-api). If you still need to use the old API, read the [Convert to the old API](#convert-to-the-old-api) section. > **Note** -> Read the [migration guide from v1 to v2](./docs/migration-guide-v2.md). +> Read the [migration guide from v1 to v2](./docs/migrations/v1-to-v2.md). From c19d2e7239ca41368cbb6cec8cb389a7f41922b4 Mon Sep 17 00:00:00 2001 From: "jonas-lt@live.dk" Date: Wed, 14 Jun 2023 14:58:29 +0200 Subject: [PATCH 05/10] adapt gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2c7221588..943fd793b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ node_modules .vscode .DS_Store -/docs +/docs/api.md /coverage /lib /esm From 404e406ee7dd4c669c8e39ec204adea0d0d93735 Mon Sep 17 00:00:00 2001 From: Jonas Lagoni Date: Tue, 20 Jun 2023 14:15:23 +0200 Subject: [PATCH 06/10] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 943fd793b..100f71f58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules .vscode .DS_Store -/docs/api.md /coverage /lib /esm From 18ac4aaf270858b6219fa92f146e4386c29bb982 Mon Sep 17 00:00:00 2001 From: Jonas Lagoni Date: Tue, 20 Jun 2023 14:19:59 +0200 Subject: [PATCH 07/10] Update docs/migrations/v1-to-v2.md Co-authored-by: Sergio Moya <1083296+smoya@users.noreply.github.com> --- docs/migrations/v1-to-v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1-to-v2.md b/docs/migrations/v1-to-v2.md index e6fc4969f..33c370891 100644 --- a/docs/migrations/v1-to-v2.md +++ b/docs/migrations/v1-to-v2.md @@ -16,7 +16,7 @@ ### New Intent API -The old Document API based 1:1 on AsyncAPI Specification has been rewritten to support the new so-called [Intent API](https://github.com/asyncapi/parser-api), which makes it easier to operate on the document and retrieve collections of needed objects from the document. Due to the fact that the new API does not stick firmly to AsyncAPI Specification, it allows for better integration with future versions of AsyncAPI. User does not need to know the new API against the new version of AsyncAPI - all methods will remain the same with the inclusion of new/deprecated fields. Also new API has been enhanced with some additional functionality like retrieving the JSON Path of the object using the `.jsonPath()` method etc. +The old Document API based 1:1 on AsyncAPI Specification has been rewritten to support the new so-called [Parser API](https://github.com/asyncapi/parser-api), which makes it easier to operate on the document and retrieve collections of needed objects from it. Due to the fact that the new API does not stick firmly to the AsyncAPI Specification file structure, it allows for better integration with future versions of AsyncAPI. The user does not need to know the new API against the new version of AsyncAPI - all methods will remain the same with the inclusion of new/deprecated fields. Also new API has been enhanced with some additional functionality like retrieving the JSON Path of the object using the `.jsonPath()` method etc. ### TypeScript and ESM From 1e65b8162a113a5c5abced128dec5175edaf0fe8 Mon Sep 17 00:00:00 2001 From: Jonas Lagoni Date: Tue, 20 Jun 2023 14:37:29 +0200 Subject: [PATCH 08/10] Update docs/migrations/v1-to-v2.md Co-authored-by: Sergio Moya <1083296+smoya@users.noreply.github.com> --- docs/migrations/v1-to-v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v1-to-v2.md b/docs/migrations/v1-to-v2.md index 33c370891..b2e4497c6 100644 --- a/docs/migrations/v1-to-v2.md +++ b/docs/migrations/v1-to-v2.md @@ -34,7 +34,7 @@ const parser = new Parser(); const { document } = await parser.parse(`...AsyncAPI document`, ...?options); ``` -### Powered by [Spectral](https://github.com/stoplightio/spectral) +### Powered by Spectral [Spectral](https://github.com/stoplightio/spectral) is an open-source API linting tool by [Stoplight](https://stoplight.io/) that allows users to create custom style guides to enforce validations on API design automatically. This allows for better validation and showing all errors in one validation process - the old parser had a huge problem with this. In addition, there is the ability to show good practices though like defining `operationId` for each operation, or enforcing things that are optional but required for a project or company (API governance). Together with [Stoplight](https://stoplight.io/), we have an official partnership and will be improving integration as well as [Spectral](https://github.com/stoplightio/spectral) itself for better quality validation of AsyncAPI documents. From e71d70deab5f7657c10de470fe40ac9a1bb04229 Mon Sep 17 00:00:00 2001 From: "jonas-lt@live.dk" Date: Tue, 20 Jun 2023 14:57:40 +0200 Subject: [PATCH 09/10] structural changes and grammarly changes --- docs/migrations/v1-to-v2.md | 284 ++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 140 deletions(-) diff --git a/docs/migrations/v1-to-v2.md b/docs/migrations/v1-to-v2.md index b2e4497c6..0d5b78560 100644 --- a/docs/migrations/v1-to-v2.md +++ b/docs/migrations/v1-to-v2.md @@ -5,129 +5,33 @@ - The parser API has been rewritten from the ground up. It's now based on user intents instead of a 1:1 map to the spec. [Read more](#new-intent-api). - Source code has been rewritten in TypeScript and ESM. This makes the package fully [tree-shakable](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking). [Read more](#typescript-and-esm). - The parser is now exposed as a class, making it possible to have multiple parser instances with different configurations. [Read more](#parser-class). -- It validates AsyncAPI documents using [Spectral](https://github.com/stoplightio/spectral). This enables better validation and introduces linting capabilities. [Read more](#spectral). -- Introduces validation without parsing via the `.validate()` method. [Read more](#validate-method). -- The `.parse()` method now also returns diagnostics (errors, warnings, and bits of advice). [Read more](#parse-method). -- The `parseFromUrl()` method has been deprecated in favor of the `fromURL` standalone function. [Read more](#parseFromUrl-is-now-deprecated). -- Custom parsers now have a `validate()` method. [Read more](#new-custom-schema-parser-interface). -- The way to pass custom `$ref` resolvers has changed. [Read more](#custom-reference-resolvers). -- Stringify and unstringify the parsed document is now a separate functionality. [Read more](#standalone-stringify-and-unstringify-functionality). -- If there is a need to use an old version of the API, the Parser provides the `convertToOldAPI` functionality. [Read more](#convert-new-api-to-the-old-one). - -### New Intent API +- It validates AsyncAPI documents using [Spectral](https://github.com/stoplightio/spectral). This enables better validation and introduces linting capabilities. [Read more](#Powered-by-Spectral). +- API Changes + - Introduces validation without parsing via the `.validate()` method. [Read more](#validate-method). + - The `.parse()` method now also returns diagnostics (errors, warnings, and bits of advice). [Read more](#parse-method). + - The `parseFromUrl()` method has been deprecated in favor of the `fromURL` standalone function. [Read more](#parseFromUrl-is-now-deprecated). + - Custom parsers now have a `validate()` method. [Read more](#new-custom-schema-parser-interface). + - The way to pass custom `$ref` resolvers has changed. [Read more](#custom-reference-resolvers). + - Stringify and unstringify the parsed document is now a separate functionality. [Read more](#standalone-stringify-and-unstringify-functionality). + - If there is a need to use an old version of the API, the Parser provides the `convertToOldAPI` functionality. [Read more](#convert-new-api-to-the-old-one). + +## New Intent API The old Document API based 1:1 on AsyncAPI Specification has been rewritten to support the new so-called [Parser API](https://github.com/asyncapi/parser-api), which makes it easier to operate on the document and retrieve collections of needed objects from it. Due to the fact that the new API does not stick firmly to the AsyncAPI Specification file structure, it allows for better integration with future versions of AsyncAPI. The user does not need to know the new API against the new version of AsyncAPI - all methods will remain the same with the inclusion of new/deprecated fields. Also new API has been enhanced with some additional functionality like retrieving the JSON Path of the object using the `.jsonPath()` method etc. -### TypeScript and ESM +## TypeScript and ESM The source code of the package has been rewritten to ESM (EcmaScript modules). This enables [tree-shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) and much better integration in browser-based environments. In addition, the package exposes a `cjs` directory containing the CommonJS version of the parser, for compatibility with older environments. Both versions have support for Typescript types. -### Parser class - -The v1 version of package had only one instance of the Parser which was global. The new version exposes the Parser class, so now it's possible to have several different instances of Parser with different configurations. - -```ts -import { Parser } from '@asyncapi/parser'; - -const parser = new Parser(); - -const { document } = await parser.parse(`...AsyncAPI document`, ...?options); -``` - -### Powered by Spectral - -[Spectral](https://github.com/stoplightio/spectral) is an open-source API linting tool by [Stoplight](https://stoplight.io/) that allows users to create custom style guides to enforce validations on API design automatically. This allows for better validation and showing all errors in one validation process - the old parser had a huge problem with this. In addition, there is the ability to show good practices though like defining `operationId` for each operation, or enforcing things that are optional but required for a project or company (API governance). Together with [Stoplight](https://stoplight.io/), we have an official partnership and will be improving integration as well as [Spectral](https://github.com/stoplightio/spectral) itself for better quality validation of AsyncAPI documents. - -### `.parse()` method - -The function `.parse()` returns not only parsed AsyncAPi `document` but also possible `diagnostics` array based on validation configuration: - -```ts -import { Parser } from '@asyncapi/parser'; - -const parser = new Parser(); - -const { document, diagnostics } = await parser.parse(` - asyncapi: '2.4.0' - info: - title: Example AsyncAPI specification - version: '0.1.0' - channels: - example-channel: - subscribe: - message: - payload: - type: object - properties: - exampleField: - type: string - exampleNumber: - type: number - exampleDate: - type: string - format: date-time -`); - -if (document) { - // => Example AsyncAPI specification - console.log(document.info().title()); -} -``` - -### `.validate()` method +## Powered by Spectral -New version of Parser brings new `.validate()` method (exposed by Parser class) which only validates the AsyncAPI document based on [Spectral](https://github.com/stoplightio/spectral) configuration. This is a useful feature for cases when we only need to know if given AsyncAPI document is valid or not. +[Spectral](https://github.com/stoplightio/spectral) is an open-source API linting tool by [Stoplight](https://stoplight.io/) that allows users to create custom style guides to enforce validations on API design automatically. This allows for better validation and shows all errors in one validation process - the old parser had a huge problem with this. In addition, there is the ability to show good practices though like defining `operationId` for each operation or enforcing things that are optional but required for a project or company (API governance). Together with [Stoplight](https://stoplight.io/), we have an official partnership and will be improving integration as well as [Spectral](https://github.com/stoplightio/spectral) itself for better quality validation of AsyncAPI documents. -```ts -import { Parser } from '@asyncapi/parser'; +## Custom reference resolvers -const parser = new Parser(); +In v1, there was an option to pass a custom resolver to the reference resolver ([json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)), which allowed custom logic for retrieving reference data - e.g. for mapping data located on the external resources to local data (used by `--map-base-url` flag for [Generator](https://github.com/asyncapi/generator#cli-usage)'s CLI). The current logic is similar, but differs in a few things: -// One of the diagnostics will contain an error regarding an unsupported version of AsyncAPI (2.1.37) -const diagnostics = await parser.validate(` - asyncapi: '2.1.37' - info: - title: Example AsyncAPI specification - version: '0.1.0' - channels: - example-channel: - subscribe: - message: - payload: - type: object - properties: - exampleField: - type: string - exampleNumber: - type: number - exampleDate: - type: string - format: date-time -`); -``` - -### `parseFromUrl()` is now deprecated - -The `parseFromUrl()` function has been deprecated in favour of the new `fromURL` function: - -```ts -import { Parser, fromURL } from '@asyncapi/parser'; - -const parser = new Parser(); - -const { document } = fromURL(parser, 'https://my.server.com/example-asyncapi.yaml', ...?fetching options).parse(); -// `validate` function is also available: -// fromURL(parser, ...).validate(); -``` - -> **Note** -> Parser exposes similar function to handle file system sources - `fromFile`. - -### Custom reference resolvers - -With the v1 version there was an option to pass a custom resolvers to the reference resolver ([json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)), which allowed custom logic for retrieving reference data - e.g. for mapping data located on the external resources to local data (used by `--map-base-url` flag for [Generator](https://github.com/asyncapi/generator#cli-usage)'s CLI). The current logic is similar, but differs in a few things: - -- custom resolver have to be passes in the `Parser`'s constructor, in the `__unstable.resolver.resolvers` array. +- custom resolver has to be passed in the `Parser`'s constructor, in the `__unstable.resolver.resolvers` array. - must define what schema it supports, such as `https`, or `file`. ```ts @@ -184,15 +88,15 @@ const { document } = await parser.parse(asyncApiDocument); ``` > **Note** -> As [Spectral](https://github.com/stoplightio/spectral) natively does not support multiple custom resolvers, `Parser` implements wrapper for existing logic in [Spectral](https://github.com/stoplightio/spectral), and due to that fact, custom resolvers need to be passed with `__unstable.resolver.resolvers` array. If [Spectral](https://github.com/stoplightio/spectral) will support out in the box multiple custom resolvers we will include this logic permanently in the `Parser`, currently it is in the "unstable" phase. +> As [Spectral](https://github.com/stoplightio/spectral) natively does not support multiple custom resolvers, `Parser` implements a wrapper for existing logic in [Spectral](https://github.com/stoplightio/spectral), and due to that fact, custom resolvers need to be passed with `__unstable.resolver.resolvers` array. If [Spectral](https://github.com/stoplightio/spectral) will support out-in-the-box multiple custom resolvers we will include this logic permanently in the `Parser`, currently it is in the "unstable" phase. ### New Custom Schema Parser interface The custom parser interface has been changed to shape including methods: -- `validate` (**NEW FUNCTION**) - function that validates (its syntax) used schema. -- `parse` - function that parses the given schema to the [AsyncAPI Schema Format](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject). -- `getMimeTypes` - function that returns the list of mime types that will be used as the `schemaFormat` property to determine the mime type of a given schema. +- `validate` (**NEW FUNCTION**) - the function that validates (its syntax) used schema. +- `parse` - the function that parses the given schema to the [AsyncAPI Schema Format](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md#schemaObject). +- `getMimeTypes` - the function that returns the list of mime types that will be used as the `schemaFormat` property to determine the mime type of a given schema. Example: @@ -209,7 +113,7 @@ export default { } ``` -Before parsing/validating an AsyncAPI document with a parser, register the additional custom schema parser like in old parser: +Before parsing/validating an AsyncAPI document with a parser, register the additional custom schema parser like in the old parser: ```ts import { Parser } from '@asyncapi/parser'; @@ -219,16 +123,57 @@ const parser = new Parser(); parser.registerSchemaParser(myCustomSchemaParser); ``` -### Standalone stringify and unstringify functionality +## Convert new API to the old one + +Because a large part of the AsyncAPI tooling ecosystem uses a Parser with the old API and rewriting the tool for the new one can be time-consuming and difficult, the package exposes the `convertToOldAPI()` function to convert the new API to the old one: + +```js +import { Parser, convertToOldAPI } from '@asyncapi/parser'; + +const parser = new Parser(); +const { document } = parser.parse(...); +const oldAsyncAPIDocument = convertToOldAPI(document); +``` + +> **Warning** +> The old API will be supported only for a certain period. The target date for turning off support of the old API is around the end of March 2024. + +If there is a need to convert an existing instance of the old API to the new one, the library provides the `convertToNewAPI` function: + +```js +import { Parser, convertToNewAPI } from '@asyncapi/parser'; + +const newAsyncAPIDocument = convertToNewAPI(oldDocument); +``` + +> **Warning** +> Due to the fact that the new Parser validates the document more restrictively, it may be that a parsed document in the old way will not work 100% in accordance with the new API. + +## Important API changes + +These are all the exposed API changes and how you interact with the parser. For how to interact with the AsyncAPI document, checkout [the intent API](#new-intent-api). -The ability to stringify and unstringify (parsing of stringified AsyncAPI document) was possible as methods in the old `AsyncAPiDocument` instance. Currently, both functions are standalone: +### Parser class +In v1 we had only one instance of the Parser because it was global. The new version exposes the Parser class, so now it's possible to have several different instances of Parser with different configurations. ```ts -import { Parser, stringify, unstringify } from '@asyncapi/parser'; +import { Parser } from '@asyncapi/parser'; const parser = new Parser(); -const { document } = await parser.parse(` +const { document } = await parser.parse(`...AsyncAPI document`, ...?options); +``` + +### `.parse()` method + +The function `.parse()` returns not only parsed AsyncAPi `document` but also possible `diagnostics` array based on validation configuration: + +```ts +import { Parser } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document, diagnostics } = await parser.parse(` asyncapi: '2.4.0' info: title: Example AsyncAPI specification @@ -250,35 +195,94 @@ const { document } = await parser.parse(` `); if (document) { - // stringify function returns string type - const stringifiedDocument = stringify(document); - // unstringify function returns new AsyncAPIDocument instance - const unstringifiedDocument = unstringify(stringifiedDocument); + // => Example AsyncAPI specification + console.log(document.info().title()); } ``` -### Convert new API to the old one +### `.validate()` method -Due to the fact that a large part of the AsyncAPI tooling ecosystem uses a Parser with the old API and rewriting the tool for the new one can be time-consuming and difficult, the package exposes the `convertToOldAPI()` function to convert new API to old one: +In v2 it brings a new `.validate()` method (exposed by Parser class) which only validates the AsyncAPI document based on [Spectral](https://github.com/stoplightio/spectral) configuration. This is a useful feature for cases when we only need to know if a given AsyncAPI document is valid or not. -```js -import { Parser, convertToOldAPI } from '@asyncapi/parser'; +```ts +import { Parser } from '@asyncapi/parser'; const parser = new Parser(); -const { document } = parser.parse(...); -const oldAsyncAPIDocument = convertToOldAPI(document); + +// One of the diagnostics will contain an error regarding an unsupported version of AsyncAPI (2.1.37) +const diagnostics = await parser.validate(` + asyncapi: '2.1.37' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); ``` -> **Warning** -> The old api will be supported only for a certain period of time. The target date for turning off support of the old API is around the end of March 2023. +### `parseFromUrl()` is now deprecated -If there is a need to convert an existing instance of the old API to the new one, the library provides the `convertToNewAPI` function: +The `parseFromUrl()` function has been deprecated in favor of the new `fromURL` function: -```js -import { Parser, convertToNewAPI } from '@asyncapi/parser'; +```ts +import { Parser, fromURL } from '@asyncapi/parser'; -const newAsyncAPIDocument = convertToNewAPI(oldDocument); +const parser = new Parser(); + +const { document } = fromURL(parser, 'https://my.server.com/example-asyncapi.yaml', ...?fetching options).parse(); +// `validate` function is also available: +// fromURL(parser, ...).validate(); ``` -> **Warning** -> Due to the fact that the new Parser validates the document more restrictively, it may be that a parsed document in the old way will not work 100% in accordance with the new API. +> **Note** +> Parser exposes a similar function to handle file system sources - `fromFile`. + +### Standalone stringify and un-stringify functionality + +The ability to stringify and un-stringify (parsing of stringified AsyncAPI document) was possible as methods in the old `AsyncAPiDocument` instance. Currently, both functions are standalone: + +```ts +import { Parser, stringify, unstringify } from '@asyncapi/parser'; + +const parser = new Parser(); + +const { document } = await parser.parse(` + asyncapi: '2.4.0' + info: + title: Example AsyncAPI specification + version: '0.1.0' + channels: + example-channel: + subscribe: + message: + payload: + type: object + properties: + exampleField: + type: string + exampleNumber: + type: number + exampleDate: + type: string + format: date-time +`); + +if (document) { + // stringify function returns string type + const stringifiedDocument = stringify(document); + // unstringify function returns new AsyncAPIDocument instance + const unstringifiedDocument = unstringify(stringifiedDocument); +} +``` From 9e784fc79a801c7633d917f9694273241ea9f250 Mon Sep 17 00:00:00 2001 From: "jonas-lt@live.dk" Date: Mon, 26 Jun 2023 21:10:06 +0200 Subject: [PATCH 10/10] fix sonar --- .sonarcloud.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.sonarcloud.properties b/.sonarcloud.properties index 1b41ae5b3..98a487f3c 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1,2 +1,3 @@ # Disable specific duplicate code since it would introduce more complexity to reduce it. -sonar.cpd.exclusions=src/models/**/*.ts \ No newline at end of file +sonar.cpd.exclusions=src/models/**/*.ts +sonar.exclusions=test/**/*