├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ ├── codeql.yml │ ├── npmpublish.yml │ └── test-coverage.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── codecov.yml ├── index.d.ts ├── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── MessageValidator.js ├── Parser.js ├── ValidationError.js └── ValidatorFactory.js └── test ├── mocks.js ├── schemas ├── broken.yml ├── invalid.yml ├── jsonSchema.json ├── v2.0.0 │ ├── DeviceInstallationPublished.yaml │ ├── deviceMessages.yaml │ ├── mqtt.yaml │ ├── slack.yml │ └── userMessages.yaml ├── v2.4.0 │ ├── DeviceInstallationPublished.yaml │ ├── deviceMessages.yaml │ ├── mqtt.yaml │ ├── slack.yml │ └── userMessages.yaml └── v3.0.0 │ ├── pingPong.yml │ ├── streetLights.yml │ ├── streetLightsMini.yml │ └── streetLightsMultiMsg.yml ├── src └── AsyncApiValidatorFactory.js ├── v2.0.0 ├── deviceMessages.js ├── mqtt.js ├── slack.js └── userMessages.js ├── v2.4.0 ├── deviceMessages.js ├── mqtt.js ├── slack.js └── userMessages.js └── v3.0.0 ├── pingPong.js ├── streetLights.js ├── streetLightsMini.js └── streetLightsMultiMsg.js /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules 3 | dev/ 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "jest": true 7 | }, 8 | "extends": "standard", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 11 15 | }, 16 | "rules": { 17 | "space-before-function-paren": ["error", { 18 | "anonymous": "never", 19 | "named": "never", 20 | "asyncArrow": "always" 21 | }], 22 | "curly": ["error", "multi-line"], 23 | "no-return-await": "error", 24 | "quote-props": ["error", "as-needed"], 25 | "arrow-spacing": "error", 26 | "space-before-blocks": "error", 27 | "keyword-spacing": "error", 28 | "indent": ["error", 2, {"SwitchCase": 1}], 29 | "key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}], 30 | "comma-spacing": ["error", { "before": false, "after": true }], 31 | "camelcase": "error", 32 | "no-var": "error", 33 | "prefer-const": "error", 34 | "semi": ["error", "never"], 35 | "max-len": ["error", 150, 4], 36 | "generator-star-spacing": ["error", {"before": true, "after": false}], 37 | "quotes": ["error", "single"], 38 | "strict": ["error", "global"], 39 | "object-curly-spacing": ["error", "never"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | schedule: 20 | - cron: '00 12 * * 1' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # required to fetch internal or private CodeQL packs 37 | packages: read 38 | 39 | # only required for workflows in private repositories 40 | actions: read 41 | contents: read 42 | 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - language: javascript-typescript 48 | build-mode: none 49 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 50 | # Use `c-cpp` to analyze code written in C, C++ or both 51 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 52 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 53 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 54 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 55 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 56 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v4 60 | 61 | # Initializes the CodeQL tools for scanning. 62 | - name: Initialize CodeQL 63 | uses: github/codeql-action/init@v3 64 | with: 65 | languages: ${{ matrix.language }} 66 | build-mode: ${{ matrix.build-mode }} 67 | # If you wish to specify custom queries, you can do so here or in a config file. 68 | # By default, queries listed here will override any specified in a config file. 69 | # Prefix the list here with "+" to use these queries and those in the config file. 70 | 71 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 72 | # queries: security-extended,security-and-quality 73 | 74 | # If the analyze step fails for one of the languages you are analyzing with 75 | # "We were unable to automatically build your code", modify the matrix above 76 | # to set the build mode to "manual" for that language. Then modify this step 77 | # to build your code. 78 | # ℹ️ Command-line programs to run using the OS shell. 79 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 80 | - if: matrix.build-mode == 'manual' 81 | shell: bash 82 | run: | 83 | echo 'If you are using a "manual" build mode for one or more of the' \ 84 | 'languages you are analyzing, replace this with the commands to build' \ 85 | 'your code, for example:' 86 | echo ' make bootstrap' 87 | echo ' make release' 88 | exit 1 89 | 90 | - name: Perform CodeQL Analysis 91 | uses: github/codeql-action/analyze@v3 92 | with: 93 | category: "/language:${{matrix.language}}" 94 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: NPM Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish-npm: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | with: 15 | # We must fetch at least the immediate parents so that if this is 16 | # a pull request then we can checkout the head. 17 | fetch-depth: 2 18 | 19 | # If this run was triggered by a pull request event, then checkout 20 | # the head of the pull request instead of the merge commit. 21 | - run: git checkout HEAD^2 22 | if: ${{ github.event_name == 'pull_request' }} 23 | 24 | - name: Setup NodeJS 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: 18 28 | registry-url: 'https://registry.npmjs.org' 29 | 30 | - run: | 31 | npm ci 32 | npm run lint 33 | npm test 34 | 35 | - name: Publishing to NPM 36 | run: npm publish 37 | env: 38 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 39 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repository 10 | uses: actions/checkout@v4 11 | with: 12 | # We must fetch at least the immediate parents so that if this is 13 | # a pull request then we can checkout the head. 14 | fetch-depth: 2 15 | 16 | # If this run was triggered by a pull request event, then checkout 17 | # the head of the pull request instead of the merge commit. 18 | - run: git checkout HEAD^2 19 | if: ${{ github.event_name == 'pull_request' }} 20 | 21 | - name: Setup nodejs and run tests 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 18 25 | 26 | - run: | 27 | npm ci 28 | npm run lint 29 | npm test 30 | 31 | - name: Upload coverage reports to Codecov with GitHub Action 32 | uses: codecov/codecov-action@v4.2.0 33 | env: 34 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | # vscode 79 | .vscode/ 80 | 81 | # development files 82 | dev/ 83 | dist/ 84 | .DS_Store 85 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 5.1.1 *(2025-03-11)* 5 | ---------------------------- 6 | * Add support for Reply Operation Object (by @viktorgt) 7 | 8 | Version 5.1.0 *(2025-02-11)* 9 | ---------------------------- 10 | * Bump @asyncapi/parser version 11 | 12 | Version 5.0.1 *(2024-08-22)* 13 | ---------------------------- 14 | * Fix issue when multiple operations use same channel. 15 | 16 | Version 5.0.0 *(2024-07-21)* 17 | ---------------------------- 18 | ### Breaking: 19 | * Errors message format for the initial check on the AsyncApi document has been updated. 20 | * _Note: No change in errors thrown from the `validate()` method._ 21 | * Deprecate the `validateByMessageId()` method as `messageId` was removed in AsyncApi v3.0.0. More details here [asyncapi/spec/issues/978](https://github.com/asyncapi/spec/issues/978) 22 | 23 | ### New / Improvements: 24 | * Support AsyncAPI schema v3.0.0 25 | * Start using @asyncapi/parser v3 26 | 27 | Version 4.0.0 *(2022-05-22)* 28 | ---------------------------- 29 | ### Breaking: 30 | * "should" replaced with "must" in the error messages, as per [in AJV](https://github.com/ajv-validator/ajv/blob/master/docs/v6-to-v8-migration.md#new-features) 31 | 32 | ### New / Improvements: 33 | * Support AsyncAPI schema v2.4.0 34 | * Added `validateByMessageId()` method, which only requires `key` and `value`. To use this method, your AsyncAPI schema should be >= 2.4.0. `messageId` should be defined in the schema as per [AsyncAPI 2.4.0 schema definition](https://www.asyncapi.com/docs/specifications/v2.4.0#messageObject). 35 | * The `msgIdentifier` option is only required if you use the `.validate()` method. 36 | * You can use both `validateByMessageId()` and `validate()` methods if your AsyncAPI schema version is >= 2.4.0 37 | * Start using `ajv-formats` for custom formats. 38 | 39 | Version 3.2.0 *(2022-03-22)* 40 | ---------------------------- 41 | * Option to provide `path` parameter for relative file ref. 42 | 43 | Version 3.1.1 *(2021-09-30)* 44 | ---------------------------- 45 | * Add TS types 46 | * Fix JSDocs for `fromSource()` 47 | 48 | Version 3.1.0 *(2021-08-22)* 49 | ---------------------------- 50 | * Add support for deserialized objects as source 51 | 52 | Version 3.0.0 *(2021-03-20)* 53 | ---------------------------- 54 | ### Breaking: 55 | * Remove support for AsyncAPI Schema v1.2.0 56 | * Remove the `._schema` property from the instance. Use `.schema` instead. 57 | 58 | ### New / Improvements: 59 | * `channel`, `operation`, and `options.msgIdentifier` are required for validation. 60 | * Add support for OpenAPI formats 61 | * Using @asyncapi/openapi-schema-parser and @asyncapi/parser 62 | 63 | Version 2.5.0 *(2020-10-03)* 64 | ---------------------------- 65 | * Provide `.schema` property on instance, to access ref resolved JSON schema. 66 | * Add deprecation message for AsyncAPI v1. 67 | 68 | Version 2.4.5 *(2020-07-16)* 69 | ---------------------------- 70 | * Remove Travis integration 71 | * use GitHub actions for testing and coverage report 72 | 73 | Version 2.4.4 *(2020-05-29)* 74 | ---------------------------- 75 | * remove the dist folder 76 | 77 | Version 2.4.3 *(2020-05-09)* 78 | ---------------------------- 79 | * bump dependencies 80 | * remove watchify 81 | 82 | Version 2.4.2 *(2019-12-20)* 83 | ---------------------------- 84 | * Fixed validation for schema without components 85 | 86 | Version 2.4.1 *(2019-12-03)* 87 | ---------------------------- 88 | * Updated asyncapi to version 2.6.1 with fix for additionalProps 89 | 90 | Version 2.4.0 *(2019-12-01)* 91 | ---------------------------- 92 | * Support version 2 of AsyncAPI 93 | * Added support for `Channel` and `Operation` validation 94 | * Added support for custom message identifier. 95 | 96 | Version 2.3.2 *(2019-09-01)* 97 | ---------------------------- 98 | * Fixed tests lint 99 | 100 | Version 2.3.1 *(2019-09-01)* 101 | ---------------------------- 102 | * Updated dependencies 103 | 104 | Version 2.3.0 *(2019-07-24)* 105 | ---------------------------- 106 | * Custom error messages 107 | * Updated readme 108 | 109 | Version 2.2.1 *(2019-07-18)* 110 | ---------------------------- 111 | * Added support for options 112 | * Added support for if the payload is object and schema is an array 113 | 114 | Version 2.2.0 *(2019-07-15)* 115 | ---------------------------- 116 | * Removed parsers/loaders 117 | * Added dist for browsers 118 | 119 | Version 2.1.0 *(2019-07-07)* 120 | ---------------------------- 121 | * Added tests 122 | * Added codecov for coverage report 123 | 124 | Version 2.0.1 *(2019-07-06)* 125 | ---------------------------- 126 | * Better dir structure 127 | 128 | Version 2.0.0 *(2019-07-06)* 129 | ---------------------------- 130 | * Added eslint, travis, better linting 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WaleedAshraf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Unit Tests](https://github.com/WaleedAshraf/asyncapi-validator/workflows/Unit%20Tests/badge.svg) [![codecov](https://codecov.io/gh/WaleedAshraf/asyncapi-validator/graph/badge.svg?token=QlYEl26MvM)](https://codecov.io/gh/WaleedAshraf/asyncapi-validator) ![CodeQL](https://github.com/WaleedAshraf/asyncapi-validator/workflows/CodeQL/badge.svg) 2 | 3 | # asyncapi-validator 4 | 5 | Validate messages through AsyncApi 6 | 7 | _Note: This package only support AsyncApi Schema v2.0.0 and above._ 8 | 9 | `npm i asyncapi-validator` 10 | 11 | ## Features 12 | - Validate your messages against your AsyncApi Document 13 | - Validate your AsyncApi Document against AsyncApi Schema definition 14 | - Load your AsyncApi Schema from local file or any URL 15 | - Supports AsyncApi in JSON and YAML format 16 | - Supports AsyncApi v2.0.0 and above 17 | 18 | ## Content 19 | - [Class Methods](#class-methods) 20 | - [AsyncApiValidator.fromSource()](#asyncapivalidatorfromsource) 21 | - [Options](#options) 22 | - [Instance Methods / Properties](#instance-methods--properties) 23 | - [.validate()](#validate) 24 | - [.validateByMessageId()](#validateByMessageId) - _deprecated_ 25 | - [.schema](#schema) 26 | - [Example usage with .validate() method](#example-usage-with-validate-method) 27 | - [Example usage with .validateByMessageId() method](#example-usage-with-validatebymessageid-method) 28 | - [Errors](#errors) 29 | - [Error Example](#error-example) 30 | 31 | ## Class Methods 32 | 33 | ### AsyncApiValidator.fromSource() 34 | ```js 35 | /** 36 | * Load and Parse the schema from source. 37 | * @param {string | Object} source - local PATH or URL of schema or schema Object 38 | * @param {Object} options - options for validation 39 | * @returns {Promise} 40 | */ 41 | AsyncApiValidator.fromSource(source, options) 42 | ``` 43 | 44 | #### Options 45 | | value | type | | description | 46 | |-----|----|----|---| 47 | | msgIdentifier | string | required | Name of the parameter whose value will be used as `"key"` in `.validate()` method. Recommendation is to use `"name"` as described in [message-object](https://www.asyncapi.com/docs/reference/specification/v3.0.0#messageObject). You can also use [Specification Extensions](https://www.asyncapi.com/docs/reference/specification/v3.0.0#specificationExtensions). | 48 | | ignoreArray | boolean | optional | If `true`, then if schema is defined as an array and payload is an object, then payload will be placed inside an array before validation. | 49 | | path | string | optional | Path to the AsyncApi document. | 50 | 51 | ## Instance Methods and Properties 52 | 53 | ### .validate() 54 | 55 | You should provide `msgIdentifier` in AsyncApiValidator `options`. 56 | 57 | ```js 58 | /** 59 | * Method to validate the Payload against schema definition. 60 | * @param {string} key - required - message key (as per msgIdentifier) 61 | * @param {Object} payload - required - payload of the message 62 | * @param {string} channel - required - name of the channel/topic 63 | * @param {string} operation - required - publish | subscribe | send | receive 64 | * @returns {boolean} 65 | */ 66 | .validate(key, payload, channel, operation) 67 | ``` 68 | 69 | ### .validateByMessageId() - deprecated 70 | _This method is deprecated as `messageId` was removed in AsyncApi v3.0.0. More details here [asyncapi/spec/issues/978](https://github.com/asyncapi/spec/issues/978) ._ 71 | 72 | Here `messageId` should be as [defined in AsyncApi Schema v2.4.0](https://www.asyncapi.com/docs/specifications/v2.4.0#messageObject). To use this method, your AsyncApi Schema version should be >= v2.4.0 and <3.0.0. 73 | 74 | ```js 75 | /** 76 | * Method to validate the Payload against schema definition. 77 | * @param {string} key - required - messageId 78 | * @param {Object} payload - required - payload of the message 79 | * @returns {boolean} 80 | */ 81 | .validateByMessageId(key, payload) 82 | ``` 83 | 84 | ### .schema 85 | `.schema` property can be used to access AsyncApi schema in JSON format and with all the refs resolved. 86 | 87 | ## Example usage with .validate() method 88 | Schema 89 | ```yaml 90 | asyncapi: 3.0.0 91 | info: 92 | title: Streetlights Kafka API 93 | version: 1.0.0 94 | channels: 95 | lightingMeasured: 96 | messages: 97 | lightMeasured: 98 | $ref: '#/components/messages/lightMeasured' 99 | operations: 100 | sendLightMeasurement: 101 | action: send 102 | channel: 103 | $ref: '#/channels/lightingMeasured' 104 | messages: 105 | - $ref: '#/channels/lightingMeasured/messages/lightMeasured' 106 | components: 107 | messages: 108 | lightMeasured: 109 | x-unique-id: lightMeasured 110 | payload: 111 | $ref: '#/components/schemas/lightMeasuredPayload' 112 | schemas: 113 | lightMeasuredPayload: 114 | type: object 115 | properties: 116 | lumens: 117 | type: integer 118 | minimum: 0 119 | description: Light intensity measured in lumens. 120 | ``` 121 | ```js 122 | const AsyncApiValidator = require('asyncapi-validator') 123 | let va = await AsyncApiValidator.fromSource('./api.yaml', {msgIdentifier: 'x-unique-id'}) 124 | 125 | // validate 'lightMeasured' on channel 'lightingMeasured' with operation 'send' 126 | va.validate('lightMeasured', { 127 | lumens: 0, 128 | sentAt: '2019-08-24T14:15:22Z' 129 | }, 'lightingMeasured', 'send') 130 | ``` 131 | In above example, `"msgIdentifier"` is `"'x-unique-id"`. That is why, `"lightMeasured"` is used as `"key"` in `"va.validate()"` method. 132 | 133 | ## Example usage with .validateByMessageId() method 134 | Schema 135 | ```yaml 136 | asyncapi: 2.4.0 137 | 138 | info: 139 | title: User Events 140 | version: 1.0.0 141 | 142 | channels: 143 | user-events: 144 | description: user related events 145 | publish: 146 | message: 147 | messageId: UserRemoved 148 | payload: 149 | type: object 150 | properties: 151 | userEmail: 152 | type: string 153 | userId: 154 | type: string 155 | ``` 156 | ```js 157 | const AsyncApiValidator = require('asyncapi-validator') 158 | let va = await AsyncApiValidator.fromSource('./api.yaml') 159 | 160 | // validate messageId 'UserRemoved' 161 | va.validateByMessageId('UserRemoved', { 162 | userId: '123456789', 163 | userEmail: 'alex@mail.com', 164 | }) 165 | ``` 166 | 167 | ## Errors 168 | Error thrown from asyncapi-validator will have these properties. 169 | 170 | | key | type | value | description | 171 | |---------|--------|-------------------------|-----------------------------------------------------------------------------------------------------------------| 172 | | name | string | AsyncAPIValidationError | AsyncAPIValidationError | 173 | | key | string | | "key" of payload against which schema is validated | 174 | | message | string | | [errorsText from AJV](https://ajv.js.org/api.html#ajv-errorstext-errors-object-options-object-string) | 175 | | errors | array | | [Array of errors from AJV](https://ajv.js.org/api.html#validation-errors) | 176 | 177 | ### Error Example 178 | ```js 179 | { 180 | AsyncAPIValidationError: data.type must be equal to one of the allowed values at MessageValidator.validate (..... 181 | name: 'AsyncAPIValidationError', 182 | key: 'hello', 183 | errors: 184 | [ 185 | { keyword: 'enum', 186 | dataPath: '.type', 187 | schemaPath: '#/properties/type/enum', 188 | params: [Object], 189 | message: 'must be equal to one of the allowed values' 190 | } 191 | ] 192 | } 193 | ``` 194 | 195 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "diff" 3 | behavior: default 4 | require_changes: true # if true: only post the comment if coverage changes 5 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'asyncapi-validator' { 2 | type Validator = { 3 | validate: ( 4 | key: string, 5 | payload: unknown, 6 | channel: string, 7 | operation: 'publish' | 'subscribe' | 'send' | 'receive', 8 | ) => boolean; 9 | 10 | validateByMessageId: ( 11 | key: string, 12 | payload: unknown, 13 | ) => boolean; 14 | }; 15 | 16 | const fromSource: ( 17 | path: string | Record, 18 | options?: { msgIdentifier?: string; ignoreArray?: boolean, path?: string }, 19 | ) => Promise; 20 | } 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/ValidatorFactory') 2 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after `n` failures 9 | // bail: 0, 10 | 11 | // Respect "browser" field in package.json when resolving modules 12 | // browser: false, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/private/var/folders/10/b09w36l504v3s77xjy3csyz00000gp/T/jest_dy", 16 | 17 | // Automatically clear mock calls and instances between every test 18 | clearMocks: true, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | collectCoverage: true, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: null, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: 'coverage', 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: null, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: null, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // Force coverage collection from ignored files using an array of glob patterns 52 | // forceCoverageMatch: [], 53 | 54 | // A path to a module which exports an async function that is triggered once before all test suites 55 | // globalSetup: null, 56 | 57 | // A path to a module which exports an async function that is triggered once after all test suites 58 | // globalTeardown: null, 59 | 60 | // A set of global variables that need to be available in all test environments 61 | // globals: {}, 62 | 63 | // An array of directory names to be searched recursively up from the requiring module's location 64 | // moduleDirectories: [ 65 | // "node_modules" 66 | // ], 67 | 68 | // An array of file extensions your modules use 69 | // moduleFileExtensions: [ 70 | // "js", 71 | // "json", 72 | // "jsx", 73 | // "ts", 74 | // "tsx", 75 | // "node" 76 | // ], 77 | 78 | // A map from regular expressions to module names that allow to stub out resources with a single module 79 | // moduleNameMapper: {}, 80 | 81 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 82 | // modulePathIgnorePatterns: [], 83 | 84 | // Activates notifications for test results 85 | // notify: false, 86 | 87 | // An enum that specifies notification mode. Requires { notify: true } 88 | // notifyMode: "failure-change", 89 | 90 | // A preset that is used as a base for Jest's configuration 91 | // preset: null, 92 | 93 | // Run tests from one or more projects 94 | // projects: null, 95 | 96 | // Use this configuration option to add custom reporters to Jest 97 | // reporters: undefined, 98 | 99 | // Automatically reset mock state between every test 100 | // resetMocks: false, 101 | 102 | // Reset the module registry before running each individual test 103 | // resetModules: false, 104 | 105 | // A path to a custom resolver 106 | // resolver: null, 107 | 108 | // Automatically restore mock state between every test 109 | // restoreMocks: false, 110 | 111 | // The root directory that Jest should scan for tests and modules within 112 | // rootDir: null, 113 | 114 | // A list of paths to directories that Jest should use to search for files in 115 | // roots: [ 116 | // "" 117 | // ], 118 | 119 | // Allows you to use a custom runner instead of Jest's default test runner 120 | // runner: "jest-runner", 121 | 122 | // The paths to modules that run some code to configure or set up the testing environment before each test 123 | // setupFiles: [], 124 | 125 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 126 | // setupFilesAfterEnv: [], 127 | 128 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 129 | // snapshotSerializers: [], 130 | 131 | // The test environment that will be used for testing 132 | testEnvironment: 'node', 133 | 134 | // Options that will be passed to the testEnvironment 135 | // testEnvironmentOptions: {}, 136 | 137 | // Adds a location field to test results 138 | // testLocationInResults: false, 139 | 140 | // The glob patterns Jest uses to detect test files 141 | // testMatch: [ 142 | // "**/__tests__/**/*.[jt]s?(x)", 143 | // "**/?(*.)+(spec|test).[tj]s?(x)" 144 | // ], 145 | 146 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 147 | // testPathIgnorePatterns: [ 148 | // "/node_modules/" 149 | // ], 150 | 151 | // The regexp pattern or array of patterns that Jest uses to detect test files 152 | testRegex: ['./test/src/*', './test/v2.*/*', './test/v3.*/*'], 153 | 154 | // This option allows the use of a custom results processor 155 | // testResultsProcessor: null, 156 | 157 | // This option allows use of a custom test runner 158 | // testRunner: "jasmine2", 159 | 160 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 161 | // testURL: "http://localhost", 162 | 163 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 164 | // timers: "real", 165 | 166 | // A map from regular expressions to paths to transformers 167 | // transform: null, 168 | 169 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 170 | // transformIgnorePatterns: [ 171 | // "/node_modules/" 172 | // ], 173 | 174 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 175 | // unmockedModulePathPatterns: undefined, 176 | 177 | // Indicates whether each individual test should be reported during the run 178 | verbose: true 179 | 180 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 181 | // watchPathIgnorePatterns: [], 182 | 183 | // Whether to use watchman for file crawling 184 | // watchman: true, 185 | } 186 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asyncapi-validator", 3 | "version": "5.1.1", 4 | "description": "message validator through asyncapi schema", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "jest", 9 | "lint": "./node_modules/.bin/eslint ./" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/WaleedAshraf/asyncapi-validator.git" 14 | }, 15 | "keywords": [ 16 | "asyncapi", 17 | "kafka", 18 | "rabbitmq", 19 | "test", 20 | "validator", 21 | "api" 22 | ], 23 | "author": "Waleed Ashraf", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/WaleedAshraf/asyncapi-validator/issues" 27 | }, 28 | "homepage": "https://github.com/WaleedAshraf/asyncapi-validator#readme", 29 | "dependencies": { 30 | "@asyncapi/openapi-schema-parser": "3.0.24", 31 | "@asyncapi/parser": "3.4.0", 32 | "ajv": "^8.17.1", 33 | "ajv-formats": "^3.0.1" 34 | }, 35 | "devDependencies": { 36 | "@types/jest": "^25.2.1", 37 | "eslint": "7.2.0", 38 | "eslint-config-standard": "^14.1.1", 39 | "eslint-plugin-import": "^2.29.1", 40 | "eslint-plugin-node": "^11.1.0", 41 | "eslint-plugin-promise": "^4.2.1", 42 | "eslint-plugin-standard": "^4.0.1", 43 | "jest": "^28.0.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/MessageValidator.js: -------------------------------------------------------------------------------- 1 | const Ajv = require('ajv') 2 | const addFormats = require('ajv-formats') 3 | const ValidationError = require('./ValidationError') 4 | 5 | class MessageValidator { 6 | /** 7 | * @param {object} schema - user defined AsyncApi Schema 8 | * @param {{ msgIdentifier?: string; ignoreArray?: boolean; path?: any; }} options - options for validations 9 | * @param {object} channels - simplified channels object from schema 10 | */ 11 | constructor(schema, options, channels, messagesWithId) { 12 | this.schema = schema 13 | this._messages = this.schema.components ? this.schema.components.messages : [] 14 | this._messagesWithId = messagesWithId 15 | this._channels = channels 16 | this._ajv = new Ajv({allErrors: true, strict: false, unicodeRegExp: false}) 17 | addFormats(this._ajv) 18 | this._options = options 19 | } 20 | 21 | /** 22 | * @param {string} key 23 | * @param {Object} payload 24 | * @param {Object} channel 25 | * @param {Object} operation 26 | * @returns {boolean} 27 | */ 28 | validate(key, payload, channel, operation) { 29 | this._validateArgs(key, channel, operation) 30 | const payloadSchema = this._channels[channel][operation][key].payload 31 | payload = this._shouldHandleArray(payloadSchema, payload) ? [payload] : payload 32 | const validator = this._ajv.compile(payloadSchema) 33 | 34 | const result = validator(payload) 35 | 36 | if (!result) throw new ValidationError(this._ajv.errorsText(validator.errors), key, validator.errors) 37 | 38 | return true 39 | } 40 | 41 | // deprecated 42 | validateByMessageId(key, payload) { 43 | this._validateArgs(key, null, null, 'validateByMessageId') 44 | const payloadSchema = this._messagesWithId[key].payload 45 | payload = this._shouldHandleArray(payloadSchema, payload) ? [payload] : payload 46 | const validator = this._ajv.compile(payloadSchema) 47 | 48 | const result = validator(payload) 49 | 50 | if (!result) throw new ValidationError(this._ajv.errorsText(validator.errors), key, validator.errors) 51 | 52 | return true 53 | } 54 | 55 | /** 56 | * @param {string} key 57 | * @param {string | number | null} channel 58 | * @param {string | null} operation 59 | * @param {string | null} method 60 | */ 61 | _validateArgs(key, channel, operation, method = null) { 62 | if (method === 'validateByMessageId') { 63 | const [major, minor] = this.schema.asyncapi.split('.') 64 | if (parseInt(major) < 2 || (parseInt(major) === 2 && parseInt(minor) < 4)) { 65 | throw new ValidationError(`AsyncAPI schema version should be >= 2.4.0 and <3.0.0 . Your version is "${this.schema.asyncapi}"`) 66 | } 67 | 68 | if (!this._messagesWithId[key]) { 69 | throw new ValidationError(`message with messageId "${key}" not found`) 70 | } 71 | } else { 72 | if (!this._options.msgIdentifier) { 73 | throw new ValidationError('"msgIdentifier" is required') 74 | } 75 | 76 | if (!operation || !(operation === 'publish' || operation === 'subscribe' || operation === 'send' || operation === 'receive')) { 77 | throw new ValidationError(`operation "${operation}" is not valid`) 78 | } 79 | 80 | if (channel !== null && !this._channels[channel]) { 81 | throw new ValidationError(`channel "${channel}" not found`) 82 | } 83 | 84 | if (channel !== null && !this._channels[channel][operation][key]) { 85 | throw new ValidationError(`message with key "${key}" on channel "${channel}" and operation "${operation}" not found`) 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * @param {{ type: string; }} payloadSchema 92 | * @param {any} payload 93 | */ 94 | _shouldHandleArray(payloadSchema, payload) { 95 | const shouldIgnoreArray = this._options.ignoreArray === true 96 | const schemaIsArray = payloadSchema.type === 'array' 97 | const payloadIsNotArray = !Array.isArray(payload) 98 | 99 | if (shouldIgnoreArray && schemaIsArray && payloadIsNotArray) return true 100 | return false 101 | } 102 | } 103 | 104 | module.exports = MessageValidator 105 | -------------------------------------------------------------------------------- /src/Parser.js: -------------------------------------------------------------------------------- 1 | const {Parser: AsyncapiParser, fromURL, fromFile} = require('@asyncapi/parser') 2 | const {OpenAPISchemaParser} = require('@asyncapi/openapi-schema-parser') 3 | const ValidationError = require('./ValidationError') 4 | 5 | class Parser { 6 | /** 7 | * @param {string} source 8 | * @param {{ msgIdentifier?: string; ignoreArray?: boolean; path?: any; }} options 9 | * @returns {Promise} ref resolved schema object 10 | */ 11 | async parse(source, options) { 12 | const defaultConfig = options && options.path ? {source: options.path} : {} 13 | const parser = new AsyncapiParser() 14 | let document, diagnostics 15 | try { 16 | parser.registerSchemaParser(OpenAPISchemaParser()) 17 | if (source instanceof Object) { 18 | // Source could be an object (instead of JSON / YAML string.) 19 | const result = await parser.parse(source) 20 | document = result.document 21 | diagnostics = result.diagnostics 22 | } else if (source.indexOf('https://') === 0 || source.indexOf('http://') === 0) { 23 | const result = await fromURL(parser, source).parse(defaultConfig) 24 | document = result.document 25 | diagnostics = result.diagnostics 26 | } else { 27 | const result = await fromFile(parser, source).parse(defaultConfig) 28 | document = result.document 29 | diagnostics = result.diagnostics 30 | } 31 | if (diagnostics && diagnostics.length) { 32 | const errorMessages = this._formatError(diagnostics) 33 | if (errorMessages) throw new ValidationError(errorMessages, undefined, diagnostics) 34 | } 35 | return document 36 | } catch (err) { 37 | if (err instanceof ValidationError) throw err 38 | throw new ValidationError(err.message, undefined, err) 39 | } 40 | } 41 | 42 | _formatError(diagnostics) { 43 | let messages = '' 44 | 45 | diagnostics.forEach(error => { 46 | if (error.severity === 0) { 47 | messages += error.message ? `${error.message} ` : '' 48 | } 49 | }) 50 | 51 | return messages === '' ? null : messages 52 | } 53 | } 54 | 55 | module.exports = new Parser() 56 | -------------------------------------------------------------------------------- /src/ValidationError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {String} message - Error message 3 | * @param {String} key - Key of message against which payload is validated 4 | * @param {Array} errors - Optional details about the message 5 | */ 6 | class AsyncAPIValidationError extends Error { 7 | constructor(message, key, errors) { 8 | super(message) 9 | this.name = this.constructor.name 10 | this.key = key 11 | this.message = message 12 | this.errors = errors 13 | Error.captureStackTrace(this, this.constructor) 14 | } 15 | } 16 | 17 | module.exports = AsyncAPIValidationError 18 | -------------------------------------------------------------------------------- /src/ValidatorFactory.js: -------------------------------------------------------------------------------- 1 | const MessageValidator = require('./MessageValidator') 2 | const Parser = require('./Parser') 3 | const messageId = 'messageId' 4 | 5 | function ValidatorFactory() { 6 | /** 7 | * Load schema from provided source 8 | * @param {string | Object} source 9 | * @param {{ msgIdentifier?: string; ignoreArray?: boolean; path?: any; }} [options] - options for validations 10 | * @returns {Promise} 11 | */ 12 | this.fromSource = async (source, options = {}) => { 13 | const {_json: schema} = await Parser.parse(source, options) 14 | const {channels, messagesWithId} = constructsChannels(schema, options.msgIdentifier) 15 | return new MessageValidator(schema, options, channels, messagesWithId) 16 | } 17 | } 18 | 19 | /** 20 | * Constructs channels and messages with identifiers from the given schema. 21 | * @param {object} schema - The schema object. 22 | * @param {string} [msgIdentifier=''] - The message identifier. 23 | * @returns {object} - An object containing the constructed channels and messages with identifiers. 24 | */ 25 | const constructsChannels = (schema, msgIdentifier = '') => { 26 | const channels = {} 27 | let messagesWithId = {} 28 | 29 | // For AsyncAPI 3.x.x 30 | if (schema.operations) { 31 | Object.values(schema.operations).forEach(operation => { 32 | const channel = operation.channel['x-parser-unique-object-id'] 33 | const action = operation.action 34 | 35 | const messages = getMessagesByMsgIdentifier(operation.messages, msgIdentifier) 36 | channels[channel] = {...channels[channel], [action]: messages} 37 | 38 | if (operation.reply && operation.reply.channel) { 39 | const replyChannel = operation.reply.channel['x-parser-unique-object-id'] 40 | const replyMessages = getMessagesByMsgIdentifier(operation.reply.messages, msgIdentifier) 41 | channels[replyChannel] = {...channels[replyChannel], [action]: replyMessages} 42 | } 43 | }) 44 | } 45 | 46 | Object.keys(schema.channels).forEach(c => { 47 | // For AsyncAPI 2.x.x 48 | const operations = ['publish', 'subscribe'] 49 | operations.forEach(operation => { 50 | const {msgsForOp, msgsForId} = getMessagesForOperation(schema.channels[c], operation, msgIdentifier) 51 | if (Object.keys(msgsForOp).length) { 52 | channels[c] = { 53 | ...channels[c], 54 | [operation]: msgsForOp 55 | } 56 | } 57 | messagesWithId = { 58 | ...messagesWithId, 59 | ...msgsForId 60 | } 61 | }) 62 | }) 63 | return {channels, messagesWithId} 64 | } 65 | 66 | /** 67 | * @param { any } channel 68 | * @param {string} operation - The operation for which to create a validator. 69 | * - 'publish' for publishing messages 70 | * - 'subscribe' for subscribing to topics 71 | * @param {string} msgIdentifier 72 | * @returns 73 | */ 74 | const getMessagesForOperation = (channel, operation, msgIdentifier) => { 75 | const msgsForOp = {} 76 | const msgsForId = {} 77 | if (channel[operation]) { 78 | if (channel[operation].message.oneOf) { 79 | channel[operation].message.oneOf.forEach((m) => { 80 | if (m[msgIdentifier]) msgsForOp[m[msgIdentifier]] = m 81 | if (m[messageId]) msgsForId[m[messageId]] = m 82 | }) 83 | } else { 84 | const tempMsg = channel[operation].message 85 | if (tempMsg[msgIdentifier]) msgsForOp[tempMsg[msgIdentifier]] = tempMsg 86 | if (tempMsg[messageId]) msgsForId[tempMsg[messageId]] = tempMsg 87 | } 88 | } 89 | return {msgsForOp, msgsForId} 90 | } 91 | 92 | const getMessagesByMsgIdentifier = (messages, msgIdentifier) => { 93 | return messages.reduce((messages, message) => 94 | message[msgIdentifier] ? {...messages, [message[msgIdentifier]]: message} : messages, 95 | {}) 96 | } 97 | 98 | module.exports = new ValidatorFactory() 99 | -------------------------------------------------------------------------------- /test/mocks.js: -------------------------------------------------------------------------------- 1 | 2 | const mocks = { 3 | slack: 'https://raw.githubusercontent.com/asyncapi/asyncapi/master/examples/2.0.0/slack-rtm.yml', 4 | streetlights: 'https://raw.githubusercontent.com/asyncapi/asyncapi/master/examples/2.0.0/streetlights.yml', 5 | htmlFile: 'https://github.com/asyncapi/asyncapi/blob/master/examples/2.0.0/slack-rtm.yml' 6 | } 7 | 8 | module.exports = mocks 9 | -------------------------------------------------------------------------------- /test/schemas/broken.yml: -------------------------------------------------------------------------------- 1 | asyncapi: '2.0.0' 2 | info: 3 | title: Streetlights API 4 | version: '1.0.0' 5 | description: | 6 | The Smartylighting Streetlights API allows you 7 | to remotely manage the city lights. 8 | license: 9 | name: Apache 2.0 10 | url: 'https://www.apache.org/licenses/LICENSE-2.0' 11 | servers: 12 | mosquitto: 13 | url: mqtt://test.mosquitto.org 14 | protocol: mqtt 15 | channel: 16 | light/measured: 17 | publish: 18 | summary: Inform about environmental lighting conditions for a particular streetlight. 19 | operationId: onLightMeasured 20 | message: 21 | payload: 22 | type: object 23 | properties: 24 | id: 25 | type: integer 26 | minimum: 0 27 | description: Id of the streetlight. 28 | lumens: 29 | type: integer 30 | minimum: 0 31 | description: Light intensity measured in lumens. 32 | sentAt: 33 | type: string 34 | format: date-time 35 | description: Date and time when the message was sent. 36 | -------------------------------------------------------------------------------- /test/schemas/invalid.yml: -------------------------------------------------------------------------------- 1 | asyncapi: '2.0.0' 2 | info: 3 | title: Streetlights API 4 | version: '1.0.0' 5 | description: | 6 | The Smartylighting Streetlights API allows you 7 | to remotely manage the city lights. 8 | license: 9 | name: Apache 2.0 10 | url: 'https://www.apache.org/licenses/LICENSE-2.0' 11 | servers: 12 | mosquitto: 13 | url: mqtt://test.mosquitto.org 14 | protocol: mqtt 15 | channel: 16 | channels: 17 | light/measured: 18 | publish: 19 | summary: Inform about environmental lighting conditions for a particular streetlight. 20 | operationId: onLightMeasured 21 | message: 22 | payload: 23 | type: object 24 | properties: 25 | id: 26 | type: integer 27 | minimum: 0 28 | description: Id of the streetlight. 29 | lumens: 30 | type: integer 31 | minimum: 0 32 | description: Light intensity measured in lumens. 33 | sentAt: 34 | type: string 35 | format: date-time 36 | description: Date and time when the message was sent. 37 | -------------------------------------------------------------------------------- /test/schemas/jsonSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "asyncapi": "2.0.0", 3 | "info": { 4 | "title": "Streetlights API", 5 | "version": "1.0.0", 6 | "description": "The Smartylighting Streetlights API allows you\nto remotely manage the city lights.\n", 7 | "license": { 8 | "name": "Apache 2.0", 9 | "url": "https://www.apache.org/licenses/LICENSE-2.0" 10 | } 11 | }, 12 | "servers": { 13 | "mosquitto": { 14 | "url": "mqtt://test.mosquitto.org", 15 | "protocol": "mqtt" 16 | } 17 | }, 18 | "channels": { 19 | "light/measured": { 20 | "publish": { 21 | "summary": "Inform about environmental lighting conditions for a particular streetlight.", 22 | "operationId": "onLightMeasured", 23 | "message": { 24 | "name": "onLightMeasured", 25 | "payload": { 26 | "type": "object", 27 | "properties": { 28 | "id": { 29 | "type": "integer", 30 | "minimum": 0, 31 | "description": "Id of the streetlight." 32 | }, 33 | "lumens": { 34 | "type": "integer", 35 | "minimum": 0, 36 | "description": "Light intensity measured in lumens." 37 | }, 38 | "sentAt": { 39 | "type": "string", 40 | "format": "date-time", 41 | "description": "Date and time when the message was sent." 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/schemas/v2.0.0/DeviceInstallationPublished.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.0.0 2 | 3 | info: 4 | title: Kafka Messages definition 5 | version: 1.0.0 6 | description: Kafka messages specification 7 | 8 | components: 9 | schemas: 10 | DeviceInstallationPublished: 11 | allOf: 12 | - $ref: '#/components/schemas/BasicKafkaEnvelope' 13 | - type: object 14 | properties: 15 | data: 16 | type: object 17 | required: 18 | - deviceId 19 | - id 20 | - received 21 | - recorded 22 | properties: 23 | deviceId: 24 | $ref: '#/components/schemas/UUID' 25 | id: 26 | $ref: '#/components/schemas/UUID' 27 | received: 28 | $ref: '#/components/schemas/Timestamp' 29 | recorded: 30 | $ref: '#/components/schemas/Timestamp' 31 | 32 | # ----------------- OTHER schemas ------------------- 33 | Timestamp: 34 | title: timestamp 35 | type: string 36 | pattern: >- 37 | ^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$ 38 | example: '2017-01-09T08:27:22.222Z' 39 | 40 | UUID: 41 | type: string 42 | format: uuid 43 | example: bd58d14f-fd3e-449c-b60c-a56548190d68 44 | 45 | BasicKafkaEnvelope: 46 | type: object 47 | required: 48 | - id 49 | - key 50 | - type 51 | - generated 52 | - requestId 53 | properties: 54 | id: 55 | $ref: '#/components/schemas/UUID' 56 | key: 57 | type: string 58 | type: 59 | type: string 60 | generated: 61 | $ref: '#/components/schemas/Timestamp' 62 | requestId: 63 | type: string 64 | 65 | TaskStatus: 66 | type: string 67 | enum: 68 | - success 69 | - error 70 | - in_progress 71 | 72 | 73 | -------------------------------------------------------------------------------- /test/schemas/v2.0.0/deviceMessages.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.0.0 2 | 3 | info: 4 | title: Kafka Messages definition 5 | version: 1.0.0 6 | description: Kafka messages specification 7 | 8 | channels: 9 | device-installation-events: 10 | description: user related events 11 | bindings: 12 | kafka: 13 | cleanup.policy: delete 14 | publish: 15 | message: 16 | oneOf: 17 | - $ref: '#/components/messages/DeviceInstallationPublished' 18 | - $ref: '#/components/messages/DeviceInstallationResponsePublished' 19 | subscribe: 20 | message: 21 | $ref: '#/components/messages/DeviceInstallationResponsePublished' 22 | 23 | components: 24 | messages: 25 | DeviceInstallationPublished: 26 | name: DeviceInstallationPublished 27 | title: DeviceInstallationPublished 28 | summary: Device Installation Published 29 | schemaFormat: 'application/schema+yaml;version=draft-07' 30 | payload: 31 | $ref: 'DeviceInstallationPublished.yaml#/components/schemas/DeviceInstallationPublished' 32 | DeviceInstallationResponsePublished: 33 | name: DeviceInstallationResponsePublished 34 | title: DeviceInstallationResponsePublished 35 | summary: Device Installation Response Published 36 | schemaFormat: 'application/schema+yaml;version=draft-07' 37 | payload: 38 | $ref: '#/components/schemas/DeviceInstallationResponsePublished' 39 | 40 | schemas: 41 | DeviceInstallationResponsePublished: 42 | allOf: 43 | - $ref: '#/components/schemas/BasicKafkaEnvelope' 44 | - type: object 45 | properties: 46 | data: 47 | type: object 48 | required: 49 | - deviceId 50 | - id 51 | - status 52 | additionalProperties: false 53 | properties: 54 | deviceId: 55 | $ref: '#/components/schemas/UUID' 56 | id: 57 | $ref: '#/components/schemas/UUID' 58 | status: 59 | $ref: '#/components/schemas/TaskStatus' 60 | message: 61 | type: string 62 | temperature: 63 | type: integer 64 | format: int32 65 | vibration: 66 | type: integer 67 | format: int64 68 | floatType: 69 | type: number 70 | format: float 71 | doubleType: 72 | type: number 73 | format: double 74 | byteType: 75 | type: string 76 | format: byte 77 | 78 | # ----------------- OTHER schemas ------------------- 79 | Timestamp: 80 | title: timestamp 81 | type: string 82 | pattern: >- 83 | ^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$ 84 | example: '2017-01-09T08:27:22.222Z' 85 | 86 | UUID: 87 | type: string 88 | format: uuid 89 | example: bd58d14f-fd3e-449c-b60c-a56548190d68 90 | 91 | BasicKafkaEnvelope: 92 | type: object 93 | required: 94 | - id 95 | - key 96 | - type 97 | - generated 98 | - requestId 99 | properties: 100 | id: 101 | $ref: '#/components/schemas/UUID' 102 | key: 103 | type: string 104 | type: 105 | type: string 106 | generated: 107 | $ref: '#/components/schemas/Timestamp' 108 | requestId: 109 | type: string 110 | 111 | TaskStatus: 112 | type: string 113 | enum: 114 | - success 115 | - error 116 | - in_progress 117 | 118 | -------------------------------------------------------------------------------- /test/schemas/v2.0.0/mqtt.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.0.0 2 | info: 3 | title: MQTT Broker 4 | version: 1.16.1 5 | description: MQTT Broker 6 | x-topic-separator: / 7 | 8 | channels: 9 | 'devices/{deviceId}/measurements': 10 | parameters: 11 | deviceId: 12 | schema: 13 | $ref: '#/components/schemas/id' 14 | publish: 15 | message: 16 | $ref: '#/components/messages/measurements' 17 | 'devices/{deviceId}/alerts': 18 | parameters: 19 | deviceId: 20 | schema: 21 | $ref: '#/components/schemas/id' 22 | publish: 23 | message: 24 | $ref: '#/components/messages/alerts' 25 | 'devices/{deviceId}/commands': 26 | parameters: 27 | deviceId: 28 | schema: 29 | $ref: '#/components/schemas/id' 30 | subscribe: 31 | message: 32 | $ref: '#/components/messages/commands' 33 | 'devices/{deviceId}/command_responses': 34 | parameters: 35 | deviceId: 36 | schema: 37 | $ref: '#/components/schemas/id' 38 | publish: 39 | message: 40 | $ref: '#/components/messages/command_responses' 41 | 'devices/{deviceId}/configs': 42 | parameters: 43 | deviceId: 44 | schema: 45 | $ref: '#/components/schemas/id' 46 | subscribe: 47 | message: 48 | $ref: '#/components/messages/configs' 49 | 'devices/{deviceId}/config_responses': 50 | parameters: 51 | deviceId: 52 | schema: 53 | $ref: '#/components/schemas/id' 54 | publish: 55 | message: 56 | $ref: '#/components/messages/config_responses' 57 | 'devices/{deviceId}/config_requests': 58 | parameters: 59 | deviceId: 60 | schema: 61 | $ref: '#/components/schemas/id' 62 | publish: 63 | message: 64 | $ref: '#/components/messages/config_requests' 65 | 'devices/{deviceId}/installations': 66 | parameters: 67 | deviceId: 68 | schema: 69 | $ref: '#/components/schemas/id' 70 | subscribe: 71 | message: 72 | $ref: '#/components/messages/installations' 73 | 'devices/{deviceId}/installation_responses': 74 | parameters: 75 | deviceId: 76 | schema: 77 | $ref: '#/components/schemas/id' 78 | publish: 79 | message: 80 | $ref: '#/components/messages/installation_responses' 81 | 'devices/{deviceId}/errors': 82 | parameters: 83 | deviceId: 84 | schema: 85 | $ref: '#/components/schemas/id' 86 | subscribe: 87 | message: 88 | $ref: '#/components/messages/errors' 89 | 'devices/{deviceId}/logs': 90 | parameters: 91 | deviceId: 92 | schema: 93 | $ref: '#/components/schemas/id' 94 | publish: 95 | message: 96 | $ref: '#/components/messages/logs' 97 | 'devices/{deviceId}/locations': 98 | parameters: 99 | deviceId: 100 | schema: 101 | $ref: '#/components/schemas/id' 102 | publish: 103 | message: 104 | $ref: '#/components/messages/locations' 105 | 'device-groups/{deviceGroupId}/analytics_events': 106 | parameters: 107 | deviceGroupId: 108 | schema: 109 | $ref: '#/components/schemas/id' 110 | publish: 111 | message: 112 | $ref: '#/components/messages/analytics_events' 113 | 114 | components: 115 | schemas: 116 | id: 117 | description: Resource identifier 118 | type: string 119 | format: uuid 120 | pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ 121 | example: 763c073a-e0ff-41a9-bd51-3386975ea4e3 122 | 123 | deviceId: 124 | description: Device identifier 125 | $ref: '#/components/schemas/id' 126 | example: d44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8 127 | 128 | deviceGroupId: 129 | description: Device group identifier 130 | $ref: '#/components/schemas/id' 131 | example: d44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8 132 | 133 | datetime: 134 | description: | 135 | Date and time of the message. We don't store data with higher precision than millisecond. 136 | Measurements sent for the same millisecond will overwrite each other. If it is a "number" it should be Unix timestamp in milliseconds. 137 | If it is a string, it should be ISO 8601 format. 138 | oneOf: 139 | - type: number 140 | - type: string 141 | 142 | # measurements 143 | measurement: 144 | type: object 145 | required: 146 | - name 147 | - value 148 | properties: 149 | name: 150 | $ref: '#/components/schemas/measurementName' 151 | value: 152 | $ref: '#/components/schemas/measurementValue' 153 | timestamp: 154 | $ref: '#/components/schemas/datetime' 155 | 156 | measurementName: 157 | description: Name of the measurement defined in device-model 158 | type: string 159 | example: temperature 160 | 161 | measurementValue: 162 | description: Value of the measurement 163 | oneOf: 164 | - type: number 165 | - type: boolean 166 | - type: string 167 | example: 36.6 168 | 169 | # alerts 170 | alert: 171 | type: object 172 | required: 173 | - name 174 | - state 175 | properties: 176 | name: 177 | $ref: '#/components/schemas/alertName' 178 | state: 179 | $ref: '#/components/schemas/alertState' 180 | message: 181 | $ref: '#/components/schemas/alertMessage' 182 | timestamp: 183 | $ref: '#/components/schemas/datetime' 184 | 185 | alertName: 186 | description: Name of the alert defined in device-model 187 | type: string 188 | example: temperature_high 189 | 190 | alertState: 191 | description: State of the alert (set/clear) 192 | type: string 193 | enum: 194 | - set 195 | - clear 196 | 197 | alertMessage: 198 | description: Optional alert message 199 | type: string 200 | example: temperature too high 201 | minLength: 1 202 | maxLength: 256 203 | 204 | 205 | #commands 206 | command: 207 | type: object 208 | required: 209 | - deviceId 210 | - id 211 | - name 212 | - timestamp 213 | properties: 214 | deviceId: 215 | $ref: '#/components/schemas/deviceId' 216 | id: 217 | $ref: '#/components/schemas/commandId' 218 | name: 219 | $ref: '#/components/schemas/commandName' 220 | parameter: 221 | $ref: '#/components/schemas/commandParameter' 222 | timestamp: 223 | $ref: '#/components/schemas/datetime' 224 | 225 | commandResponse: 226 | type: object 227 | required: 228 | - id 229 | - status 230 | properties: 231 | id: 232 | $ref: '#/components/schemas/commandId' 233 | status: 234 | $ref: '#/components/schemas/commandStatus' 235 | message: 236 | $ref: '#/components/schemas/commandMessage' 237 | timestamp: 238 | $ref: '#/components/schemas/datetime' 239 | 240 | commandId: 241 | description: Command identifier 242 | $ref: '#/components/schemas/id' 243 | example: 7f5bc456-21f2-4e9e-a38f-80baf762b1c5 244 | 245 | commandName: 246 | description: Name of the command defined in device-model 247 | type: string 248 | example: dim_light 249 | 250 | commandParameter: 251 | description: Parameter of the command (depending on device-model can be optional) 252 | oneOf: 253 | - type: number 254 | - type: string 255 | - type: boolean 256 | - type: object 257 | - type: array 258 | example: true 259 | 260 | commandStatus: 261 | description: Command status 262 | type: string 263 | enum: 264 | - in_progress 265 | - success 266 | - error 267 | 268 | commandMessage: 269 | description: Optional response message 270 | type: string 271 | example: message describing the command progress 272 | minLength: 1 273 | maxLength: 256 274 | 275 | # configurations 276 | configuration: 277 | type: object 278 | required: 279 | - configuration 280 | - version 281 | - deviceId 282 | properties: 283 | configuration: 284 | $ref: '#/components/schemas/configurationPayload' 285 | version: 286 | $ref: '#/components/schemas/configurationVersion' 287 | deviceId: 288 | $ref: '#/components/schemas/deviceId' 289 | 290 | configurationResponse: 291 | type: object 292 | required: 293 | - status 294 | - version 295 | properties: 296 | status: 297 | $ref: '#/components/schemas/configurationStatus' 298 | version: 299 | $ref: '#/components/schemas/configurationVersion' 300 | message: 301 | $ref: '#/components/schemas/configurationMessage' 302 | timestamp: 303 | $ref: '#/components/schemas/datetime' 304 | 305 | configurationRequest: 306 | type: object 307 | required: 308 | - timestamp 309 | properties: 310 | timestamp: 311 | $ref: '#/components/schemas/datetime' 312 | 313 | configurationStatus: 314 | description: Status of the configuration 315 | type: string 316 | enum: 317 | - success 318 | - error 319 | example: error 320 | 321 | configurationVersion: 322 | description: Version of the configuration 323 | type: integer 324 | example: 2 325 | 326 | configurationPayload: 327 | description: Payload of the configuration (any valid JSON) 328 | example: 329 | maximum_temperature: 60 330 | 331 | configurationMessage: 332 | description: Optional response message 333 | type: string 334 | example: missing value x 335 | 336 | # installations 337 | installation: 338 | type: object 339 | required: 340 | - deviceId 341 | - id 342 | - timestamp 343 | properties: 344 | deviceId: 345 | $ref: '#/components/schemas/deviceId' 346 | id: 347 | $ref: '#/components/schemas/installationId' 348 | type: 349 | $ref: '#/components/schemas/installationType' 350 | fileName: 351 | $ref: '#/components/schemas/installationFile' 352 | location: 353 | $ref: '#/components/schemas/installationLocation' 354 | signature: 355 | $ref: '#/components/schemas/installationSignature' 356 | signatureType: 357 | $ref: '#/components/schemas/installationSignatureType' 358 | size: 359 | $ref: '#/components/schemas/installationSize' 360 | description: 361 | $ref: '#/components/schemas/installationDesc' 362 | timestamp: 363 | $ref: '#/components/schemas/datetime' 364 | buildStamp: 365 | $ref: '#/components/schemas/installationBuildStamp' 366 | 367 | installationResponse: 368 | type: object 369 | required: 370 | - id 371 | - status 372 | properties: 373 | id: 374 | $ref: '#/components/schemas/installationId' 375 | status: 376 | $ref: '#/components/schemas/installationStatus' 377 | message: 378 | $ref: '#/components/schemas/installationMessage' 379 | timestamp: 380 | $ref: '#/components/schemas/datetime' 381 | 382 | installationId: 383 | description: Installation identifier 384 | $ref: '#/components/schemas/id' 385 | 386 | installationType: 387 | description: Type of the installation file 388 | type: string 389 | example: gwa-core-package 390 | 391 | installationFile: 392 | description: Name of the installation file 393 | type: string 394 | example: gwa-core.tgz 395 | 396 | installationLocation: 397 | description: Location (url) of the installation file 398 | type: string 399 | example: http://foo.bar/buzz.xyz 400 | 401 | installationSignature: 402 | description: Signature of the installation file 403 | type: string 404 | example: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 405 | 406 | installationSignatureType: 407 | description: Type of the file signature 408 | type: string 409 | example: sha-256 410 | 411 | installationSize: 412 | description: Size of the installation file (bytes) 413 | type: integer 414 | example: 1048576 415 | 416 | installationDesc: 417 | description: Optional file description 418 | type: string 419 | example: package.gwa-core-v1.1 420 | 421 | installationStatus: 422 | description: Status of the installation 423 | type: string 424 | enum: 425 | - in_progress 426 | - success 427 | - error 428 | example: in_progress 429 | 430 | installationMessage: 431 | description: Optional response message 432 | type: string 433 | example: message describing installation progress 434 | minLength: 1 435 | maxLength: 256 436 | 437 | installationBuildStamp: 438 | description: a build stamp of the software to be installed 439 | type: string 440 | 441 | # errors 442 | error: 443 | type: object 444 | required: 445 | - error 446 | - topic 447 | - payload 448 | - messageId 449 | properties: 450 | error: 451 | $ref: '#/components/schemas/errorContent' 452 | topic: 453 | $ref: '#/components/schemas/errorTopic' 454 | payload: 455 | $ref: '#/components/schemas/errorPayload' 456 | messageId: 457 | $ref: '#/components/schemas/errorMessageId' 458 | 459 | errorContent: 460 | description: Human-readable error message 461 | type: string 462 | example: command field 'id' is NOT an UUID 463 | 464 | errorTopic: 465 | description: Topic to which the malformed message was sent 466 | type: string 467 | example: devices/763c073a-e0ff-41a9-bd51-3386975ea4e3/commands 468 | 469 | errorPayload: 470 | description: Payload of the message that caused the error (stringified) 471 | type: string 472 | example: '{''id'':''not UUID'',''status'':''in_progress''}' 473 | 474 | errorMessageId: 475 | description: Id of the message (for tracking purposes) 476 | type: integer 477 | example: 31248 478 | 479 | log: 480 | type: object 481 | required: 482 | - level 483 | - message 484 | properties: 485 | level: 486 | $ref: '#/components/schemas/logLevel' 487 | message: 488 | $ref: '#/components/schemas/logMessage' 489 | timestamp: 490 | $ref: '#/components/schemas/datetime' 491 | 492 | logLevel: 493 | description: | 494 | Level of log severity (0 - 5) 495 | 5 = trace 496 | 4 = debug 497 | 3 = minor 498 | 2 = major 499 | 1 = error 500 | 0 = critical 501 | type: integer 502 | minimum: 0 503 | maximum: 5 504 | example: 2 505 | 506 | logMessage: 507 | description: Log message 508 | type: string 509 | example: Device has been restarted 510 | 511 | anomalyScore: 512 | type: object 513 | required: 514 | - score 515 | - deviceFeatureVectorTime 516 | - deviceGroupId 517 | - phaseName 518 | - attributeImportance 519 | - anomalyRegions 520 | additionalProperties: false 521 | properties: 522 | score: 523 | description: The raw anomaly score of the edge event. 524 | The higher the score, the more anomalous the event. 525 | The score can, but does not have to be normalized to the range [0,1]. 526 | If the anomaly score is not normalized to [0,1], we can normalize the score in the cloud using a `ScoreNormalizer` in the `analytics-event-service`. 527 | type: number 528 | totalNumberOfContributors: 529 | description: The total number of contributors (ie features) that were considered by the anomaly model, 530 | not only the N most important ones that are directly reported. 531 | type: number 532 | minimum: 0 533 | attributeImportance: 534 | type: object 535 | description: A list of features and their respective importances. 536 | This is a measure of the relative "importance" of each of the features used by the model. 537 | Each feature is assigned a percentage weighting of how much it contributed to the anomaly score. 538 | In the above example, the model has thus determined that one feature is 90% responsible for the current anomaly, 539 | and another feature for the remaining 10%. All attributeImportances sum up to 1.0. 540 | The list of attributeImportances is truncated to the N most important features, where N is configurable. 541 | required: 542 | - rawFeatures 543 | properties: 544 | rawFeatures: 545 | type: object 546 | description: A mapping between features' names and their respective importances, grouped by respective device ids. 547 | engineeredFeatures: 548 | type: object 549 | description: A mapping between features' names and their respective importances. 550 | anomalyRegions: 551 | description: Represents the models interpretation of the most likely range of values it expects to find for that particular feature. 552 | Provided for each of the N most important features. 553 | The `min` and `max` determines the lower and upper bounds of the encoded anomaly region, 554 | and the `values` list represents equally spaced buckets over that range. 555 | The values themselves represent the relative expectation of finding the feature within this region. 556 | The lower the value, the higher is the probability to find the value in that bucket. 557 | In other words, the values array encodes an inverse probability distribution. 558 | Observing a feature value outside the range [`min`, `max`] always corresponds to an anomalous feature. 559 | The `featureValue` encodes the actual value of the feature when the event was created. 560 | type: object 561 | required: 562 | - rawFeatures 563 | properties: 564 | rawFeatures: 565 | type: object 566 | description: A mapping between features' names and their respective anomaly regions, grouped by respective device ids. 567 | engineeredFeatures: 568 | type: object 569 | description: A mapping between features' names and their respective anomaly regions. 570 | deviceFeatureVectorTime: 571 | description: The timestamp of the event, ie when the event was created. 572 | $ref: '#/components/schemas/datetime' 573 | deviceGroupId: 574 | description: The cloud2.0 deviceGroupId to which this `anomaly event` belongs. 575 | $ref: '#/components/schemas/deviceGroupId' 576 | phaseName: 577 | description: Most systems operate in multiple states or phases, the most simple example being on and off. Take for example a coffee machine, this can have multiple different phases such as grinding beans, boiling water, extracting coffee, frothing milk etc.. For the purposes of analytics and machine learning, attempting to build a model to detect anomalies or specific behaviours across all of those drastically different phases is not a simple task. A more reliable approach would be to create one more specifically taylored model to detect problems in each of the phases. The `phaseName` is a string that is used to differentiate those phases. 578 | type: string 579 | minLength: 1 580 | maxLength: 64 581 | thresholdChange: 582 | description: boolean flag to indicate if this event is used for updating feature threshold and should not be processed by RCA or health state services. 583 | type: boolean 584 | 585 | MQTTQoSHeader: 586 | type: number 587 | format: int32 588 | description: Quality of Service (0 - 1) 589 | enum: 590 | - 1 591 | - 0 592 | 593 | securitySchemes: 594 | userPass: 595 | type: userPassword 596 | description: | 597 | Username and password obtained from the **Cloud** [MQTT endpoints](http://api-documentation.relayr.io/#tag/MQTT-Credentials-Device) 598 | X509Certificate: 599 | type: X509 600 | description: | 601 | X.509 Certificate 602 | 603 | messages: 604 | 605 | locations: 606 | name: devices/locations 607 | summary: Location of the device 608 | description: Device location is received from the device through MQTT and published to Kafka. Location service receives the Kafka message and updates the device location. 609 | tags: 610 | - name: location 611 | headers: 612 | type: object 613 | properties: 614 | qos: 615 | $ref: '#/components/schemas/MQTTQoSHeader' 616 | payload: 617 | type: array 618 | items: 619 | type: object 620 | properties: 621 | latitude: 622 | description: number of degrees north 623 | type: number 624 | minimum: -90 625 | maximum: 90 626 | longitude: 627 | description: number of degrees east 628 | type: number 629 | minimum: -180 630 | maximum: 180 631 | address: 632 | description: address of device location 633 | type: string 634 | maxLength: 200 635 | zipCode: 636 | description: zipCode of device location 637 | type: string 638 | city: 639 | description: city of device location 640 | type: string 641 | country: 642 | description: Country code from ISO 3166-1 alpha-2 standard 643 | type: string 644 | example: UA 645 | timestamp: 646 | $ref: '#/components/schemas/datetime' 647 | required: 648 | - longitude 649 | - latitude 650 | additionalProperties: false 651 | 652 | measurements: 653 | name: devices/measurements 654 | summary: Publish measurement(s) 655 | description: | 656 | Measurements are sent in an array which contains one or more measurements 657 | for a single device. Measurements **must** be defined in the [device model version](http://api-documentation.relayr.io/#tag/Device-Models/paths/~1device-models~1{modelId}~1versions/post). 658 | tags: 659 | - name: measurements 660 | payload: 661 | type: array 662 | items: 663 | $ref: '#/components/schemas/measurement' 664 | headers: 665 | type: object 666 | properties: 667 | qos: 668 | $ref: '#/components/schemas/MQTTQoSHeader' 669 | 670 | alerts: 671 | name: devices/alerts 672 | summary: Publish alert(s) 673 | tags: 674 | - name: alerts 675 | payload: 676 | type: array 677 | items: 678 | $ref: '#/components/schemas/alert' 679 | headers: 680 | type: object 681 | properties: 682 | qos: 683 | $ref: '#/components/schemas/MQTTQoSHeader' 684 | 685 | commands: 686 | name: devices/commands 687 | description: | 688 | Commands can be sent and executed on devices through the **Cloud** command import [endpoint](http://api-documentation.relayr.io/#tag/Commands-Import-HTTP) 689 | and command execution [endpoint](http://api-documentation.relayr.io/#tag/Commands-Execution-HTTP). 690 | summary: Subscribe to device commands 691 | tags: 692 | - name: commands 693 | payload: 694 | $ref: '#/components/schemas/command' 695 | headers: 696 | type: object 697 | properties: 698 | qos: 699 | $ref: '#/components/schemas/MQTTQoSHeader' 700 | 701 | command_responses: 702 | name: devices/command_responses 703 | summary: Publish command response(s) 704 | tags: 705 | - name: commands 706 | payload: 707 | type: array 708 | items: 709 | $ref: '#/components/schemas/commandResponse' 710 | headers: 711 | type: object 712 | properties: 713 | qos: 714 | $ref: '#/components/schemas/MQTTQoSHeader' 715 | 716 | configs: 717 | name: devices/configs 718 | description: | 719 | Configurations can be sent to devices through the **Cloud** configuration [endpoints](http://services-docs.relayr.io/ext-api/#tag/Device-configurations/paths/~1devices~1{deviceId}~1configurations/post). 720 | summary: Subscribe to device configurations 721 | tags: 722 | - name: configurations 723 | payload: 724 | $ref: '#/components/schemas/configuration' 725 | headers: 726 | type: object 727 | properties: 728 | qos: 729 | $ref: '#/components/schemas/MQTTQoSHeader' 730 | 731 | config_responses: 732 | name: devices/config_responses 733 | summary: Publish configuration response(s) 734 | tags: 735 | - name: configurations 736 | payload: 737 | type: array 738 | items: 739 | $ref: '#/components/schemas/configurationResponse' 740 | headers: 741 | type: object 742 | properties: 743 | qos: 744 | $ref: '#/components/schemas/MQTTQoSHeader' 745 | 746 | config_requests: 747 | name: devices/config_requests 748 | summary: Publish configuration request 749 | tags: 750 | - name: configurations 751 | payload: 752 | type: array 753 | items: 754 | $ref: '#/components/schemas/configurationRequest' 755 | headers: 756 | type: object 757 | properties: 758 | qos: 759 | $ref: '#/components/schemas/MQTTQoSHeader' 760 | 761 | installations: 762 | name: devices/installations 763 | description: | 764 | Installations can be sent to devices through the **Cloud** software installation [endpoints](http://api-documentation.relayr.io/#tag/Software-Installation-Device). 765 | summary: Subscribe to device installations 766 | tags: 767 | - name: installations 768 | payload: 769 | $ref: '#/components/schemas/installation' 770 | headers: 771 | type: object 772 | properties: 773 | qos: 774 | $ref: '#/components/schemas/MQTTQoSHeader' 775 | 776 | installation_responses: 777 | name: devices/installation_responses 778 | summary: Publish configuration response(s) 779 | tags: 780 | - name: installations 781 | payload: 782 | type: array 783 | items: 784 | $ref: '#/components/schemas/installationResponse' 785 | headers: 786 | type: object 787 | properties: 788 | qos: 789 | $ref: '#/components/schemas/MQTTQoSHeader' 790 | 791 | errors: 792 | name: devices/errors 793 | description: | 794 | When malformed message is published it will be filtered out and rejected. \ 795 | Reason for rejection can be obtained from the **/errors** topic. 796 | summary: Subscribe to device errors 797 | tags: 798 | - name: errors 799 | payload: 800 | $ref: '#/components/schemas/error' 801 | headers: 802 | type: object 803 | properties: 804 | qos: 805 | $ref: '#/components/schemas/MQTTQoSHeader' 806 | 807 | logs: 808 | name: devices/logs 809 | summary: Publish device log(s) 810 | tags: 811 | - name: logs 812 | payload: 813 | type: array 814 | items: 815 | $ref: '#/components/schemas/log' 816 | headers: 817 | type: object 818 | properties: 819 | qos: 820 | $ref: '#/components/schemas/MQTTQoSHeader' 821 | 822 | analytics_events: 823 | name: device-groups/analytics_events 824 | summary: Publish analytics events 825 | tags: 826 | - name: analytics events 827 | payload: 828 | type: array 829 | items: 830 | $ref: '#/components/schemas/anomalyScore' 831 | headers: 832 | type: object 833 | properties: 834 | qos: 835 | $ref: '#/components/schemas/MQTTQoSHeader' 836 | -------------------------------------------------------------------------------- /test/schemas/v2.0.0/slack.yml: -------------------------------------------------------------------------------- 1 | asyncapi: '2.0.0' 2 | id: 'urn:com:slack:rtm:api' 3 | info: 4 | title: Slack Real Time Messaging API 5 | version: '1.0.0' 6 | 7 | servers: 8 | production: 9 | url: https://slack.com/api/rtm.connect 10 | protocol: https 11 | protocolVersion: '1.1' 12 | security: 13 | - token: [] 14 | 15 | channels: 16 | /: 17 | publish: 18 | message: 19 | $ref: '#/components/messages/outgoingMessage' 20 | subscribe: 21 | message: 22 | oneOf: 23 | - $ref: '#/components/messages/hello' 24 | - $ref: '#/components/messages/connectionError' 25 | - $ref: '#/components/messages/accountsChanged' 26 | - $ref: '#/components/messages/botAdded' 27 | - $ref: '#/components/messages/botChanged' 28 | - $ref: '#/components/messages/channelArchive' 29 | - $ref: '#/components/messages/channelCreated' 30 | - $ref: '#/components/messages/channelDeleted' 31 | - $ref: '#/components/messages/channelHistoryChanged' 32 | - $ref: '#/components/messages/channelJoined' 33 | - $ref: '#/components/messages/channelLeft' 34 | - $ref: '#/components/messages/channelMarked' 35 | - $ref: '#/components/messages/channelRename' 36 | - $ref: '#/components/messages/channelUnarchive' 37 | - $ref: '#/components/messages/commandsChanged' 38 | - $ref: '#/components/messages/dndUpdated' 39 | - $ref: '#/components/messages/dndUpdatedUser' 40 | - $ref: '#/components/messages/emailDomainChanged' 41 | - $ref: '#/components/messages/emojiRemoved' 42 | - $ref: '#/components/messages/emojiAdded' 43 | - $ref: '#/components/messages/fileChange' 44 | - $ref: '#/components/messages/fileCommentAdded' 45 | - $ref: '#/components/messages/fileCommentDeleted' 46 | - $ref: '#/components/messages/fileCommentEdited' 47 | - $ref: '#/components/messages/fileCreated' 48 | - $ref: '#/components/messages/fileDeleted' 49 | - $ref: '#/components/messages/filePublic' 50 | - $ref: '#/components/messages/fileShared' 51 | - $ref: '#/components/messages/fileUnshared' 52 | - $ref: '#/components/messages/goodbye' 53 | - $ref: '#/components/messages/groupArchive' 54 | - $ref: '#/components/messages/groupClose' 55 | - $ref: '#/components/messages/groupHistoryChanged' 56 | - $ref: '#/components/messages/groupJoined' 57 | - $ref: '#/components/messages/groupLeft' 58 | - $ref: '#/components/messages/groupMarked' 59 | - $ref: '#/components/messages/groupOpen' 60 | - $ref: '#/components/messages/groupRename' 61 | - $ref: '#/components/messages/groupUnarchive' 62 | - $ref: '#/components/messages/imClose' 63 | - $ref: '#/components/messages/imCreated' 64 | - $ref: '#/components/messages/imMarked' 65 | - $ref: '#/components/messages/imOpen' 66 | - $ref: '#/components/messages/manualPresenceChange' 67 | - $ref: '#/components/messages/memberJoinedChannel' 68 | - $ref: '#/components/messages/message' 69 | 70 | components: 71 | securitySchemes: 72 | token: 73 | type: httpApiKey 74 | name: token 75 | in: query 76 | 77 | schemas: 78 | attachment: 79 | type: object 80 | properties: 81 | fallback: 82 | type: string 83 | color: 84 | type: string 85 | pretext: 86 | type: string 87 | author_name: 88 | type: string 89 | author_link: 90 | type: string 91 | format: uri 92 | author_icon: 93 | type: string 94 | format: uri 95 | title: 96 | type: string 97 | title_link: 98 | type: string 99 | format: uri 100 | text: 101 | type: string 102 | fields: 103 | type: array 104 | items: 105 | type: object 106 | properties: 107 | title: 108 | type: string 109 | value: 110 | type: string 111 | short: 112 | type: boolean 113 | image_url: 114 | type: string 115 | format: uri 116 | thumb_url: 117 | type: string 118 | format: uri 119 | footer: 120 | type: string 121 | footer_icon: 122 | type: string 123 | format: uri 124 | ts: 125 | type: number 126 | 127 | messages: 128 | hello: 129 | name: hello 130 | summary: 'First event received upon connection.' 131 | payload: 132 | type: object 133 | properties: 134 | type: 135 | type: string 136 | enum: 137 | - hello 138 | connectionError: 139 | name: connectionError 140 | summary: 'Event received when a connection error happens.' 141 | payload: 142 | type: object 143 | properties: 144 | type: 145 | type: string 146 | enum: 147 | - error 148 | error: 149 | type: object 150 | properties: 151 | code: 152 | type: number 153 | msg: 154 | type: string 155 | accountsChanged: 156 | name: accountsChanged 157 | summary: 'The list of accounts a user is signed into has changed.' 158 | payload: 159 | type: object 160 | properties: 161 | type: 162 | type: string 163 | enum: 164 | - accounts_changed 165 | botAdded: 166 | name: botAdded 167 | summary: 'A bot user was added.' 168 | payload: 169 | type: object 170 | properties: 171 | type: 172 | type: string 173 | enum: 174 | - bot_added 175 | bot: 176 | type: object 177 | properties: 178 | id: 179 | type: string 180 | app_id: 181 | type: string 182 | name: 183 | type: string 184 | icons: 185 | type: object 186 | additionalProperties: 187 | type: string 188 | botChanged: 189 | name: botChanged 190 | summary: 'A bot user was changed.' 191 | payload: 192 | type: object 193 | properties: 194 | type: 195 | type: string 196 | enum: 197 | - bot_added 198 | bot: 199 | type: object 200 | properties: 201 | id: 202 | type: string 203 | app_id: 204 | type: string 205 | name: 206 | type: string 207 | icons: 208 | type: object 209 | additionalProperties: 210 | type: string 211 | channelArchive: 212 | name: channelArchive 213 | summary: 'A channel was archived.' 214 | payload: 215 | type: object 216 | properties: 217 | type: 218 | type: string 219 | enum: 220 | - channel_archive 221 | channel: 222 | type: string 223 | user: 224 | type: string 225 | channelCreated: 226 | name: channelArchive 227 | summary: 'A channel was created.' 228 | payload: 229 | type: object 230 | properties: 231 | type: 232 | type: string 233 | enum: 234 | - channel_created 235 | channel: 236 | type: object 237 | properties: 238 | id: 239 | type: string 240 | name: 241 | type: string 242 | created: 243 | type: number 244 | creator: 245 | type: string 246 | channelDeleted: 247 | name: channelDeleted 248 | summary: 'A channel was deleted.' 249 | payload: 250 | type: object 251 | properties: 252 | type: 253 | type: string 254 | enum: 255 | - channel_deleted 256 | channel: 257 | type: string 258 | channelHistoryChanged: 259 | name: channelHistoryChanged 260 | summary: 'Bulk updates were made to a channel''s history.' 261 | payload: 262 | type: object 263 | properties: 264 | type: 265 | type: string 266 | enum: 267 | - channel_history_changed 268 | latest: 269 | type: string 270 | ts: 271 | type: string 272 | event_ts: 273 | type: string 274 | channelJoined: 275 | name: channelJoined 276 | summary: 'You joined a channel.' 277 | payload: 278 | type: object 279 | properties: 280 | type: 281 | type: string 282 | enum: 283 | - channel_joined 284 | channel: 285 | type: object 286 | properties: 287 | id: 288 | type: string 289 | name: 290 | type: string 291 | created: 292 | type: number 293 | creator: 294 | type: string 295 | channelLeft: 296 | name: channelLeft 297 | summary: 'You left a channel.' 298 | payload: 299 | type: object 300 | properties: 301 | type: 302 | type: string 303 | enum: 304 | - channel_left 305 | channel: 306 | type: string 307 | channelMarked: 308 | name: channelMarked 309 | summary: 'Your channel read marker was updated.' 310 | payload: 311 | type: object 312 | properties: 313 | type: 314 | type: string 315 | enum: 316 | - channel_marked 317 | channel: 318 | type: string 319 | ts: 320 | type: string 321 | channelRename: 322 | name: channelRename 323 | summary: 'A channel was renamed.' 324 | payload: 325 | type: object 326 | properties: 327 | type: 328 | type: string 329 | enum: 330 | - channel_rename 331 | channel: 332 | type: object 333 | properties: 334 | id: 335 | type: string 336 | name: 337 | type: string 338 | created: 339 | type: number 340 | channelUnarchive: 341 | name: channelUnarchive 342 | summary: 'A channel was unarchived.' 343 | payload: 344 | type: object 345 | properties: 346 | type: 347 | type: string 348 | enum: 349 | - channel_unarchive 350 | channel: 351 | type: string 352 | user: 353 | type: string 354 | commandsChanged: 355 | name: commandsChanged 356 | summary: 'A slash command has been added or changed.' 357 | payload: 358 | type: object 359 | properties: 360 | type: 361 | type: string 362 | enum: 363 | - commands_changed 364 | event_ts: 365 | type: string 366 | dndUpdated: 367 | name: dndUpdated 368 | summary: 'Do not Disturb settings changed for the current user.' 369 | payload: 370 | type: object 371 | properties: 372 | type: 373 | type: string 374 | enum: 375 | - dnd_updated 376 | user: 377 | type: string 378 | dnd_status: 379 | type: object 380 | properties: 381 | dnd_enabled: 382 | type: boolean 383 | next_dnd_start_ts: 384 | type: number 385 | next_dnd_end_ts: 386 | type: number 387 | snooze_enabled: 388 | type: boolean 389 | snooze_endtime: 390 | type: number 391 | dndUpdatedUser: 392 | name: dndUpdatedUser 393 | summary: 'Do not Disturb settings changed for a member.' 394 | payload: 395 | type: object 396 | properties: 397 | type: 398 | type: string 399 | enum: 400 | - dnd_updated_user 401 | user: 402 | type: string 403 | dnd_status: 404 | type: object 405 | properties: 406 | dnd_enabled: 407 | type: boolean 408 | next_dnd_start_ts: 409 | type: number 410 | next_dnd_end_ts: 411 | type: number 412 | emailDomainChanged: 413 | name: emailDomainChanged 414 | summary: 'The workspace email domain has changed.' 415 | payload: 416 | type: object 417 | properties: 418 | type: 419 | type: string 420 | enum: 421 | - email_domain_changed 422 | email_domain: 423 | type: string 424 | event_ts: 425 | type: string 426 | emojiRemoved: 427 | name: emojiRemoved 428 | summary: 'A custom emoji has been removed.' 429 | payload: 430 | type: object 431 | properties: 432 | type: 433 | type: string 434 | enum: 435 | - emoji_changed 436 | subtype: 437 | type: string 438 | enum: 439 | - remove 440 | names: 441 | type: array 442 | items: 443 | type: string 444 | event_ts: 445 | type: string 446 | emojiAdded: 447 | name: emojiAdded 448 | summary: 'A custom emoji has been added.' 449 | payload: 450 | type: object 451 | properties: 452 | type: 453 | type: string 454 | enum: 455 | - emoji_changed 456 | subtype: 457 | type: string 458 | enum: 459 | - add 460 | name: 461 | type: string 462 | value: 463 | type: string 464 | format: uri 465 | event_ts: 466 | type: string 467 | fileChange: 468 | name: fileChange 469 | summary: 'A file was changed.' 470 | payload: 471 | type: object 472 | properties: 473 | type: 474 | type: string 475 | enum: 476 | - file_change 477 | file_id: 478 | type: string 479 | file: 480 | type: object 481 | properties: 482 | id: 483 | type: string 484 | fileCommentAdded: 485 | name: fileCommentAdded 486 | summary: 'A file comment was added.' 487 | payload: 488 | type: object 489 | properties: 490 | type: 491 | type: string 492 | enum: 493 | - file_comment_added 494 | comment: {} 495 | file_id: 496 | type: string 497 | file: 498 | type: object 499 | properties: 500 | id: 501 | type: string 502 | fileCommentDeleted: 503 | name: fileCommentDeleted 504 | summary: 'A file comment was deleted.' 505 | payload: 506 | type: object 507 | properties: 508 | type: 509 | type: string 510 | enum: 511 | - file_comment_deleted 512 | comment: 513 | type: string 514 | file_id: 515 | type: string 516 | file: 517 | type: object 518 | properties: 519 | id: 520 | type: string 521 | fileCommentEdited: 522 | name: fileCommentEdited 523 | summary: 'A file comment was edited.' 524 | payload: 525 | type: object 526 | properties: 527 | type: 528 | type: string 529 | enum: 530 | - file_comment_edited 531 | comment: {} 532 | file_id: 533 | type: string 534 | file: 535 | type: object 536 | properties: 537 | id: 538 | type: string 539 | fileCreated: 540 | name: fileCreated 541 | summary: 'A file was created.' 542 | payload: 543 | type: object 544 | properties: 545 | type: 546 | type: string 547 | enum: 548 | - file_created 549 | file_id: 550 | type: string 551 | file: 552 | type: object 553 | properties: 554 | id: 555 | type: string 556 | fileDeleted: 557 | name: fileDeleted 558 | summary: 'A file was deleted.' 559 | payload: 560 | type: object 561 | properties: 562 | type: 563 | type: string 564 | enum: 565 | - file_deleted 566 | file_id: 567 | type: string 568 | event_ts: 569 | type: string 570 | filePublic: 571 | name: filePublic 572 | summary: 'A file was made public.' 573 | payload: 574 | type: object 575 | properties: 576 | type: 577 | type: string 578 | enum: 579 | - file_public 580 | file_id: 581 | type: string 582 | file: 583 | type: object 584 | properties: 585 | id: 586 | type: string 587 | fileShared: 588 | name: fileShared 589 | summary: 'A file was shared.' 590 | payload: 591 | type: object 592 | properties: 593 | type: 594 | type: string 595 | enum: 596 | - file_shared 597 | file_id: 598 | type: string 599 | file: 600 | type: object 601 | properties: 602 | id: 603 | type: string 604 | fileUnshared: 605 | name: fileUnshared 606 | summary: 'A file was unshared.' 607 | payload: 608 | type: object 609 | properties: 610 | type: 611 | type: string 612 | enum: 613 | - file_unshared 614 | file_id: 615 | type: string 616 | file: 617 | type: object 618 | properties: 619 | id: 620 | type: string 621 | goodbye: 622 | name: goodbye 623 | summary: 'The server intends to close the connection soon.' 624 | payload: 625 | type: object 626 | properties: 627 | type: 628 | type: string 629 | enum: 630 | - goodbye 631 | groupArchive: 632 | name: groupArchive 633 | summary: 'A private channel was archived.' 634 | payload: 635 | type: object 636 | properties: 637 | type: 638 | type: string 639 | enum: 640 | - group_archive 641 | channel: 642 | type: string 643 | groupClose: 644 | name: groupClose 645 | summary: 'You closed a private channel.' 646 | payload: 647 | type: object 648 | properties: 649 | type: 650 | type: string 651 | enum: 652 | - group_close 653 | user: 654 | type: string 655 | channel: 656 | type: string 657 | groupHistoryChanged: 658 | name: groupHistoryChanged 659 | summary: 'Bulk updates were made to a private channel''s history.' 660 | payload: 661 | type: object 662 | properties: 663 | type: 664 | type: string 665 | enum: 666 | - group_history_changed 667 | latest: 668 | type: string 669 | ts: 670 | type: string 671 | event_ts: 672 | type: string 673 | groupJoined: 674 | name: groupJoined 675 | summary: 'You joined a private channel.' 676 | payload: 677 | type: object 678 | properties: 679 | type: 680 | type: string 681 | enum: 682 | - group_joined 683 | channel: 684 | type: object 685 | properties: 686 | id: 687 | type: string 688 | name: 689 | type: string 690 | created: 691 | type: number 692 | creator: 693 | type: string 694 | groupLeft: 695 | name: groupLeft 696 | summary: 'You left a private channel.' 697 | payload: 698 | type: object 699 | properties: 700 | type: 701 | type: string 702 | enum: 703 | - group_left 704 | channel: 705 | type: string 706 | groupMarked: 707 | name: groupMarked 708 | summary: 'A private channel read marker was updated.' 709 | payload: 710 | type: object 711 | properties: 712 | type: 713 | type: string 714 | enum: 715 | - group_marked 716 | channel: 717 | type: string 718 | ts: 719 | type: string 720 | groupOpen: 721 | name: groupOpen 722 | summary: 'You opened a private channel.' 723 | payload: 724 | type: object 725 | properties: 726 | type: 727 | type: string 728 | enum: 729 | - group_open 730 | user: 731 | type: string 732 | channel: 733 | type: string 734 | groupRename: 735 | name: groupRename 736 | summary: 'A private channel was renamed.' 737 | payload: 738 | type: object 739 | properties: 740 | type: 741 | type: string 742 | enum: 743 | - group_rename 744 | channel: 745 | type: object 746 | properties: 747 | id: 748 | type: string 749 | name: 750 | type: string 751 | created: 752 | type: number 753 | groupUnarchive: 754 | name: groupUnarchive 755 | summary: 'A private channel was unarchived.' 756 | payload: 757 | type: object 758 | properties: 759 | type: 760 | type: string 761 | enum: 762 | - group_unarchive 763 | channel: 764 | type: string 765 | user: 766 | type: string 767 | imClose: 768 | name: imClose 769 | summary: 'You closed a DM.' 770 | payload: 771 | type: object 772 | properties: 773 | type: 774 | type: string 775 | enum: 776 | - im_close 777 | channel: 778 | type: string 779 | user: 780 | type: string 781 | imCreated: 782 | name: imCreated 783 | summary: 'A DM was created.' 784 | payload: 785 | type: object 786 | properties: 787 | type: 788 | type: string 789 | enum: 790 | - im_created 791 | channel: 792 | type: object 793 | properties: 794 | id: 795 | type: string 796 | name: 797 | type: string 798 | created: 799 | type: number 800 | creator: 801 | type: string 802 | user: 803 | type: string 804 | imMarked: 805 | name: imMarked 806 | summary: 'A direct message read marker was updated.' 807 | payload: 808 | type: object 809 | properties: 810 | type: 811 | type: string 812 | enum: 813 | - im_marked 814 | channel: 815 | type: string 816 | ts: 817 | type: string 818 | imOpen: 819 | name: imOpen 820 | summary: 'You opened a DM.' 821 | payload: 822 | type: object 823 | properties: 824 | type: 825 | type: string 826 | enum: 827 | - im_open 828 | channel: 829 | type: string 830 | user: 831 | type: string 832 | manualPresenceChange: 833 | name: manualPresenceChange 834 | summary: 'You manually updated your presence.' 835 | payload: 836 | type: object 837 | properties: 838 | type: 839 | type: string 840 | enum: 841 | - manual_presence_change 842 | presence: 843 | type: string 844 | memberJoinedChannel: 845 | name: memberJoinedChannel 846 | summary: 'A user joined a public or private channel.' 847 | payload: 848 | type: object 849 | properties: 850 | type: 851 | type: string 852 | enum: 853 | - member_joined_channel 854 | user: 855 | type: string 856 | channel: 857 | type: string 858 | channel_type: 859 | type: string 860 | enum: 861 | - C 862 | - G 863 | team: 864 | type: string 865 | inviter: 866 | type: string 867 | memberLeftChannel: 868 | name: memberLeftChannel 869 | summary: 'A user left a public or private channel.' 870 | payload: 871 | type: object 872 | properties: 873 | type: 874 | type: string 875 | enum: 876 | - member_left_channel 877 | user: 878 | type: string 879 | channel: 880 | type: string 881 | channel_type: 882 | type: string 883 | enum: 884 | - C 885 | - G 886 | team: 887 | type: string 888 | message: 889 | name: message 890 | summary: 'A message was sent to a channel.' 891 | payload: 892 | type: object 893 | properties: 894 | type: 895 | type: string 896 | enum: 897 | - message 898 | user: 899 | type: string 900 | channel: 901 | type: string 902 | text: 903 | type: string 904 | ts: 905 | type: string 906 | attachments: 907 | type: array 908 | items: 909 | $ref: '#/components/schemas/attachment' 910 | edited: 911 | type: object 912 | properties: 913 | user: 914 | type: string 915 | ts: 916 | type: string 917 | outgoingMessage: 918 | name: outgoingMessage 919 | summary: 'A message was sent to a channel.' 920 | payload: 921 | type: object 922 | properties: 923 | id: 924 | type: number 925 | type: 926 | type: string 927 | enum: 928 | - message 929 | channel: 930 | type: string 931 | text: 932 | type: string 933 | -------------------------------------------------------------------------------- /test/schemas/v2.0.0/userMessages.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.0.0 2 | 3 | info: 4 | title: User Events 5 | version: 1.0.0 6 | 7 | channels: 8 | user-events: 9 | description: user related events 10 | publish: 11 | message: 12 | name: UserDeletedMessage 13 | x-custom-key: UserDeleted 14 | payload: 15 | type: object 16 | properties: 17 | userEmail: 18 | type: string 19 | userId: 20 | type: string 21 | -------------------------------------------------------------------------------- /test/schemas/v2.4.0/DeviceInstallationPublished.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.4.0 2 | 3 | info: 4 | title: Kafka Messages definition 5 | version: 1.0.0 6 | description: Kafka messages specification 7 | 8 | components: 9 | schemas: 10 | DeviceInstallationPublished: 11 | allOf: 12 | - $ref: '#/components/schemas/BasicKafkaEnvelope' 13 | - type: object 14 | properties: 15 | data: 16 | type: object 17 | required: 18 | - deviceId 19 | - id 20 | - received 21 | - recorded 22 | properties: 23 | deviceId: 24 | $ref: '#/components/schemas/UUID' 25 | id: 26 | $ref: '#/components/schemas/UUID' 27 | received: 28 | $ref: '#/components/schemas/Timestamp' 29 | recorded: 30 | $ref: '#/components/schemas/Timestamp' 31 | 32 | # ----------------- OTHER schemas ------------------- 33 | Timestamp: 34 | title: timestamp 35 | type: string 36 | pattern: >- 37 | ^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$ 38 | example: '2017-01-09T08:27:22.222Z' 39 | 40 | UUID: 41 | type: string 42 | format: uuid 43 | example: bd58d14f-fd3e-449c-b60c-a56548190d68 44 | 45 | BasicKafkaEnvelope: 46 | type: object 47 | required: 48 | - id 49 | - key 50 | - type 51 | - generated 52 | - requestId 53 | properties: 54 | id: 55 | $ref: '#/components/schemas/UUID' 56 | key: 57 | type: string 58 | type: 59 | type: string 60 | generated: 61 | $ref: '#/components/schemas/Timestamp' 62 | requestId: 63 | type: string 64 | 65 | TaskStatus: 66 | type: string 67 | enum: 68 | - success 69 | - error 70 | - in_progress 71 | 72 | 73 | -------------------------------------------------------------------------------- /test/schemas/v2.4.0/deviceMessages.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.4.0 2 | 3 | info: 4 | title: Kafka Messages definition 5 | version: 1.0.0 6 | description: Kafka messages specification 7 | 8 | channels: 9 | device-installation-events: 10 | description: user related events 11 | bindings: 12 | kafka: 13 | cleanup.policy: delete 14 | publish: 15 | message: 16 | oneOf: 17 | - $ref: '#/components/messages/DeviceInstallationPublished' 18 | - $ref: '#/components/messages/PubDeviceInstallationResponsePublished' 19 | subscribe: 20 | message: 21 | $ref: '#/components/messages/SubDeviceInstallationResponsePublished' 22 | 23 | components: 24 | messages: 25 | DeviceInstallationPublished: 26 | name: DeviceInstallationPublished 27 | title: DeviceInstallationPublished 28 | summary: Device Installation Published 29 | messageId: DeviceInstallationPublishedMessage 30 | payload: 31 | $ref: 'DeviceInstallationPublished.yaml#/components/schemas/DeviceInstallationPublished' 32 | PubDeviceInstallationResponsePublished: 33 | name: DeviceInstallationResponsePublished 34 | title: DeviceInstallationResponsePublished 35 | summary: Device Installation Response Published 36 | messageId: PubDeviceInstallationResponsePublishedMessage 37 | schemaFormat: 'application/schema+yaml;version=draft-07' 38 | payload: 39 | $ref: '#/components/schemas/DeviceInstallationResponsePublished' 40 | SubDeviceInstallationResponsePublished: 41 | name: DeviceInstallationResponsePublished 42 | title: DeviceInstallationResponsePublished 43 | summary: Device Installation Response Published 44 | messageId: SubDeviceInstallationResponsePublishedMessage 45 | schemaFormat: 'application/schema+yaml;version=draft-07' 46 | payload: 47 | $ref: '#/components/schemas/DeviceInstallationResponsePublished' 48 | 49 | schemas: 50 | DeviceInstallationResponsePublished: 51 | allOf: 52 | - $ref: '#/components/schemas/BasicKafkaEnvelope' 53 | - type: object 54 | properties: 55 | data: 56 | type: object 57 | required: 58 | - deviceId 59 | - id 60 | - status 61 | additionalProperties: false 62 | properties: 63 | deviceId: 64 | $ref: '#/components/schemas/UUID' 65 | id: 66 | $ref: '#/components/schemas/UUID' 67 | status: 68 | $ref: '#/components/schemas/TaskStatus' 69 | message: 70 | type: string 71 | temperature: 72 | type: integer 73 | format: int32 74 | vibration: 75 | type: integer 76 | format: int64 77 | floatType: 78 | type: number 79 | format: float 80 | doubleType: 81 | type: number 82 | format: double 83 | byteType: 84 | type: string 85 | format: byte 86 | numberType: 87 | type: number 88 | minimum: 10 89 | exclusiveMaximum: 20 90 | 91 | # ----------------- OTHER schemas ------------------- 92 | Timestamp: 93 | title: timestamp 94 | type: string 95 | pattern: >- 96 | ^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$ 97 | example: '2017-01-09T08:27:22.222Z' 98 | 99 | UUID: 100 | type: string 101 | format: uuid 102 | example: bd58d14f-fd3e-449c-b60c-a56548190d68 103 | 104 | BasicKafkaEnvelope: 105 | type: object 106 | required: 107 | - id 108 | - key 109 | - type 110 | - generated 111 | - requestId 112 | properties: 113 | id: 114 | $ref: '#/components/schemas/UUID' 115 | key: 116 | type: string 117 | type: 118 | type: string 119 | generated: 120 | $ref: '#/components/schemas/Timestamp' 121 | requestId: 122 | type: string 123 | 124 | TaskStatus: 125 | type: string 126 | enum: 127 | - success 128 | - error 129 | - in_progress 130 | 131 | -------------------------------------------------------------------------------- /test/schemas/v2.4.0/mqtt.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.4.0 2 | info: 3 | title: MQTT Broker 4 | version: 1.16.1 5 | description: MQTT Broker 6 | x-topic-separator: / 7 | 8 | channels: 9 | 'devices/{deviceId}/measurements': 10 | parameters: 11 | deviceId: 12 | schema: 13 | $ref: '#/components/schemas/id' 14 | publish: 15 | message: 16 | $ref: '#/components/messages/measurements' 17 | 'devices/{deviceId}/alerts': 18 | parameters: 19 | deviceId: 20 | schema: 21 | $ref: '#/components/schemas/id' 22 | publish: 23 | message: 24 | $ref: '#/components/messages/alerts' 25 | 'devices/{deviceId}/commands': 26 | parameters: 27 | deviceId: 28 | schema: 29 | $ref: '#/components/schemas/id' 30 | subscribe: 31 | message: 32 | $ref: '#/components/messages/commands' 33 | 'devices/{deviceId}/command_responses': 34 | parameters: 35 | deviceId: 36 | schema: 37 | $ref: '#/components/schemas/id' 38 | publish: 39 | message: 40 | $ref: '#/components/messages/command_responses' 41 | 'devices/{deviceId}/configs': 42 | parameters: 43 | deviceId: 44 | schema: 45 | $ref: '#/components/schemas/id' 46 | subscribe: 47 | message: 48 | $ref: '#/components/messages/configs' 49 | 'devices/{deviceId}/config_responses': 50 | parameters: 51 | deviceId: 52 | schema: 53 | $ref: '#/components/schemas/id' 54 | publish: 55 | message: 56 | $ref: '#/components/messages/config_responses' 57 | 'devices/{deviceId}/config_requests': 58 | parameters: 59 | deviceId: 60 | schema: 61 | $ref: '#/components/schemas/id' 62 | publish: 63 | message: 64 | $ref: '#/components/messages/config_requests' 65 | 'devices/{deviceId}/installations': 66 | parameters: 67 | deviceId: 68 | schema: 69 | $ref: '#/components/schemas/id' 70 | subscribe: 71 | message: 72 | $ref: '#/components/messages/installations' 73 | 'devices/{deviceId}/installation_responses': 74 | parameters: 75 | deviceId: 76 | schema: 77 | $ref: '#/components/schemas/id' 78 | publish: 79 | message: 80 | $ref: '#/components/messages/installation_responses' 81 | 'devices/{deviceId}/errors': 82 | parameters: 83 | deviceId: 84 | schema: 85 | $ref: '#/components/schemas/id' 86 | subscribe: 87 | message: 88 | $ref: '#/components/messages/errors' 89 | 'devices/{deviceId}/logs': 90 | parameters: 91 | deviceId: 92 | schema: 93 | $ref: '#/components/schemas/id' 94 | publish: 95 | message: 96 | $ref: '#/components/messages/logs' 97 | 'devices/{deviceId}/locations': 98 | parameters: 99 | deviceId: 100 | schema: 101 | $ref: '#/components/schemas/id' 102 | publish: 103 | message: 104 | $ref: '#/components/messages/locations' 105 | 'device-groups/{deviceGroupId}/analytics_events': 106 | parameters: 107 | deviceGroupId: 108 | schema: 109 | $ref: '#/components/schemas/id' 110 | publish: 111 | message: 112 | $ref: '#/components/messages/analytics_events' 113 | 114 | components: 115 | schemas: 116 | id: 117 | description: Resource identifier 118 | type: string 119 | format: uuid 120 | pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ 121 | example: 763c073a-e0ff-41a9-bd51-3386975ea4e3 122 | 123 | deviceId: 124 | description: Device identifier 125 | $ref: '#/components/schemas/id' 126 | example: d44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8 127 | 128 | deviceGroupId: 129 | description: Device group identifier 130 | $ref: '#/components/schemas/id' 131 | example: d44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8 132 | 133 | datetime: 134 | description: | 135 | Date and time of the message. We don't store data with higher precision than millisecond. 136 | Measurements sent for the same millisecond will overwrite each other. If it is a "number" it should be Unix timestamp in milliseconds. 137 | If it is a string, it should be ISO 8601 format. 138 | oneOf: 139 | - type: number 140 | - type: string 141 | 142 | # measurements 143 | measurement: 144 | type: object 145 | required: 146 | - name 147 | - value 148 | properties: 149 | name: 150 | $ref: '#/components/schemas/measurementName' 151 | value: 152 | $ref: '#/components/schemas/measurementValue' 153 | timestamp: 154 | $ref: '#/components/schemas/datetime' 155 | 156 | measurementName: 157 | description: Name of the measurement defined in device-model 158 | type: string 159 | example: temperature 160 | 161 | measurementValue: 162 | description: Value of the measurement 163 | oneOf: 164 | - type: number 165 | - type: boolean 166 | - type: string 167 | example: 36.6 168 | 169 | # alerts 170 | alert: 171 | type: object 172 | required: 173 | - name 174 | - state 175 | properties: 176 | name: 177 | $ref: '#/components/schemas/alertName' 178 | state: 179 | $ref: '#/components/schemas/alertState' 180 | message: 181 | $ref: '#/components/schemas/alertMessage' 182 | timestamp: 183 | $ref: '#/components/schemas/datetime' 184 | 185 | alertName: 186 | description: Name of the alert defined in device-model 187 | type: string 188 | example: temperature_high 189 | 190 | alertState: 191 | description: State of the alert (set/clear) 192 | type: string 193 | enum: 194 | - set 195 | - clear 196 | 197 | alertMessage: 198 | description: Optional alert message 199 | type: string 200 | example: temperature too high 201 | minLength: 1 202 | maxLength: 256 203 | 204 | 205 | #commands 206 | command: 207 | type: object 208 | required: 209 | - deviceId 210 | - id 211 | - name 212 | - timestamp 213 | properties: 214 | deviceId: 215 | $ref: '#/components/schemas/deviceId' 216 | id: 217 | $ref: '#/components/schemas/commandId' 218 | name: 219 | $ref: '#/components/schemas/commandName' 220 | parameter: 221 | $ref: '#/components/schemas/commandParameter' 222 | timestamp: 223 | $ref: '#/components/schemas/datetime' 224 | 225 | commandResponse: 226 | type: object 227 | required: 228 | - id 229 | - status 230 | properties: 231 | id: 232 | $ref: '#/components/schemas/commandId' 233 | status: 234 | $ref: '#/components/schemas/commandStatus' 235 | message: 236 | $ref: '#/components/schemas/commandMessage' 237 | timestamp: 238 | $ref: '#/components/schemas/datetime' 239 | 240 | commandId: 241 | description: Command identifier 242 | $ref: '#/components/schemas/id' 243 | example: 7f5bc456-21f2-4e9e-a38f-80baf762b1c5 244 | 245 | commandName: 246 | description: Name of the command defined in device-model 247 | type: string 248 | example: dim_light 249 | 250 | commandParameter: 251 | description: Parameter of the command (depending on device-model can be optional) 252 | oneOf: 253 | - type: number 254 | - type: string 255 | - type: boolean 256 | - type: object 257 | - type: array 258 | example: true 259 | 260 | commandStatus: 261 | description: Command status 262 | type: string 263 | enum: 264 | - in_progress 265 | - success 266 | - error 267 | 268 | commandMessage: 269 | description: Optional response message 270 | type: string 271 | example: message describing the command progress 272 | minLength: 1 273 | maxLength: 256 274 | 275 | # configurations 276 | configuration: 277 | type: object 278 | required: 279 | - configuration 280 | - version 281 | - deviceId 282 | properties: 283 | configuration: 284 | $ref: '#/components/schemas/configurationPayload' 285 | version: 286 | $ref: '#/components/schemas/configurationVersion' 287 | deviceId: 288 | $ref: '#/components/schemas/deviceId' 289 | 290 | configurationResponse: 291 | type: object 292 | required: 293 | - status 294 | - version 295 | properties: 296 | status: 297 | $ref: '#/components/schemas/configurationStatus' 298 | version: 299 | $ref: '#/components/schemas/configurationVersion' 300 | message: 301 | $ref: '#/components/schemas/configurationMessage' 302 | timestamp: 303 | $ref: '#/components/schemas/datetime' 304 | 305 | configurationRequest: 306 | type: object 307 | required: 308 | - timestamp 309 | properties: 310 | timestamp: 311 | $ref: '#/components/schemas/datetime' 312 | 313 | configurationStatus: 314 | description: Status of the configuration 315 | type: string 316 | enum: 317 | - success 318 | - error 319 | example: error 320 | 321 | configurationVersion: 322 | description: Version of the configuration 323 | type: integer 324 | example: 2 325 | 326 | configurationPayload: 327 | description: Payload of the configuration (any valid JSON) 328 | example: 329 | maximum_temperature: 60 330 | 331 | configurationMessage: 332 | description: Optional response message 333 | type: string 334 | example: missing value x 335 | 336 | # installations 337 | installation: 338 | type: object 339 | required: 340 | - deviceId 341 | - id 342 | - timestamp 343 | properties: 344 | deviceId: 345 | $ref: '#/components/schemas/deviceId' 346 | id: 347 | $ref: '#/components/schemas/installationId' 348 | type: 349 | $ref: '#/components/schemas/installationType' 350 | fileName: 351 | $ref: '#/components/schemas/installationFile' 352 | location: 353 | $ref: '#/components/schemas/installationLocation' 354 | signature: 355 | $ref: '#/components/schemas/installationSignature' 356 | signatureType: 357 | $ref: '#/components/schemas/installationSignatureType' 358 | size: 359 | $ref: '#/components/schemas/installationSize' 360 | description: 361 | $ref: '#/components/schemas/installationDesc' 362 | timestamp: 363 | $ref: '#/components/schemas/datetime' 364 | buildStamp: 365 | $ref: '#/components/schemas/installationBuildStamp' 366 | 367 | installationResponse: 368 | type: object 369 | required: 370 | - id 371 | - status 372 | properties: 373 | id: 374 | $ref: '#/components/schemas/installationId' 375 | status: 376 | $ref: '#/components/schemas/installationStatus' 377 | message: 378 | $ref: '#/components/schemas/installationMessage' 379 | timestamp: 380 | $ref: '#/components/schemas/datetime' 381 | 382 | installationId: 383 | description: Installation identifier 384 | $ref: '#/components/schemas/id' 385 | 386 | installationType: 387 | description: Type of the installation file 388 | type: string 389 | example: gwa-core-package 390 | 391 | installationFile: 392 | description: Name of the installation file 393 | type: string 394 | example: gwa-core.tgz 395 | 396 | installationLocation: 397 | description: Location (url) of the installation file 398 | type: string 399 | example: http://foo.bar/buzz.xyz 400 | 401 | installationSignature: 402 | description: Signature of the installation file 403 | type: string 404 | example: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 405 | 406 | installationSignatureType: 407 | description: Type of the file signature 408 | type: string 409 | example: sha-256 410 | 411 | installationSize: 412 | description: Size of the installation file (bytes) 413 | type: integer 414 | example: 1048576 415 | 416 | installationDesc: 417 | description: Optional file description 418 | type: string 419 | example: package.gwa-core-v1.1 420 | 421 | installationStatus: 422 | description: Status of the installation 423 | type: string 424 | enum: 425 | - in_progress 426 | - success 427 | - error 428 | example: in_progress 429 | 430 | installationMessage: 431 | description: Optional response message 432 | type: string 433 | example: message describing installation progress 434 | minLength: 1 435 | maxLength: 256 436 | 437 | installationBuildStamp: 438 | description: a build stamp of the software to be installed 439 | type: string 440 | 441 | # errors 442 | error: 443 | type: object 444 | required: 445 | - error 446 | - topic 447 | - payload 448 | - messageId 449 | properties: 450 | error: 451 | $ref: '#/components/schemas/errorContent' 452 | topic: 453 | $ref: '#/components/schemas/errorTopic' 454 | payload: 455 | $ref: '#/components/schemas/errorPayload' 456 | messageId: 457 | $ref: '#/components/schemas/errorMessageId' 458 | 459 | errorContent: 460 | description: Human-readable error message 461 | type: string 462 | example: command field 'id' is NOT an UUID 463 | 464 | errorTopic: 465 | description: Topic to which the malformed message was sent 466 | type: string 467 | example: devices/763c073a-e0ff-41a9-bd51-3386975ea4e3/commands 468 | 469 | errorPayload: 470 | description: Payload of the message that caused the error (stringified) 471 | type: string 472 | example: '{''id'':''not UUID'',''status'':''in_progress''}' 473 | 474 | errorMessageId: 475 | description: Id of the message (for tracking purposes) 476 | type: integer 477 | example: 31248 478 | 479 | log: 480 | type: object 481 | required: 482 | - level 483 | - message 484 | properties: 485 | level: 486 | $ref: '#/components/schemas/logLevel' 487 | message: 488 | $ref: '#/components/schemas/logMessage' 489 | timestamp: 490 | $ref: '#/components/schemas/datetime' 491 | 492 | logLevel: 493 | description: | 494 | Level of log severity (0 - 5) 495 | 5 = trace 496 | 4 = debug 497 | 3 = minor 498 | 2 = major 499 | 1 = error 500 | 0 = critical 501 | type: integer 502 | minimum: 0 503 | maximum: 5 504 | example: 2 505 | 506 | logMessage: 507 | description: Log message 508 | type: string 509 | example: Device has been restarted 510 | 511 | anomalyScore: 512 | type: object 513 | required: 514 | - score 515 | - deviceFeatureVectorTime 516 | - deviceGroupId 517 | - phaseName 518 | - attributeImportance 519 | - anomalyRegions 520 | additionalProperties: false 521 | properties: 522 | score: 523 | description: The raw anomaly score of the edge event. 524 | The higher the score, the more anomalous the event. 525 | The score can, but does not have to be normalized to the range [0,1]. 526 | If the anomaly score is not normalized to [0,1], we can normalize the score in the cloud using a `ScoreNormalizer` in the `analytics-event-service`. 527 | type: number 528 | totalNumberOfContributors: 529 | description: The total number of contributors (ie features) that were considered by the anomaly model, 530 | not only the N most important ones that are directly reported. 531 | type: number 532 | minimum: 0 533 | attributeImportance: 534 | type: object 535 | description: A list of features and their respective importances. 536 | This is a measure of the relative "importance" of each of the features used by the model. 537 | Each feature is assigned a percentage weighting of how much it contributed to the anomaly score. 538 | In the above example, the model has thus determined that one feature is 90% responsible for the current anomaly, 539 | and another feature for the remaining 10%. All attributeImportances sum up to 1.0. 540 | The list of attributeImportances is truncated to the N most important features, where N is configurable. 541 | required: 542 | - rawFeatures 543 | properties: 544 | rawFeatures: 545 | type: object 546 | description: A mapping between features' names and their respective importances, grouped by respective device ids. 547 | engineeredFeatures: 548 | type: object 549 | description: A mapping between features' names and their respective importances. 550 | anomalyRegions: 551 | description: Represents the models interpretation of the most likely range of values it expects to find for that particular feature. 552 | Provided for each of the N most important features. 553 | The `min` and `max` determines the lower and upper bounds of the encoded anomaly region, 554 | and the `values` list represents equally spaced buckets over that range. 555 | The values themselves represent the relative expectation of finding the feature within this region. 556 | The lower the value, the higher is the probability to find the value in that bucket. 557 | In other words, the values array encodes an inverse probability distribution. 558 | Observing a feature value outside the range [`min`, `max`] always corresponds to an anomalous feature. 559 | The `featureValue` encodes the actual value of the feature when the event was created. 560 | type: object 561 | required: 562 | - rawFeatures 563 | properties: 564 | rawFeatures: 565 | type: object 566 | description: A mapping between features' names and their respective anomaly regions, grouped by respective device ids. 567 | engineeredFeatures: 568 | type: object 569 | description: A mapping between features' names and their respective anomaly regions. 570 | deviceFeatureVectorTime: 571 | description: The timestamp of the event, ie when the event was created. 572 | $ref: '#/components/schemas/datetime' 573 | deviceGroupId: 574 | description: The cloud2.0 deviceGroupId to which this `anomaly event` belongs. 575 | $ref: '#/components/schemas/deviceGroupId' 576 | phaseName: 577 | description: Most systems operate in multiple states or phases, the most simple example being on and off. Take for example a coffee machine, this can have multiple different phases such as grinding beans, boiling water, extracting coffee, frothing milk etc.. For the purposes of analytics and machine learning, attempting to build a model to detect anomalies or specific behaviours across all of those drastically different phases is not a simple task. A more reliable approach would be to create one more specifically taylored model to detect problems in each of the phases. The `phaseName` is a string that is used to differentiate those phases. 578 | type: string 579 | minLength: 1 580 | maxLength: 64 581 | thresholdChange: 582 | description: boolean flag to indicate if this event is used for updating feature threshold and should not be processed by RCA or health state services. 583 | type: boolean 584 | 585 | MQTTQoSHeader: 586 | type: number 587 | format: int32 588 | description: Quality of Service (0 - 1) 589 | enum: 590 | - 1 591 | - 0 592 | 593 | securitySchemes: 594 | userPass: 595 | type: userPassword 596 | description: | 597 | Username and password obtained from the **Cloud** [MQTT endpoints](http://api-documentation.relayr.io/#tag/MQTT-Credentials-Device) 598 | X509Certificate: 599 | type: X509 600 | description: | 601 | X.509 Certificate 602 | 603 | messages: 604 | 605 | locations: 606 | messageId: devices/locations 607 | summary: Location of the device 608 | description: Device location is received from the device through MQTT and published to Kafka. Location service receives the Kafka message and updates the device location. 609 | tags: 610 | - name: location 611 | headers: 612 | type: object 613 | properties: 614 | qos: 615 | $ref: '#/components/schemas/MQTTQoSHeader' 616 | payload: 617 | type: array 618 | items: 619 | type: object 620 | properties: 621 | latitude: 622 | description: number of degrees north 623 | type: number 624 | minimum: -90 625 | maximum: 90 626 | longitude: 627 | description: number of degrees east 628 | type: number 629 | minimum: -180 630 | maximum: 180 631 | address: 632 | description: address of device location 633 | type: string 634 | maxLength: 200 635 | zipCode: 636 | description: zipCode of device location 637 | type: string 638 | city: 639 | description: city of device location 640 | type: string 641 | country: 642 | description: Country code from ISO 3166-1 alpha-2 standard 643 | type: string 644 | example: UA 645 | timestamp: 646 | $ref: '#/components/schemas/datetime' 647 | required: 648 | - longitude 649 | - latitude 650 | additionalProperties: false 651 | 652 | measurements: 653 | messageId: devices/measurements 654 | summary: Publish measurement(s) 655 | description: | 656 | Measurements are sent in an array which contains one or more measurements 657 | for a single device. Measurements **must** be defined in the [device model version](http://api-documentation.relayr.io/#tag/Device-Models/paths/~1device-models~1{modelId}~1versions/post). 658 | tags: 659 | - name: measurements 660 | payload: 661 | type: array 662 | items: 663 | $ref: '#/components/schemas/measurement' 664 | headers: 665 | type: object 666 | properties: 667 | qos: 668 | $ref: '#/components/schemas/MQTTQoSHeader' 669 | 670 | alerts: 671 | messageId: devices/alerts 672 | summary: Publish alert(s) 673 | tags: 674 | - name: alerts 675 | payload: 676 | type: array 677 | items: 678 | $ref: '#/components/schemas/alert' 679 | headers: 680 | type: object 681 | properties: 682 | qos: 683 | $ref: '#/components/schemas/MQTTQoSHeader' 684 | 685 | commands: 686 | messageId: devices/commands 687 | description: | 688 | Commands can be sent and executed on devices through the **Cloud** command import [endpoint](http://api-documentation.relayr.io/#tag/Commands-Import-HTTP) 689 | and command execution [endpoint](http://api-documentation.relayr.io/#tag/Commands-Execution-HTTP). 690 | summary: Subscribe to device commands 691 | tags: 692 | - name: commands 693 | payload: 694 | $ref: '#/components/schemas/command' 695 | headers: 696 | type: object 697 | properties: 698 | qos: 699 | $ref: '#/components/schemas/MQTTQoSHeader' 700 | 701 | command_responses: 702 | messageId: devices/command_responses 703 | summary: Publish command response(s) 704 | tags: 705 | - name: commands 706 | payload: 707 | type: array 708 | items: 709 | $ref: '#/components/schemas/commandResponse' 710 | headers: 711 | type: object 712 | properties: 713 | qos: 714 | $ref: '#/components/schemas/MQTTQoSHeader' 715 | 716 | configs: 717 | messageId: devices/configs 718 | description: | 719 | Configurations can be sent to devices through the **Cloud** configuration [endpoints](http://services-docs.relayr.io/ext-api/#tag/Device-configurations/paths/~1devices~1{deviceId}~1configurations/post). 720 | summary: Subscribe to device configurations 721 | tags: 722 | - name: configurations 723 | payload: 724 | $ref: '#/components/schemas/configuration' 725 | headers: 726 | type: object 727 | properties: 728 | qos: 729 | $ref: '#/components/schemas/MQTTQoSHeader' 730 | 731 | config_responses: 732 | messageId: devices/config_responses 733 | summary: Publish configuration response(s) 734 | tags: 735 | - name: configurations 736 | payload: 737 | type: array 738 | items: 739 | $ref: '#/components/schemas/configurationResponse' 740 | headers: 741 | type: object 742 | properties: 743 | qos: 744 | $ref: '#/components/schemas/MQTTQoSHeader' 745 | 746 | config_requests: 747 | messageId: devices/config_requests 748 | summary: Publish configuration request 749 | tags: 750 | - name: configurations 751 | payload: 752 | type: array 753 | items: 754 | $ref: '#/components/schemas/configurationRequest' 755 | headers: 756 | type: object 757 | properties: 758 | qos: 759 | $ref: '#/components/schemas/MQTTQoSHeader' 760 | 761 | installations: 762 | messageId: devices/installations 763 | description: | 764 | Installations can be sent to devices through the **Cloud** software installation [endpoints](http://api-documentation.relayr.io/#tag/Software-Installation-Device). 765 | summary: Subscribe to device installations 766 | tags: 767 | - name: installations 768 | payload: 769 | $ref: '#/components/schemas/installation' 770 | headers: 771 | type: object 772 | properties: 773 | qos: 774 | $ref: '#/components/schemas/MQTTQoSHeader' 775 | 776 | installation_responses: 777 | messageId: devices/installation_responses 778 | summary: Publish configuration response(s) 779 | tags: 780 | - name: installations 781 | payload: 782 | type: array 783 | items: 784 | $ref: '#/components/schemas/installationResponse' 785 | headers: 786 | type: object 787 | properties: 788 | qos: 789 | $ref: '#/components/schemas/MQTTQoSHeader' 790 | 791 | errors: 792 | messageId: devices/errors 793 | description: | 794 | When malformed message is published it will be filtered out and rejected. \ 795 | Reason for rejection can be obtained from the **/errors** topic. 796 | summary: Subscribe to device errors 797 | tags: 798 | - name: errors 799 | payload: 800 | $ref: '#/components/schemas/error' 801 | headers: 802 | type: object 803 | properties: 804 | qos: 805 | $ref: '#/components/schemas/MQTTQoSHeader' 806 | 807 | logs: 808 | messageId: devices/logs 809 | summary: Publish device log(s) 810 | tags: 811 | - name: logs 812 | payload: 813 | type: array 814 | items: 815 | $ref: '#/components/schemas/log' 816 | headers: 817 | type: object 818 | properties: 819 | qos: 820 | $ref: '#/components/schemas/MQTTQoSHeader' 821 | 822 | analytics_events: 823 | messageId: device-groups/analytics_events 824 | summary: Publish analytics events 825 | tags: 826 | - name: analytics events 827 | payload: 828 | type: array 829 | items: 830 | $ref: '#/components/schemas/anomalyScore' 831 | headers: 832 | type: object 833 | properties: 834 | qos: 835 | $ref: '#/components/schemas/MQTTQoSHeader' 836 | -------------------------------------------------------------------------------- /test/schemas/v2.4.0/slack.yml: -------------------------------------------------------------------------------- 1 | asyncapi: '2.4.0' 2 | id: 'urn:com:slack:rtm:api' 3 | info: 4 | title: Slack Real Time Messaging API 5 | version: '1.0.0' 6 | 7 | servers: 8 | production: 9 | url: https://slack.com/api/rtm.connect 10 | protocol: https 11 | protocolVersion: '1.1' 12 | security: 13 | - token: [] 14 | 15 | channels: 16 | /: 17 | publish: 18 | message: 19 | $ref: '#/components/messages/outgoingMessage' 20 | subscribe: 21 | message: 22 | oneOf: 23 | - $ref: '#/components/messages/hello' 24 | - $ref: '#/components/messages/connectionError' 25 | - $ref: '#/components/messages/accountsChanged' 26 | - $ref: '#/components/messages/botAdded' 27 | - $ref: '#/components/messages/botChanged' 28 | - $ref: '#/components/messages/channelArchive' 29 | - $ref: '#/components/messages/channelCreated' 30 | - $ref: '#/components/messages/channelDeleted' 31 | - $ref: '#/components/messages/channelHistoryChanged' 32 | - $ref: '#/components/messages/channelJoined' 33 | - $ref: '#/components/messages/channelLeft' 34 | - $ref: '#/components/messages/channelMarked' 35 | - $ref: '#/components/messages/channelRename' 36 | - $ref: '#/components/messages/channelUnarchive' 37 | - $ref: '#/components/messages/commandsChanged' 38 | - $ref: '#/components/messages/dndUpdated' 39 | - $ref: '#/components/messages/dndUpdatedUser' 40 | - $ref: '#/components/messages/emailDomainChanged' 41 | - $ref: '#/components/messages/emojiRemoved' 42 | - $ref: '#/components/messages/emojiAdded' 43 | - $ref: '#/components/messages/fileChange' 44 | - $ref: '#/components/messages/fileCommentAdded' 45 | - $ref: '#/components/messages/fileCommentDeleted' 46 | - $ref: '#/components/messages/fileCommentEdited' 47 | - $ref: '#/components/messages/fileCreated' 48 | - $ref: '#/components/messages/fileDeleted' 49 | - $ref: '#/components/messages/filePublic' 50 | - $ref: '#/components/messages/fileShared' 51 | - $ref: '#/components/messages/fileUnshared' 52 | - $ref: '#/components/messages/goodbye' 53 | - $ref: '#/components/messages/groupArchive' 54 | - $ref: '#/components/messages/groupClose' 55 | - $ref: '#/components/messages/groupHistoryChanged' 56 | - $ref: '#/components/messages/groupJoined' 57 | - $ref: '#/components/messages/groupLeft' 58 | - $ref: '#/components/messages/groupMarked' 59 | - $ref: '#/components/messages/groupOpen' 60 | - $ref: '#/components/messages/groupRename' 61 | - $ref: '#/components/messages/groupUnarchive' 62 | - $ref: '#/components/messages/imClose' 63 | - $ref: '#/components/messages/imCreated' 64 | - $ref: '#/components/messages/imMarked' 65 | - $ref: '#/components/messages/imOpen' 66 | - $ref: '#/components/messages/manualPresenceChange' 67 | - $ref: '#/components/messages/memberJoinedChannel' 68 | - $ref: '#/components/messages/message' 69 | 70 | components: 71 | securitySchemes: 72 | token: 73 | type: httpApiKey 74 | name: token 75 | in: query 76 | 77 | schemas: 78 | attachment: 79 | type: object 80 | properties: 81 | fallback: 82 | type: string 83 | color: 84 | type: string 85 | pretext: 86 | type: string 87 | author_name: 88 | type: string 89 | author_link: 90 | type: string 91 | format: uri 92 | author_icon: 93 | type: string 94 | format: uri 95 | title: 96 | type: string 97 | title_link: 98 | type: string 99 | format: uri 100 | text: 101 | type: string 102 | fields: 103 | type: array 104 | items: 105 | type: object 106 | properties: 107 | title: 108 | type: string 109 | value: 110 | type: string 111 | short: 112 | type: boolean 113 | image_url: 114 | type: string 115 | format: uri 116 | thumb_url: 117 | type: string 118 | format: uri 119 | footer: 120 | type: string 121 | footer_icon: 122 | type: string 123 | format: uri 124 | ts: 125 | type: number 126 | 127 | messages: 128 | hello: 129 | name: hello 130 | summary: 'First event received upon connection.' 131 | payload: 132 | type: object 133 | properties: 134 | type: 135 | type: string 136 | enum: 137 | - hello 138 | connectionError: 139 | name: connectionError 140 | summary: 'Event received when a connection error happens.' 141 | payload: 142 | type: object 143 | properties: 144 | type: 145 | type: string 146 | enum: 147 | - error 148 | error: 149 | type: object 150 | properties: 151 | code: 152 | type: number 153 | msg: 154 | type: string 155 | accountsChanged: 156 | name: accountsChanged 157 | summary: 'The list of accounts a user is signed into has changed.' 158 | payload: 159 | type: object 160 | properties: 161 | type: 162 | type: string 163 | enum: 164 | - accounts_changed 165 | botAdded: 166 | name: botAdded 167 | summary: 'A bot user was added.' 168 | payload: 169 | type: object 170 | properties: 171 | type: 172 | type: string 173 | enum: 174 | - bot_added 175 | bot: 176 | type: object 177 | properties: 178 | id: 179 | type: string 180 | app_id: 181 | type: string 182 | name: 183 | type: string 184 | icons: 185 | type: object 186 | additionalProperties: 187 | type: string 188 | botChanged: 189 | messageId: botChanged 190 | summary: 'A bot user was changed.' 191 | payload: 192 | type: object 193 | properties: 194 | type: 195 | type: string 196 | enum: 197 | - bot_added 198 | bot: 199 | type: object 200 | properties: 201 | id: 202 | type: string 203 | app_id: 204 | type: string 205 | name: 206 | type: string 207 | icons: 208 | type: object 209 | additionalProperties: 210 | type: string 211 | channelArchive: 212 | name: channelArchive 213 | summary: 'A channel was archived.' 214 | payload: 215 | type: object 216 | properties: 217 | type: 218 | type: string 219 | enum: 220 | - channel_archive 221 | channel: 222 | type: string 223 | user: 224 | type: string 225 | channelCreated: 226 | name: channelArchive 227 | summary: 'A channel was created.' 228 | payload: 229 | type: object 230 | properties: 231 | type: 232 | type: string 233 | enum: 234 | - channel_created 235 | channel: 236 | type: object 237 | properties: 238 | id: 239 | type: string 240 | name: 241 | type: string 242 | created: 243 | type: number 244 | creator: 245 | type: string 246 | channelDeleted: 247 | messageId: channelDeleted 248 | summary: 'A channel was deleted.' 249 | payload: 250 | type: object 251 | properties: 252 | type: 253 | type: string 254 | enum: 255 | - channel_deleted 256 | channel: 257 | type: string 258 | channelHistoryChanged: 259 | name: channelHistoryChanged 260 | summary: 'Bulk updates were made to a channel''s history.' 261 | payload: 262 | type: object 263 | properties: 264 | type: 265 | type: string 266 | enum: 267 | - channel_history_changed 268 | latest: 269 | type: string 270 | ts: 271 | type: string 272 | event_ts: 273 | type: string 274 | channelJoined: 275 | name: channelJoined 276 | summary: 'You joined a channel.' 277 | payload: 278 | type: object 279 | properties: 280 | type: 281 | type: string 282 | enum: 283 | - channel_joined 284 | channel: 285 | type: object 286 | properties: 287 | id: 288 | type: string 289 | name: 290 | type: string 291 | created: 292 | type: number 293 | creator: 294 | type: string 295 | channelLeft: 296 | name: channelLeft 297 | summary: 'You left a channel.' 298 | payload: 299 | type: object 300 | properties: 301 | type: 302 | type: string 303 | enum: 304 | - channel_left 305 | channel: 306 | type: string 307 | channelMarked: 308 | name: channelMarked 309 | summary: 'Your channel read marker was updated.' 310 | payload: 311 | type: object 312 | properties: 313 | type: 314 | type: string 315 | enum: 316 | - channel_marked 317 | channel: 318 | type: string 319 | ts: 320 | type: string 321 | channelRename: 322 | name: channelRename 323 | summary: 'A channel was renamed.' 324 | payload: 325 | type: object 326 | properties: 327 | type: 328 | type: string 329 | enum: 330 | - channel_rename 331 | channel: 332 | type: object 333 | properties: 334 | id: 335 | type: string 336 | name: 337 | type: string 338 | created: 339 | type: number 340 | channelUnarchive: 341 | name: channelUnarchive 342 | summary: 'A channel was unarchived.' 343 | payload: 344 | type: object 345 | properties: 346 | type: 347 | type: string 348 | enum: 349 | - channel_unarchive 350 | channel: 351 | type: string 352 | user: 353 | type: string 354 | commandsChanged: 355 | name: commandsChanged 356 | summary: 'A slash command has been added or changed.' 357 | payload: 358 | type: object 359 | properties: 360 | type: 361 | type: string 362 | enum: 363 | - commands_changed 364 | event_ts: 365 | type: string 366 | dndUpdated: 367 | name: dndUpdated 368 | summary: 'Do not Disturb settings changed for the current user.' 369 | payload: 370 | type: object 371 | properties: 372 | type: 373 | type: string 374 | enum: 375 | - dnd_updated 376 | user: 377 | type: string 378 | dnd_status: 379 | type: object 380 | properties: 381 | dnd_enabled: 382 | type: boolean 383 | next_dnd_start_ts: 384 | type: number 385 | next_dnd_end_ts: 386 | type: number 387 | snooze_enabled: 388 | type: boolean 389 | snooze_endtime: 390 | type: number 391 | dndUpdatedUser: 392 | name: dndUpdatedUser 393 | summary: 'Do not Disturb settings changed for a member.' 394 | payload: 395 | type: object 396 | properties: 397 | type: 398 | type: string 399 | enum: 400 | - dnd_updated_user 401 | user: 402 | type: string 403 | dnd_status: 404 | type: object 405 | properties: 406 | dnd_enabled: 407 | type: boolean 408 | next_dnd_start_ts: 409 | type: number 410 | next_dnd_end_ts: 411 | type: number 412 | emailDomainChanged: 413 | name: emailDomainChanged 414 | summary: 'The workspace email domain has changed.' 415 | payload: 416 | type: object 417 | properties: 418 | type: 419 | type: string 420 | enum: 421 | - email_domain_changed 422 | email_domain: 423 | type: string 424 | event_ts: 425 | type: string 426 | emojiRemoved: 427 | name: emojiRemoved 428 | summary: 'A custom emoji has been removed.' 429 | payload: 430 | type: object 431 | properties: 432 | type: 433 | type: string 434 | enum: 435 | - emoji_changed 436 | subtype: 437 | type: string 438 | enum: 439 | - remove 440 | names: 441 | type: array 442 | items: 443 | type: string 444 | event_ts: 445 | type: string 446 | emojiAdded: 447 | name: emojiAdded 448 | summary: 'A custom emoji has been added.' 449 | payload: 450 | type: object 451 | properties: 452 | type: 453 | type: string 454 | enum: 455 | - emoji_changed 456 | subtype: 457 | type: string 458 | enum: 459 | - add 460 | name: 461 | type: string 462 | value: 463 | type: string 464 | format: uri 465 | event_ts: 466 | type: string 467 | fileChange: 468 | name: fileChange 469 | summary: 'A file was changed.' 470 | payload: 471 | type: object 472 | properties: 473 | type: 474 | type: string 475 | enum: 476 | - file_change 477 | file_id: 478 | type: string 479 | file: 480 | type: object 481 | properties: 482 | id: 483 | type: string 484 | fileCommentAdded: 485 | name: fileCommentAdded 486 | summary: 'A file comment was added.' 487 | payload: 488 | type: object 489 | properties: 490 | type: 491 | type: string 492 | enum: 493 | - file_comment_added 494 | comment: {} 495 | file_id: 496 | type: string 497 | file: 498 | type: object 499 | properties: 500 | id: 501 | type: string 502 | fileCommentDeleted: 503 | name: fileCommentDeleted 504 | summary: 'A file comment was deleted.' 505 | payload: 506 | type: object 507 | properties: 508 | type: 509 | type: string 510 | enum: 511 | - file_comment_deleted 512 | comment: 513 | type: string 514 | file_id: 515 | type: string 516 | file: 517 | type: object 518 | properties: 519 | id: 520 | type: string 521 | fileCommentEdited: 522 | name: fileCommentEdited 523 | summary: 'A file comment was edited.' 524 | payload: 525 | type: object 526 | properties: 527 | type: 528 | type: string 529 | enum: 530 | - file_comment_edited 531 | comment: {} 532 | file_id: 533 | type: string 534 | file: 535 | type: object 536 | properties: 537 | id: 538 | type: string 539 | fileCreated: 540 | name: fileCreated 541 | summary: 'A file was created.' 542 | payload: 543 | type: object 544 | properties: 545 | type: 546 | type: string 547 | enum: 548 | - file_created 549 | file_id: 550 | type: string 551 | file: 552 | type: object 553 | properties: 554 | id: 555 | type: string 556 | fileDeleted: 557 | name: fileDeleted 558 | summary: 'A file was deleted.' 559 | payload: 560 | type: object 561 | properties: 562 | type: 563 | type: string 564 | enum: 565 | - file_deleted 566 | file_id: 567 | type: string 568 | event_ts: 569 | type: string 570 | filePublic: 571 | name: filePublic 572 | summary: 'A file was made public.' 573 | payload: 574 | type: object 575 | properties: 576 | type: 577 | type: string 578 | enum: 579 | - file_public 580 | file_id: 581 | type: string 582 | file: 583 | type: object 584 | properties: 585 | id: 586 | type: string 587 | fileShared: 588 | name: fileShared 589 | summary: 'A file was shared.' 590 | payload: 591 | type: object 592 | properties: 593 | type: 594 | type: string 595 | enum: 596 | - file_shared 597 | file_id: 598 | type: string 599 | file: 600 | type: object 601 | properties: 602 | id: 603 | type: string 604 | fileUnshared: 605 | name: fileUnshared 606 | summary: 'A file was unshared.' 607 | payload: 608 | type: object 609 | properties: 610 | type: 611 | type: string 612 | enum: 613 | - file_unshared 614 | file_id: 615 | type: string 616 | file: 617 | type: object 618 | properties: 619 | id: 620 | type: string 621 | goodbye: 622 | name: goodbye 623 | summary: 'The server intends to close the connection soon.' 624 | payload: 625 | type: object 626 | properties: 627 | type: 628 | type: string 629 | enum: 630 | - goodbye 631 | groupArchive: 632 | name: groupArchive 633 | summary: 'A private channel was archived.' 634 | payload: 635 | type: object 636 | properties: 637 | type: 638 | type: string 639 | enum: 640 | - group_archive 641 | channel: 642 | type: string 643 | groupClose: 644 | name: groupClose 645 | summary: 'You closed a private channel.' 646 | payload: 647 | type: object 648 | properties: 649 | type: 650 | type: string 651 | enum: 652 | - group_close 653 | user: 654 | type: string 655 | channel: 656 | type: string 657 | groupHistoryChanged: 658 | name: groupHistoryChanged 659 | summary: 'Bulk updates were made to a private channel''s history.' 660 | payload: 661 | type: object 662 | properties: 663 | type: 664 | type: string 665 | enum: 666 | - group_history_changed 667 | latest: 668 | type: string 669 | ts: 670 | type: string 671 | event_ts: 672 | type: string 673 | groupJoined: 674 | name: groupJoined 675 | summary: 'You joined a private channel.' 676 | payload: 677 | type: object 678 | properties: 679 | type: 680 | type: string 681 | enum: 682 | - group_joined 683 | channel: 684 | type: object 685 | properties: 686 | id: 687 | type: string 688 | name: 689 | type: string 690 | created: 691 | type: number 692 | creator: 693 | type: string 694 | groupLeft: 695 | name: groupLeft 696 | summary: 'You left a private channel.' 697 | payload: 698 | type: object 699 | properties: 700 | type: 701 | type: string 702 | enum: 703 | - group_left 704 | channel: 705 | type: string 706 | groupMarked: 707 | name: groupMarked 708 | summary: 'A private channel read marker was updated.' 709 | payload: 710 | type: object 711 | properties: 712 | type: 713 | type: string 714 | enum: 715 | - group_marked 716 | channel: 717 | type: string 718 | ts: 719 | type: string 720 | groupOpen: 721 | name: groupOpen 722 | summary: 'You opened a private channel.' 723 | payload: 724 | type: object 725 | properties: 726 | type: 727 | type: string 728 | enum: 729 | - group_open 730 | user: 731 | type: string 732 | channel: 733 | type: string 734 | groupRename: 735 | name: groupRename 736 | summary: 'A private channel was renamed.' 737 | payload: 738 | type: object 739 | properties: 740 | type: 741 | type: string 742 | enum: 743 | - group_rename 744 | channel: 745 | type: object 746 | properties: 747 | id: 748 | type: string 749 | name: 750 | type: string 751 | created: 752 | type: number 753 | groupUnarchive: 754 | name: groupUnarchive 755 | summary: 'A private channel was unarchived.' 756 | payload: 757 | type: object 758 | properties: 759 | type: 760 | type: string 761 | enum: 762 | - group_unarchive 763 | channel: 764 | type: string 765 | user: 766 | type: string 767 | imClose: 768 | name: imClose 769 | summary: 'You closed a DM.' 770 | payload: 771 | type: object 772 | properties: 773 | type: 774 | type: string 775 | enum: 776 | - im_close 777 | channel: 778 | type: string 779 | user: 780 | type: string 781 | imCreated: 782 | name: imCreated 783 | summary: 'A DM was created.' 784 | payload: 785 | type: object 786 | properties: 787 | type: 788 | type: string 789 | enum: 790 | - im_created 791 | channel: 792 | type: object 793 | properties: 794 | id: 795 | type: string 796 | name: 797 | type: string 798 | created: 799 | type: number 800 | creator: 801 | type: string 802 | user: 803 | type: string 804 | imMarked: 805 | name: imMarked 806 | summary: 'A direct message read marker was updated.' 807 | payload: 808 | type: object 809 | properties: 810 | type: 811 | type: string 812 | enum: 813 | - im_marked 814 | channel: 815 | type: string 816 | ts: 817 | type: string 818 | imOpen: 819 | name: imOpen 820 | summary: 'You opened a DM.' 821 | payload: 822 | type: object 823 | properties: 824 | type: 825 | type: string 826 | enum: 827 | - im_open 828 | channel: 829 | type: string 830 | user: 831 | type: string 832 | manualPresenceChange: 833 | name: manualPresenceChange 834 | summary: 'You manually updated your presence.' 835 | payload: 836 | type: object 837 | properties: 838 | type: 839 | type: string 840 | enum: 841 | - manual_presence_change 842 | presence: 843 | type: string 844 | memberJoinedChannel: 845 | name: memberJoinedChannel 846 | summary: 'A user joined a public or private channel.' 847 | payload: 848 | type: object 849 | properties: 850 | type: 851 | type: string 852 | enum: 853 | - member_joined_channel 854 | user: 855 | type: string 856 | channel: 857 | type: string 858 | channel_type: 859 | type: string 860 | enum: 861 | - C 862 | - G 863 | team: 864 | type: string 865 | inviter: 866 | type: string 867 | memberLeftChannel: 868 | name: memberLeftChannel 869 | summary: 'A user left a public or private channel.' 870 | payload: 871 | type: object 872 | properties: 873 | type: 874 | type: string 875 | enum: 876 | - member_left_channel 877 | user: 878 | type: string 879 | channel: 880 | type: string 881 | channel_type: 882 | type: string 883 | enum: 884 | - C 885 | - G 886 | team: 887 | type: string 888 | message: 889 | messageId: message 890 | summary: 'A message was sent to a channel.' 891 | payload: 892 | type: object 893 | properties: 894 | type: 895 | type: string 896 | enum: 897 | - message 898 | user: 899 | type: string 900 | channel: 901 | type: string 902 | text: 903 | type: string 904 | ts: 905 | type: string 906 | attachments: 907 | type: array 908 | items: 909 | $ref: '#/components/schemas/attachment' 910 | edited: 911 | type: object 912 | properties: 913 | user: 914 | type: string 915 | ts: 916 | type: string 917 | outgoingMessage: 918 | name: outgoingMessage 919 | summary: 'A message was sent to a channel.' 920 | payload: 921 | type: object 922 | properties: 923 | id: 924 | type: number 925 | type: 926 | type: string 927 | enum: 928 | - message 929 | channel: 930 | type: string 931 | text: 932 | type: string 933 | -------------------------------------------------------------------------------- /test/schemas/v2.4.0/userMessages.yaml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.4.0 2 | 3 | info: 4 | title: User Events 5 | version: 1.0.0 6 | 7 | channels: 8 | user-events: 9 | description: user related events 10 | publish: 11 | message: 12 | name: UserDeletedMessage 13 | messageId: UserDeleted 14 | payload: 15 | type: object 16 | properties: 17 | userEmail: 18 | type: string 19 | userId: 20 | type: string 21 | -------------------------------------------------------------------------------- /test/schemas/v3.0.0/pingPong.yml: -------------------------------------------------------------------------------- 1 | asyncapi: 3.0.0 2 | info: 3 | title: Ping/pong example with reply specified as dynamic information provided in the runtime 4 | version: 1.0.0 5 | description: Example document for an application that processes ping requests and replies to the address dynamically specified by the requestor in the message header 6 | channels: 7 | ping: 8 | address: /ping 9 | messages: 10 | ping: 11 | $ref: '#/components/messages/ping' 12 | pong: 13 | address: "dynamic" 14 | messages: 15 | pong: 16 | $ref: '#/components/messages/pong' 17 | operations: 18 | pingRequest: 19 | action: receive 20 | channel: 21 | $ref: '#/channels/ping' 22 | messages: 23 | - $ref: '#/channels/ping/messages/ping' 24 | reply: 25 | address: 26 | description: Reply is sent to topic specified in 'replyTo' property in the message header 27 | location: "$message.header#/replyTo" 28 | channel: 29 | $ref: '#/channels/pong' 30 | messages: 31 | - $ref: '#/channels/pong/messages/pong' 32 | components: 33 | messages: 34 | ping: 35 | name: Ping 36 | headers: 37 | type: object 38 | properties: 39 | replyTo: 40 | type: string 41 | description: Provide path to which reply must be provided 42 | requestId: 43 | type: string 44 | format: uuid 45 | description: Provide request id that you will use to identify the reply match 46 | payload: 47 | type: object 48 | properties: 49 | event: 50 | type: string 51 | const: ping 52 | correlationId: 53 | $ref: "#/components/correlationIds/pingCorrelationId" 54 | pong: 55 | name: Pong 56 | headers: 57 | type: object 58 | properties: 59 | requestId: 60 | type: string 61 | format: uuid 62 | description: Reply message must contain id of the request message 63 | payload: 64 | type: object 65 | properties: 66 | event: 67 | type: string 68 | const: pong 69 | correlationId: 70 | $ref: "#/components/correlationIds/pingCorrelationId" 71 | correlationIds: 72 | pingCorrelationId: 73 | location: '$message.header#/requestId' -------------------------------------------------------------------------------- /test/schemas/v3.0.0/streetLights.yml: -------------------------------------------------------------------------------- 1 | asyncapi: 3.0.0 2 | info: 3 | title: Streetlights Kafka API 4 | version: 1.0.0 5 | description: |- 6 | The Smartylighting Streetlights API allows you to remotely manage the city 7 | lights. 8 | ### Check out its awesome features: 9 | 10 | * Turn a specific streetlight on/off 🌃 11 | * Dim a specific streetlight 😎 12 | * Receive real-time information about environmental lighting conditions 📈 13 | license: 14 | name: Apache 2.0 15 | url: https://www.apache.org/licenses/LICENSE-2.0 16 | defaultContentType: application/json 17 | servers: 18 | scram-connections: 19 | host: test.mykafkacluster.org:18092 20 | protocol: kafka-secure 21 | description: Test broker secured with scramSha256 22 | security: 23 | - $ref: '#/components/securitySchemes/saslScram' 24 | tags: 25 | - name: env:test-scram 26 | description: >- 27 | This environment is meant for running internal tests through 28 | scramSha256 29 | - name: kind:remote 30 | description: This server is a remote server. Not exposed by the application 31 | - name: visibility:private 32 | description: This resource is private and only available to certain users 33 | mtls-connections: 34 | host: test.mykafkacluster.org:28092 35 | protocol: kafka-secure 36 | description: Test broker secured with X509 37 | security: 38 | - $ref: '#/components/securitySchemes/certs' 39 | tags: 40 | - name: env:test-mtls 41 | description: This environment is meant for running internal tests through mtls 42 | - name: kind:remote 43 | description: This server is a remote server. Not exposed by the application 44 | - name: visibility:private 45 | description: This resource is private and only available to certain users 46 | channels: 47 | lightingMeasured: 48 | address: smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured 49 | messages: 50 | lightMeasured: 51 | $ref: '#/components/messages/lightMeasured' 52 | description: The topic on which measured values may be produced and consumed. 53 | parameters: 54 | streetlightId: 55 | $ref: '#/components/parameters/streetlightId' 56 | lightTurnOn: 57 | address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.on 58 | messages: 59 | turnOn: 60 | $ref: '#/components/messages/turnOnOff' 61 | turnOff: 62 | $ref: '#/components/messages/turnOnOff' 63 | parameters: 64 | streetlightId: 65 | $ref: '#/components/parameters/streetlightId' 66 | lightTurnOff: 67 | address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.off 68 | messages: 69 | turnOff: 70 | $ref: '#/components/messages/turnOnOff' 71 | parameters: 72 | streetlightId: 73 | $ref: '#/components/parameters/streetlightId' 74 | lightsDim: 75 | address: smartylighting.streetlights.1.0.action.{streetlightId}.dim 76 | messages: 77 | dimLight: 78 | $ref: '#/components/messages/dimLight' 79 | parameters: 80 | streetlightId: 81 | $ref: '#/components/parameters/streetlightId' 82 | operations: 83 | receiveLightMeasurement: 84 | action: receive 85 | channel: 86 | $ref: '#/channels/lightingMeasured' 87 | summary: >- 88 | Inform about environmental lighting conditions of a particular 89 | streetlight. 90 | traits: 91 | - $ref: '#/components/operationTraits/kafka' 92 | messages: 93 | - $ref: '#/channels/lightingMeasured/messages/lightMeasured' 94 | turnOn: 95 | action: send 96 | channel: 97 | $ref: '#/channels/lightTurnOn' 98 | traits: 99 | - $ref: '#/components/operationTraits/kafka' 100 | messages: 101 | - $ref: '#/channels/lightTurnOn/messages/turnOn' 102 | - $ref: '#/channels/lightTurnOn/messages/turnOff' 103 | turnOff: 104 | action: send 105 | channel: 106 | $ref: '#/channels/lightTurnOff' 107 | traits: 108 | - $ref: '#/components/operationTraits/kafka' 109 | messages: 110 | - $ref: '#/channels/lightTurnOff/messages/turnOff' 111 | dimLight: 112 | action: send 113 | channel: 114 | $ref: '#/channels/lightsDim' 115 | traits: 116 | - $ref: '#/components/operationTraits/kafka' 117 | messages: 118 | - $ref: '#/channels/lightsDim/messages/dimLight' 119 | components: 120 | messages: 121 | lightMeasured: 122 | name: lightMeasured 123 | title: Light measured 124 | summary: >- 125 | Inform about environmental lighting conditions of a particular 126 | streetlight. 127 | contentType: application/json 128 | traits: 129 | - $ref: '#/components/messageTraits/commonHeaders' 130 | payload: 131 | $ref: '#/components/schemas/lightMeasuredPayload' 132 | turnOnOff: 133 | name: turnOnOff 134 | title: Turn on/off 135 | summary: Command a particular streetlight to turn the lights on or off. 136 | traits: 137 | - $ref: '#/components/messageTraits/commonHeaders' 138 | payload: 139 | $ref: '#/components/schemas/turnOnOffPayload' 140 | dimLight: 141 | name: dimLight 142 | title: Dim light 143 | summary: Command a particular streetlight to dim the lights. 144 | traits: 145 | - $ref: '#/components/messageTraits/commonHeaders' 146 | payload: 147 | $ref: '#/components/schemas/dimLightPayload' 148 | schemas: 149 | lightMeasuredPayload: 150 | type: object 151 | required: 152 | - lumens 153 | properties: 154 | lumens: 155 | type: integer 156 | minimum: 0 157 | description: Light intensity measured in lumens. 158 | sentAt: 159 | $ref: '#/components/schemas/sentAt' 160 | turnOnOffPayload: 161 | type: object 162 | properties: 163 | command: 164 | type: string 165 | enum: 166 | - 'on' 167 | - 'off' 168 | description: Whether to turn on or off the light. 169 | sentAt: 170 | $ref: '#/components/schemas/sentAt' 171 | dimLightPayload: 172 | type: object 173 | properties: 174 | percentage: 175 | type: integer 176 | description: Percentage to which the light should be dimmed to. 177 | minimum: 0 178 | maximum: 100 179 | sentAt: 180 | $ref: '#/components/schemas/sentAt' 181 | sentAt: 182 | type: string 183 | format: date-time 184 | description: Date and time when the message was sent. 185 | securitySchemes: 186 | saslScram: 187 | type: scramSha256 188 | description: Provide your username and password for SASL/SCRAM authentication 189 | certs: 190 | type: X509 191 | description: Download the certificate files from service provider 192 | parameters: 193 | streetlightId: 194 | description: The ID of the streetlight. 195 | messageTraits: 196 | commonHeaders: 197 | headers: 198 | type: object 199 | properties: 200 | my-app-header: 201 | type: integer 202 | minimum: 0 203 | maximum: 100 204 | operationTraits: 205 | kafka: 206 | bindings: 207 | kafka: 208 | clientId: 209 | type: string 210 | enum: 211 | - my-app-id -------------------------------------------------------------------------------- /test/schemas/v3.0.0/streetLightsMini.yml: -------------------------------------------------------------------------------- 1 | asyncapi: 3.0.0 2 | info: 3 | title: Streetlights Kafka API 4 | version: 1.0.0 5 | channels: 6 | lightingMeasured: 7 | messages: 8 | lightMeasured: 9 | $ref: '#/components/messages/lightMeasured' 10 | operations: 11 | sendLightMeasurement: 12 | action: send 13 | channel: 14 | $ref: '#/channels/lightingMeasured' 15 | messages: 16 | - $ref: '#/channels/lightingMeasured/messages/lightMeasured' 17 | components: 18 | messages: 19 | lightMeasured: 20 | x-unique-id: lightMeasured 21 | payload: 22 | $ref: '#/components/schemas/lightMeasuredPayload' 23 | schemas: 24 | lightMeasuredPayload: 25 | type: object 26 | properties: 27 | lumens: 28 | type: integer 29 | minimum: 0 30 | description: Light intensity measured in lumens. -------------------------------------------------------------------------------- /test/schemas/v3.0.0/streetLightsMultiMsg.yml: -------------------------------------------------------------------------------- 1 | asyncapi: 3.0.0 2 | info: 3 | title: Streetlights Kafka API 4 | version: 1.0.0 5 | description: |- 6 | The Smartylighting Streetlights API allows you to remotely manage the city 7 | lights. 8 | ### Check out its awesome features: 9 | 10 | * Turn a specific streetlight on/off 🌃 11 | * Dim a specific streetlight 😎 12 | * Receive real-time information about environmental lighting conditions 📈 13 | license: 14 | name: Apache 2.0 15 | url: https://www.apache.org/licenses/LICENSE-2.0 16 | defaultContentType: application/json 17 | servers: 18 | scram-connections: 19 | host: test.mykafkacluster.org:18092 20 | protocol: kafka-secure 21 | description: Test broker secured with scramSha256 22 | security: 23 | - $ref: '#/components/securitySchemes/saslScram' 24 | tags: 25 | - name: env:test-scram 26 | description: >- 27 | This environment is meant for running internal tests through 28 | scramSha256 29 | - name: kind:remote 30 | description: This server is a remote server. Not exposed by the application 31 | - name: visibility:private 32 | description: This resource is private and only available to certain users 33 | mtls-connections: 34 | host: test.mykafkacluster.org:28092 35 | protocol: kafka-secure 36 | description: Test broker secured with X509 37 | security: 38 | - $ref: '#/components/securitySchemes/certs' 39 | tags: 40 | - name: env:test-mtls 41 | description: This environment is meant for running internal tests through mtls 42 | - name: kind:remote 43 | description: This server is a remote server. Not exposed by the application 44 | - name: visibility:private 45 | description: This resource is private and only available to certain users 46 | channels: 47 | lightingMeasured: 48 | address: smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured 49 | messages: 50 | lightMeasured: 51 | $ref: '#/components/messages/lightMeasured' 52 | description: The topic on which measured values may be produced and consumed. 53 | parameters: 54 | streetlightId: 55 | $ref: '#/components/parameters/streetlightId' 56 | lightTurnOn: 57 | address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.on 58 | messages: 59 | turnOn: 60 | $ref: '#/components/messages/turnOnOff' 61 | turnOff: 62 | $ref: '#/components/messages/turnOnOff' 63 | parameters: 64 | streetlightId: 65 | $ref: '#/components/parameters/streetlightId' 66 | lightTurnOff: 67 | address: smartylighting.streetlights.1.0.action.{streetlightId}.turn.off 68 | messages: 69 | turnOff: 70 | $ref: '#/components/messages/turnOnOff' 71 | turnOn: 72 | $ref: '#/components/messages/turnOnOff' 73 | parameters: 74 | streetlightId: 75 | $ref: '#/components/parameters/streetlightId' 76 | lightsDim: 77 | address: smartylighting.streetlights.1.0.action.{streetlightId}.dim 78 | messages: 79 | dimLight: 80 | $ref: '#/components/messages/dimLight' 81 | parameters: 82 | streetlightId: 83 | $ref: '#/components/parameters/streetlightId' 84 | operations: 85 | receiveLightMeasurement: 86 | action: receive 87 | channel: 88 | $ref: '#/channels/lightingMeasured' 89 | summary: >- 90 | Inform about environmental lighting conditions of a particular 91 | streetlight. 92 | traits: 93 | - $ref: '#/components/operationTraits/kafka' 94 | messages: 95 | - $ref: '#/channels/lightingMeasured/messages/lightMeasured' 96 | turnOn: 97 | action: send 98 | channel: 99 | $ref: '#/channels/lightTurnOff' 100 | traits: 101 | - $ref: '#/components/operationTraits/kafka' 102 | messages: 103 | - $ref: '#/channels/lightTurnOff/messages/turnOn' 104 | turnOff: 105 | action: send 106 | channel: 107 | $ref: '#/channels/lightTurnOff' 108 | traits: 109 | - $ref: '#/components/operationTraits/kafka' 110 | messages: 111 | - $ref: '#/channels/lightTurnOff/messages/turnOff' 112 | dimLight: 113 | action: send 114 | channel: 115 | $ref: '#/channels/lightsDim' 116 | traits: 117 | - $ref: '#/components/operationTraits/kafka' 118 | messages: 119 | - $ref: '#/channels/lightsDim/messages/dimLight' 120 | components: 121 | messages: 122 | lightMeasured: 123 | name: lightMeasured 124 | title: Light measured 125 | summary: >- 126 | Inform about environmental lighting conditions of a particular 127 | streetlight. 128 | contentType: application/json 129 | traits: 130 | - $ref: '#/components/messageTraits/commonHeaders' 131 | payload: 132 | $ref: '#/components/schemas/lightMeasuredPayload' 133 | turnOnOff: 134 | name: turnOnOff 135 | title: Turn on/off 136 | summary: Command a particular streetlight to turn the lights on or off. 137 | traits: 138 | - $ref: '#/components/messageTraits/commonHeaders' 139 | payload: 140 | $ref: '#/components/schemas/turnOnOffPayload' 141 | dimLight: 142 | name: dimLight 143 | title: Dim light 144 | summary: Command a particular streetlight to dim the lights. 145 | traits: 146 | - $ref: '#/components/messageTraits/commonHeaders' 147 | payload: 148 | $ref: '#/components/schemas/dimLightPayload' 149 | schemas: 150 | lightMeasuredPayload: 151 | type: object 152 | required: 153 | - lumens 154 | properties: 155 | lumens: 156 | type: integer 157 | minimum: 0 158 | description: Light intensity measured in lumens. 159 | sentAt: 160 | $ref: '#/components/schemas/sentAt' 161 | turnOnOffPayload: 162 | type: object 163 | properties: 164 | command: 165 | type: string 166 | enum: 167 | - 'on' 168 | - 'off' 169 | description: Whether to turn on or off the light. 170 | sentAt: 171 | $ref: '#/components/schemas/sentAt' 172 | dimLightPayload: 173 | type: object 174 | properties: 175 | percentage: 176 | type: integer 177 | description: Percentage to which the light should be dimmed to. 178 | minimum: 0 179 | maximum: 100 180 | sentAt: 181 | $ref: '#/components/schemas/sentAt' 182 | sentAt: 183 | type: string 184 | format: date-time 185 | description: Date and time when the message was sent. 186 | securitySchemes: 187 | saslScram: 188 | type: scramSha256 189 | description: Provide your username and password for SASL/SCRAM authentication 190 | certs: 191 | type: X509 192 | description: Download the certificate files from service provider 193 | parameters: 194 | streetlightId: 195 | description: The ID of the streetlight. 196 | messageTraits: 197 | commonHeaders: 198 | headers: 199 | type: object 200 | properties: 201 | my-app-header: 202 | type: integer 203 | minimum: 0 204 | maximum: 100 205 | operationTraits: 206 | kafka: 207 | bindings: 208 | kafka: 209 | clientId: 210 | type: string 211 | enum: 212 | - my-app-id -------------------------------------------------------------------------------- /test/src/AsyncApiValidatorFactory.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const mocks = require('../mocks') 3 | const AsyncApiValidator = require('../../index') 4 | const assert = require('assert') 5 | 6 | describe('AsyncApiValidator', () => { 7 | describe('schema v2.0.0', () => { 8 | it('should throw error if file not found', async () => { 9 | const validator = AsyncApiValidator.fromSource('something') 10 | await expect(validator).rejects.toThrowError('ENOENT: no such file or directory, open \'something\'') 11 | }) 12 | 13 | it('should throw error if unable to parse file', async () => { 14 | const validator = AsyncApiValidator.fromSource(mocks.htmlFile) 15 | await expect(validator).rejects.toThrowError(/This is not an AsyncAPI document./) 16 | }) 17 | 18 | it('should throw error if schema body is broken', async () => { 19 | const validator = AsyncApiValidator.fromSource('./test/schemas/broken.yml') 20 | await expect(validator).rejects.toThrowError(/Object must have required property "channels"/) 21 | }) 22 | 23 | it('should throw error if schema is not valid with asyncapi', async () => { 24 | try { 25 | await AsyncApiValidator.fromSource('./test/schemas/invalid.yml') 26 | } catch (e) { 27 | expect(e.message).toBe('Property "channel" is not expected to be here ') 28 | expect(e.errors).toContainEqual({ 29 | code: 'asyncapi-document-resolved', 30 | message: 'Property "channel" is not expected to be here', 31 | path: ['channel'], 32 | severity: 0, 33 | source: 'test/schemas/invalid.yml', 34 | range: { 35 | start: {line: 14, character: 8}, 36 | end: {line: 14, character: 8} 37 | } 38 | }) 39 | } 40 | }) 41 | 42 | it('should throw error - msgIdentifier is required', async () => { 43 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/slack.yml') 44 | const validate = () => validator.validate('hello', { 45 | type: null 46 | }, 'device-installation-events') 47 | expect(validate).toThrowError(new Error('"msgIdentifier" is required')) 48 | }) 49 | 50 | it('should throw error - operation is not valid', async () => { 51 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/slack.yml', {msgIdentifier: 'summary'}) 52 | const validate = () => validator.validate('hello', { 53 | type: null 54 | }, 'device-installation-events') 55 | expect(validate).toThrowError(new Error('operation "undefined" is not valid')) 56 | }) 57 | 58 | it('should throw error - If schema is array and payload is object', async () => { 59 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/mqtt.yaml', {msgIdentifier: 'name'}) 60 | const validate = () => validator.validate('devices/measurements', { 61 | name: 'temperature', 62 | timestamp: '2019-01-21T11:04:05Z', 63 | value: 36.6 64 | }, 'devices/{deviceId}/measurements', 'publish') 65 | expect(validate).toThrowError(new Error('data must be array')) 66 | }) 67 | 68 | it('should not throw error - If schema is array and payload is array', async () => { 69 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/mqtt.yaml', {msgIdentifier: 'name'}) 70 | const validate = validator.validate('devices/measurements', [{ 71 | name: 'temperature', 72 | timestamp: '2019-01-21T11:04:05Z', 73 | value: 36.6 74 | }], 'devices/{deviceId}/measurements', 'publish') 75 | expect(validate).toStrictEqual(true) 76 | }) 77 | 78 | it('should not throw error - If schema is array and payload is object with ignoreArray option true', async () => { 79 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/mqtt.yaml', {msgIdentifier: 'name', ignoreArray: true}) 80 | const validate = validator.validate('devices/measurements', { 81 | name: 'temperature', 82 | timestamp: '2019-01-21T11:04:05Z', 83 | value: 36.6 84 | }, 'devices/{deviceId}/measurements', 'publish') 85 | expect(validate).toStrictEqual(true) 86 | }) 87 | 88 | it('should not throw error - If schema is array and payload is array with ignoreArray option true', async () => { 89 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/mqtt.yaml', {msgIdentifier: 'name', ignoreArray: true}) 90 | const validate = validator.validate('devices/measurements', [{ 91 | name: 'temperature', 92 | timestamp: '2019-01-21T11:04:05Z', 93 | value: 36.6 94 | }], 'devices/{deviceId}/measurements', 'publish') 95 | expect(validate).toStrictEqual(true) 96 | }) 97 | 98 | it('should parse JSON schema', async () => { 99 | await assert.doesNotReject(AsyncApiValidator.fromSource('./test/schemas/jsonSchema.json', {msgIdentifier: 'name'})) 100 | }) 101 | 102 | it('should accept a javascript Object as schema', async () => { 103 | const schema = JSON.parse(fs.readFileSync('./test/schemas/jsonSchema.json')) 104 | await assert.doesNotReject(AsyncApiValidator.fromSource(schema, {msgIdentifier: 'name'})) 105 | }) 106 | }) 107 | 108 | describe('schema v2.4.0', () => { 109 | it('should throw error - If schema is array and payload is object', async () => { 110 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/mqtt.yaml') 111 | const validate = () => validator.validateByMessageId('devices/measurements', { 112 | name: 'temperature', 113 | timestamp: '2019-01-21T11:04:05Z', 114 | value: 36.6 115 | }) 116 | expect(validate).toThrowError(new Error('data must be array')) 117 | }) 118 | 119 | it('should not throw error - If schema is array and payload is object with ignoreArray option true', async () => { 120 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/mqtt.yaml', {ignoreArray: true}) 121 | const validate = validator.validateByMessageId('devices/measurements', { 122 | name: 'temperature', 123 | timestamp: '2019-01-21T11:04:05Z', 124 | value: 36.6 125 | }) 126 | expect(validate).toStrictEqual(true) 127 | }) 128 | 129 | it('should not throw error - If schema is array and payload is array with ignoreArray option true', async () => { 130 | const validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/mqtt.yaml', {ignoreArray: true}) 131 | const validate = validator.validateByMessageId('devices/measurements', [{ 132 | name: 'temperature', 133 | timestamp: '2019-01-21T11:04:05Z', 134 | value: 36.6 135 | }]) 136 | expect(validate).toStrictEqual(true) 137 | }) 138 | }) 139 | }) 140 | -------------------------------------------------------------------------------- /test/v2.0.0/deviceMessages.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/deviceMessages.yaml', 7 | {msgIdentifier: 'name', path: './test/schemas/v2.0.0/'}) 8 | }) 9 | 10 | it('should throw error - channel not found', () => { 11 | const validate = () => validator.validate('hello', { 12 | type: null 13 | }, 'device-events', 'publish') 14 | expect(validate).toThrowError(new Error('channel "device-events" not found')) 15 | }) 16 | 17 | it('should throw error - key not found on channel with operation', () => { 18 | const validate = () => validator.validate('hello', { 19 | type: null 20 | }, 'device-installation-events', 'publish') 21 | expect(validate).toThrowError(new Error('message with key "hello" on channel "device-installation-events" and operation "publish" not found')) 22 | }) 23 | 24 | it('should throw error - data should have required property', () => { 25 | const validate = () => validator.validate('DeviceInstallationResponsePublished', { 26 | type: 'null' 27 | }, 'device-installation-events', 'publish') 28 | // eslint-disable-next-line quotes, max-len 29 | expect(validate).toThrowError(new Error("data must have required property 'id', data must have required property 'key', data must have required property 'generated', data must have required property 'requestId'")) 30 | }) 31 | 32 | it('should not throw error', () => { 33 | const validate = validator.validate('DeviceInstallationResponsePublished', { 34 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 35 | key: 'string', 36 | type: 'string', 37 | generated: '2017-01-09T08:27:22.222Z', 38 | requestId: 'string', 39 | data: { 40 | deviceId: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 41 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 42 | status: 'success', 43 | message: 'string', 44 | temperature: 2147483647, 45 | vibration: -9223372036854776000, 46 | floatType: 3.402823669209385e+38, 47 | doubleType: 1.7976931348623158e+308, 48 | byteType: 'U3dhZ2dlciByb2Nrcw==' 49 | } 50 | }, 'device-installation-events', 'publish') 51 | expect(validate).toStrictEqual(true) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /test/v2.0.0/mqtt.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('mqtt', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/mqtt.yaml', {msgIdentifier: 'name'}) 7 | }) 8 | 9 | it('should validate alerts message', () => { 10 | const validate = validator.validate('devices/alerts', [{ 11 | message: 'temperature too high', 12 | name: 'temperature_high', 13 | state: 'set', 14 | timestamp: 0 15 | }], 'devices/{deviceId}/alerts', 'publish') 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | 19 | it('should validate command_responses message', () => { 20 | const validate = validator.validate('devices/command_responses', [{ 21 | id: '7f5bc456-21f2-4e9e-a38f-80baf762b1c5', 22 | message: 'message describing the command progress', 23 | status: 'in_progress', 24 | timestamp: 0 25 | }], 'devices/{deviceId}/command_responses', 'publish') 26 | expect(validate).toStrictEqual(true) 27 | }) 28 | 29 | it('should validate commands message', () => { 30 | const validate = validator.validate('devices/commands', { 31 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 32 | id: '7f5bc456-21f2-4e9e-a38f-80baf762b1c5', 33 | name: 'dim_light', 34 | parameter: true, 35 | timestamp: 0 36 | }, 'devices/{deviceId}/commands', 'subscribe') 37 | expect(validate).toStrictEqual(true) 38 | }) 39 | 40 | it('should validate config_requests message', () => { 41 | const validate = validator.validate('devices/config_requests', [{ 42 | timestamp: 0 43 | }], 'devices/{deviceId}/config_requests', 'publish') 44 | expect(validate).toStrictEqual(true) 45 | }) 46 | 47 | it('should validate configs message', () => { 48 | const validate = validator.validate('devices/configs', { 49 | configuration: { 50 | maximum_temperature: 60 51 | }, 52 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 53 | version: 2 54 | }, 'devices/{deviceId}/configs', 'subscribe') 55 | expect(validate).toStrictEqual(true) 56 | }) 57 | 58 | it('should validate errors message', () => { 59 | const validate = validator.validate('devices/errors', { 60 | error: 'command field \'id\' is NOT an UUID', 61 | messageId: 31248, 62 | payload: '{\'id\':\'not UUID\',\'status\':\'in_progress\'}', 63 | topic: 'devices/763c073a-e0ff-41a9-bd51-3386975ea4e3/commands' 64 | }, 'devices/{deviceId}/errors', 'subscribe') 65 | expect(validate).toStrictEqual(true) 66 | }) 67 | 68 | it('should validate installations message', () => { 69 | const validate = validator.validate('devices/installations', { 70 | buildStamp: 'string', 71 | description: 'package.gwa-core-v1.1', 72 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 73 | fileName: 'gwa-core.tgz', 74 | id: '763c073a-e0ff-41a9-bd51-3386975ea4e3', 75 | location: 'http://foo.bar/buzz.xyz', 76 | signature: '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 77 | signatureType: 'sha-256', 78 | size: 1048576, 79 | timestamp: 0, 80 | type: 'gwa-core-package' 81 | }, 'devices/{deviceId}/installations', 'subscribe') 82 | expect(validate).toStrictEqual(true) 83 | }) 84 | 85 | it('should validate measurements message', () => { 86 | const validate = validator.validate('devices/measurements', [{ 87 | name: 'temperature', 88 | timestamp: 0, 89 | value: 36.6 90 | }], 'devices/{deviceId}/measurements', 'publish') 91 | expect(validate).toStrictEqual(true) 92 | }) 93 | 94 | it('should throw error if try to use validateByMessageId() method with unsupported schema', () => { 95 | const validate = () => validator.validateByMessageId('devices/measurements', [{ 96 | name: 'temperature', 97 | timestamp: 0, 98 | value: 36.6 99 | }]) 100 | expect(validate).toThrowError(new Error('AsyncAPI schema version should be >= 2.4.0 and <3.0.0 . Your version is "2.0.0"')) 101 | }) 102 | }) 103 | -------------------------------------------------------------------------------- /test/v2.0.0/slack.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe.only('slack', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/slack.yml', {msgIdentifier: 'name'}) 7 | }) 8 | 9 | it('should validate outgoingMessage message', () => { 10 | const validate = validator.validate('outgoingMessage', { 11 | id: 0, 12 | type: 'message', 13 | channel: 'string', 14 | text: 'string' 15 | }, '/', 'publish') 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | 19 | it('should validate botChanged message', () => { 20 | const validate = validator.validate('botChanged', { 21 | type: 'bot_added', 22 | bot: { 23 | id: 'string', 24 | app_id: 'string', 25 | name: 'string', 26 | icons: { 27 | property1: 'string', 28 | property2: 'string' 29 | } 30 | } 31 | }, '/', 'subscribe') 32 | expect(validate).toStrictEqual(true) 33 | }) 34 | 35 | it('should validate channelDeleted message', () => { 36 | const validate = validator.validate('channelDeleted', { 37 | type: 'channel_deleted', 38 | channel: 'string' 39 | }, '/', 'subscribe') 40 | expect(validate).toStrictEqual(true) 41 | }) 42 | 43 | it('should validate message', () => { 44 | const validate = validator.validate('message', { 45 | type: 'message', 46 | user: 'string', 47 | channel: 'string', 48 | text: 'string', 49 | ts: 'string', 50 | attachments: [ 51 | { 52 | fallback: 'string', 53 | color: 'string', 54 | pretext: 'string', 55 | author_name: 'string', 56 | author_link: 'http://example.com', 57 | author_icon: 'http://example.com', 58 | title: 'string', 59 | title_link: 'http://example.com', 60 | text: 'string', 61 | fields: [ 62 | { 63 | title: 'string', 64 | value: 'string', 65 | short: true 66 | } 67 | ], 68 | image_url: 'http://example.com', 69 | thumb_url: 'http://example.com', 70 | footer: 'string', 71 | footer_icon: 'http://example.com', 72 | ts: 0 73 | } 74 | ], 75 | edited: { 76 | user: 'string', 77 | ts: 'string' 78 | } 79 | }, '/', 'subscribe') 80 | expect(validate).toStrictEqual(true) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /test/v2.0.0/userMessages.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('userMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.0.0/userMessages.yaml', {msgIdentifier: 'x-custom-key'}) 7 | }) 8 | 9 | it('should validate UserDeleted message', () => { 10 | const validate = validator.validate('UserDeleted', { 11 | role: 'admin', 12 | userName: 'user@gmail.com' 13 | }, 'user-events', 'publish') 14 | expect(validate).toStrictEqual(true) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/v2.4.0/deviceMessages.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/deviceMessages.yaml', 7 | {msgIdentifier: 'name', path: './test/schemas/v2.4.0/'}) 8 | }) 9 | 10 | it('should throw error - channel not found', () => { 11 | const validate = () => validator.validate('hello', { 12 | type: null 13 | }, 'device-events', 'publish') 14 | expect(validate).toThrowError(new Error('channel "device-events" not found')) 15 | }) 16 | 17 | it('should throw error - key not found on channel with operation', () => { 18 | const validate = () => validator.validate('hello', { 19 | type: null 20 | }, 'device-installation-events', 'publish') 21 | expect(validate).toThrowError(new Error('message with key "hello" on channel "device-installation-events" and operation "publish" not found')) 22 | }) 23 | 24 | it('should throw error - data should have required property', () => { 25 | const validate = () => validator.validate('DeviceInstallationResponsePublished', { 26 | type: 'null' 27 | }, 'device-installation-events', 'publish') 28 | // eslint-disable-next-line quotes, max-len 29 | expect(validate).toThrowError(new Error("data must have required property 'id', data must have required property 'key', data must have required property 'generated', data must have required property 'requestId'")) 30 | }) 31 | 32 | it('should throw error - messageId not found', () => { 33 | const validate = () => validator.validateByMessageId('DeviceInstallationResponsePublished', { 34 | type: 'null' 35 | }) 36 | expect(validate).toThrowError(new Error('message with messageId "DeviceInstallationResponsePublished" not found')) 37 | }) 38 | 39 | it('should not throw error with validate()', () => { 40 | const validate = validator.validate('DeviceInstallationResponsePublished', { 41 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 42 | key: 'string', 43 | type: 'string', 44 | generated: '2017-01-09T08:27:22.222Z', 45 | requestId: 'string', 46 | data: { 47 | deviceId: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 48 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 49 | status: 'success', 50 | message: 'string', 51 | temperature: 2147483647, 52 | vibration: -9223372036854776000, 53 | floatType: 3.402823669209385e+38, 54 | doubleType: 1.7976931348623158e+308, 55 | byteType: 'U3dhZ2dlciByb2Nrcw==' 56 | } 57 | }, 'device-installation-events', 'publish') 58 | expect(validate).toStrictEqual(true) 59 | }) 60 | 61 | it('should not throw error with validateByMessageId()', () => { 62 | const validate = validator.validateByMessageId('PubDeviceInstallationResponsePublishedMessage', { 63 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 64 | key: 'string', 65 | type: 'string', 66 | generated: '2017-01-09T08:27:22.222Z', 67 | requestId: 'string', 68 | data: { 69 | deviceId: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 70 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 71 | status: 'success', 72 | message: 'string', 73 | temperature: 2147483647, 74 | vibration: -9223372036854776000, 75 | floatType: 3.402823669209385e+38, 76 | doubleType: 1.7976931348623158e+308, 77 | byteType: 'U3dhZ2dlciByb2Nrcw==', 78 | numberType: 19 79 | } 80 | }) 81 | expect(validate).toStrictEqual(true) 82 | }) 83 | 84 | it('should throw error for exclusiveMaximum', () => { 85 | const validate = () => validator.validateByMessageId('PubDeviceInstallationResponsePublishedMessage', { 86 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 87 | key: 'string', 88 | type: 'string', 89 | generated: '2017-01-09T08:27:22.222Z', 90 | requestId: 'string', 91 | data: { 92 | deviceId: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 93 | id: 'bd58d14f-fd3e-449c-b60c-a56548190d68', 94 | status: 'success', 95 | message: 'string', 96 | temperature: 2147483647, 97 | vibration: -9223372036854776000, 98 | floatType: 3.402823669209385e+38, 99 | doubleType: 1.7976931348623158e+308, 100 | byteType: 'U3dhZ2dlciByb2Nrcw==', 101 | numberType: 20 102 | } 103 | }) 104 | expect(validate).toThrowError(new Error('data/data/numberType must be < 20')) 105 | }) 106 | }) 107 | -------------------------------------------------------------------------------- /test/v2.4.0/mqtt.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('mqtt', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/mqtt.yaml') 7 | }) 8 | 9 | it('should validate alerts message', () => { 10 | const validate = validator.validateByMessageId('devices/alerts', [{ 11 | message: 'temperature too high', 12 | name: 'temperature_high', 13 | state: 'set', 14 | timestamp: 0 15 | }]) 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | 19 | it('should validate command_responses message', () => { 20 | const validate = validator.validateByMessageId('devices/command_responses', [{ 21 | id: '7f5bc456-21f2-4e9e-a38f-80baf762b1c5', 22 | message: 'message describing the command progress', 23 | status: 'in_progress', 24 | timestamp: 0 25 | }]) 26 | expect(validate).toStrictEqual(true) 27 | }) 28 | 29 | it('should validate commands message', () => { 30 | const validate = validator.validateByMessageId('devices/commands', { 31 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 32 | id: '7f5bc456-21f2-4e9e-a38f-80baf762b1c5', 33 | name: 'dim_light', 34 | parameter: true, 35 | timestamp: 0 36 | }) 37 | expect(validate).toStrictEqual(true) 38 | }) 39 | 40 | it('should validate config_requests message', () => { 41 | const validate = validator.validateByMessageId('devices/config_requests', [{ 42 | timestamp: 0 43 | }]) 44 | expect(validate).toStrictEqual(true) 45 | }) 46 | 47 | it('should validate configs message', () => { 48 | const validate = validator.validateByMessageId('devices/configs', { 49 | configuration: { 50 | maximum_temperature: 60 51 | }, 52 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 53 | version: 2 54 | }) 55 | expect(validate).toStrictEqual(true) 56 | }) 57 | 58 | it('should validate errors message', () => { 59 | const validate = validator.validateByMessageId('devices/errors', { 60 | error: 'command field \'id\' is NOT an UUID', 61 | messageId: 31248, 62 | payload: '{\'id\':\'not UUID\',\'status\':\'in_progress\'}', 63 | topic: 'devices/763c073a-e0ff-41a9-bd51-3386975ea4e3/commands' 64 | }) 65 | expect(validate).toStrictEqual(true) 66 | }) 67 | 68 | it('should validate installations message', () => { 69 | const validate = validator.validateByMessageId('devices/installations', { 70 | buildStamp: 'string', 71 | description: 'package.gwa-core-v1.1', 72 | deviceId: 'd44d8a14-5fbb-4e4a-96a6-ed0c71c11fa8', 73 | fileName: 'gwa-core.tgz', 74 | id: '763c073a-e0ff-41a9-bd51-3386975ea4e3', 75 | location: 'http://foo.bar/buzz.xyz', 76 | signature: '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', 77 | signatureType: 'sha-256', 78 | size: 1048576, 79 | timestamp: 0, 80 | type: 'gwa-core-package' 81 | }) 82 | expect(validate).toStrictEqual(true) 83 | }) 84 | 85 | it('should validate measurements message', () => { 86 | const validate = validator.validateByMessageId('devices/measurements', [{ 87 | name: 'temperature', 88 | timestamp: 0, 89 | value: 36.6 90 | }]) 91 | expect(validate).toStrictEqual(true) 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /test/v2.4.0/slack.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe.only('slack', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/slack.yml', {msgIdentifier: 'name'}) 7 | }) 8 | 9 | it('should validate outgoingMessage message', () => { 10 | const validate = validator.validate('outgoingMessage', { 11 | id: 0, 12 | type: 'message', 13 | channel: 'string', 14 | text: 'string' 15 | }, '/', 'publish') 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | 19 | it('should validate botChanged message', () => { 20 | const validate = validator.validateByMessageId('botChanged', { 21 | type: 'bot_added', 22 | bot: { 23 | id: 'string', 24 | app_id: 'string', 25 | name: 'string', 26 | icons: { 27 | property1: 'string', 28 | property2: 'string' 29 | } 30 | } 31 | }) 32 | expect(validate).toStrictEqual(true) 33 | }) 34 | 35 | it('should validate channelDeleted message', () => { 36 | const validate = validator.validateByMessageId('channelDeleted', { 37 | type: 'channel_deleted', 38 | channel: 'string' 39 | }) 40 | expect(validate).toStrictEqual(true) 41 | }) 42 | 43 | it('should validate message', () => { 44 | const validate = validator.validateByMessageId('message', { 45 | type: 'message', 46 | user: 'string', 47 | channel: 'string', 48 | text: 'string', 49 | ts: 'string', 50 | attachments: [ 51 | { 52 | fallback: 'string', 53 | color: 'string', 54 | pretext: 'string', 55 | author_name: 'string', 56 | author_link: 'http://example.com', 57 | author_icon: 'http://example.com', 58 | title: 'string', 59 | title_link: 'http://example.com', 60 | text: 'string', 61 | fields: [ 62 | { 63 | title: 'string', 64 | value: 'string', 65 | short: true 66 | } 67 | ], 68 | image_url: 'http://example.com', 69 | thumb_url: 'http://example.com', 70 | footer: 'string', 71 | footer_icon: 'http://example.com', 72 | ts: 0 73 | } 74 | ], 75 | edited: { 76 | user: 'string', 77 | ts: 'string' 78 | } 79 | }) 80 | expect(validate).toStrictEqual(true) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /test/v2.4.0/userMessages.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('userMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v2.4.0/userMessages.yaml') 7 | }) 8 | 9 | it('should validate UserDeleted message', () => { 10 | const validate = validator.validateByMessageId('UserDeleted', { 11 | role: 'admin', 12 | userName: 'user@gmail.com' 13 | }) 14 | expect(validate).toStrictEqual(true) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/v3.0.0/pingPong.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v3.0.0/pingPong.yml', { 7 | msgIdentifier: 'name' 8 | }) 9 | }) 10 | 11 | it('should throw error - message not found not found', () => { 12 | const validate = () => validator.validate('Pong', { 13 | type: null 14 | }, 'ping', 'receive') 15 | expect(validate).toThrowError(new Error('message with key "Pong" on channel "ping" and operation "receive" not found')) 16 | }) 17 | 18 | it('should validate pong', () => { 19 | const validate = validator.validate('Pong', { 20 | event: 'pong' 21 | }, 'pong', 'receive') 22 | expect(validate).toStrictEqual(true) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/v3.0.0/streetLights.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v3.0.0/streetLights.yml', { 7 | msgIdentifier: 'name' 8 | }) 9 | }) 10 | 11 | it('should throw error - channel not found', () => { 12 | const validate = () => validator.validate('hello', { 13 | type: null 14 | }, 'lightSent', 'receive') 15 | expect(validate).toThrowError(new Error('channel "lightSent" not found')) 16 | }) 17 | 18 | it('should throw error - key not found on channel with operation', () => { 19 | const validate = () => validator.validate('hello', { 20 | type: null 21 | }, 'lightingMeasured', 'receive') 22 | expect(validate).toThrowError(new Error('message with key "hello" on channel "lightingMeasured" and operation "receive" not found')) 23 | }) 24 | 25 | it('should throw error - data should have required property', () => { 26 | const validate = () => validator.validate('lightMeasured', { 27 | type: 'null' 28 | }, 'lightingMeasured', 'receive') 29 | expect(validate).toThrowError(new Error('data must have required property \'lumens\'')) 30 | }) 31 | 32 | it('should validate lightMeasured', () => { 33 | const validate = validator.validate('lightMeasured', { 34 | lumens: 0, 35 | sentAt: '2019-08-24T14:15:22Z' 36 | }, 'lightingMeasured', 'receive') 37 | expect(validate).toStrictEqual(true) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/v3.0.0/streetLightsMini.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v3.0.0/streetLightsMini.yml', { 7 | msgIdentifier: 'x-unique-id' 8 | }) 9 | }) 10 | 11 | it('should validate lightMeasured message', () => { 12 | const validate = validator.validate('lightMeasured', { 13 | lumens: 0, 14 | sentAt: '2019-08-24T14:15:22Z' 15 | }, 'lightingMeasured', 'send') 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/v3.0.0/streetLightsMultiMsg.js: -------------------------------------------------------------------------------- 1 | const AsyncApiValidator = require('../../index') 2 | 3 | describe('deviceMessages', () => { 4 | let validator 5 | beforeEach(async () => { 6 | validator = await AsyncApiValidator.fromSource('./test/schemas/v3.0.0/streetLightsMultiMsg.yml', { 7 | msgIdentifier: 'name' 8 | }) 9 | }) 10 | 11 | it('should validate lightMeasured with channel lightTurnOff', () => { 12 | const validate = validator.validate('turnOnOff', { 13 | command: 'on', 14 | sentAt: '2019-08-24T14:15:22Z' 15 | }, 'lightTurnOff', 'send') 16 | expect(validate).toStrictEqual(true) 17 | }) 18 | 19 | it('should throw error for channel which is not used in any operation', () => { 20 | const validate = () => validator.validate('turnOnOff', { 21 | command: 'on', 22 | sentAt: '2019-08-24T14:15:22Z' 23 | }, 'lightTurnOn', 'send') 24 | expect(validate).toThrowError(new Error('channel "lightTurnOn" not found')) 25 | }) 26 | }) 27 | --------------------------------------------------------------------------------