From e566d4dc7d0f67f540c7b7490c434e49b6fc62f1 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 19 Feb 2020 07:51:43 +0100 Subject: [PATCH] WIP --- package.json | 8 +- rollup.config.js | 1 + scripts/generate-assets.js | 6 +- scripts/generate-karma-fixtures.js | 6 +- setupKarma.ts | 18 +- .../__fixtures__/aas/streetlight.yaml | 211 +++ src/__tests__/spectral.test.ts | 48 +- src/assets.ts | 1 + src/cli/services/linter/linter.ts | 2 + src/formats/__tests__/asyncapi.test.ts | 22 + src/formats/asyncapi.ts | 6 + src/formats/bearsAStringPropertyNamed.ts | 5 + src/formats/index.ts | 1 + src/formats/openapi.ts | 2 +- src/rulesets/aas/index.json | 297 ++++ src/rulesets/aas/schemas/schema.aas2.json | 1358 +++++++++++++++++ 16 files changed, 1956 insertions(+), 36 deletions(-) create mode 100644 src/__tests__/__fixtures__/aas/streetlight.yaml create mode 100644 src/formats/__tests__/asyncapi.test.ts create mode 100644 src/formats/asyncapi.ts create mode 100644 src/formats/bearsAStringPropertyNamed.ts create mode 100644 src/rulesets/aas/index.json create mode 100644 src/rulesets/aas/schemas/schema.aas2.json diff --git a/package.json b/package.json index 50d96378b7..72b9712d65 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "scripts": { "build.binary": "pkg . --output ./binaries/spectral", "build.clean": "rimraf ./coverage && rimraf ./dist && rimraf ./rulesets && rimraf ./__karma__/__fixtures__", - "build.oas-functions": "rollup -c", + "build.functions": "rollup -c", "build": "tsc -p ./tsconfig.build.json", "cli": "node -r ts-node/register -r tsconfig-paths/register src/cli/index.ts", "cli:debug": "node -r ts-node/register -r tsconfig-paths/register --inspect-brk src/cli/index.ts", @@ -44,9 +44,9 @@ "lint.fix": "yarn lint --fix", "lint": "tsc --noEmit && tslint 'src/**/*.ts'", "copy.html-templates": "copyfiles -u 1 \"./src/formatters/html/*.html\" \"./dist/\"", - "postbuild.oas-functions": "copyfiles -u 1 \"dist/rulesets/oas*/functions/*.js\" ./", - "postbuild": "yarn build.oas-functions && yarn generate-assets", - "prebuild": "yarn build.clean && copyfiles -u 1 \"src/rulesets/oas*/**/*.json\" dist && copyfiles -u 1 \"src/rulesets/oas*/**/*.json\" ./ && yarn copy.html-templates", + "postbuild.functions": "copyfiles -u 1 \"dist/rulesets/{o,a}as/functions/*.js\" ./", + "postbuild": "yarn build.functions && yarn generate-assets", + "prebuild": "yarn build.clean && copyfiles -u 1 \"src/rulesets/{o,a}as/**/*.json\" dist && copyfiles -u 1 \"src/rulesets/{o,a}as/**/*.json\" ./ && yarn copy.html-templates", "prebuild.binary": "yarn build", "pretest.karma": "node ./scripts/generate-karma-fixtures.js && yarn pretest", "pretest": "node ./scripts/generate-assets.js", diff --git a/rollup.config.js b/rollup.config.js index d74dac46bb..b89260f1c1 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,6 +7,7 @@ import { terser } from 'rollup-plugin-terser'; const BASE_PATH = process.cwd(); const directory = 'dist/rulesets/oas/functions'; +// TODO: Add aas const targetDir = path.join(BASE_PATH, directory); const functions = []; diff --git a/scripts/generate-assets.js b/scripts/generate-assets.js index c910ef4a7e..c9ac1b0b45 100755 --- a/scripts/generate-assets.js +++ b/scripts/generate-assets.js @@ -30,8 +30,10 @@ const target = path.join(baseDir, `assets.json`); const assets = {}; (async () => { - await processDirectory(assets, path.join(__dirname, '../rulesets/oas')); - await writeFileAsync(target, JSON.stringify(assets, null, 2)); + ['oas', 'aas'].forEach(async (kind) => { + await processDirectory(assets, path.join(__dirname, `../rulesets/${kind}`)); + await writeFileAsync(target, JSON.stringify(assets, null, 2)); + }) })(); async function processDirectory(assets, dir) { diff --git a/scripts/generate-karma-fixtures.js b/scripts/generate-karma-fixtures.js index 203c412a21..4f329f9870 100755 --- a/scripts/generate-karma-fixtures.js +++ b/scripts/generate-karma-fixtures.js @@ -8,9 +8,9 @@ if (!fs.existsSync(baseDir)) { fs.mkdirSync(baseDir); } -for (const spec of ['']) { - const target = path.join(baseDir, `oas${spec}-functions.json`); - const fnsPath = path.join(__dirname, `../rulesets/oas${spec}/functions`); +for (const rulesetName of ['oas', 'aas']) { + const target = path.join(baseDir, `${rulesetName}-functions.json`); + const fnsPath = path.join(__dirname, `../rulesets/${rulesetName}/functions`); const bundledFns = {}; if (fs.existsSync(fnsPath)) { diff --git a/setupKarma.ts b/setupKarma.ts index 9313240824..142b45cbb7 100644 --- a/setupKarma.ts +++ b/setupKarma.ts @@ -3,6 +3,7 @@ import { FetchMockSandbox } from 'fetch-mock'; const oasRuleset = JSON.parse(JSON.stringify(require('./rulesets/oas/index.json'))); const oasFunctions = JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/oas-functions.json'))); +const aasFunctions = JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/aas-functions.json'))); const oas2Schema = JSON.parse(JSON.stringify(require('./rulesets/oas/schemas/schema.oas2.json'))); const oas3Schema = JSON.parse(JSON.stringify(require('./rulesets/oas/schemas/schema.oas3.json'))); @@ -28,12 +29,17 @@ beforeEach(() => { body: JSON.parse(JSON.stringify(oas3Schema)), }); - for (const [name, fn] of Object.entries(oasFunctions)) { - fetchMock.get(`https://unpkg.com/@stoplight/spectral/rulesets/oas/functions/${name}`, { - status: 200, - body: fn, - }); - } + [ + ['oas', oasFunctions], + ['aas', aasFunctions], + ].forEach(([rulesetName, funcs]) => { + for (const [name, fn] of Object.entries(funcs)) { + fetchMock.get(`https://unpkg.com/@stoplight/spectral/rulesets/${rulesetName}/functions/${name}`, { + status: 200, + body: fn, + }); + } + }); fetchMock.get('http://json-schema.org/draft-04/schema', { status: 200, diff --git a/src/__tests__/__fixtures__/aas/streetlight.yaml b/src/__tests__/__fixtures__/aas/streetlight.yaml new file mode 100644 index 0000000000..51f75039f2 --- /dev/null +++ b/src/__tests__/__fixtures__/aas/streetlight.yaml @@ -0,0 +1,211 @@ +%YAML 1.2 +--- +asyncapi: '2.0.0' +info: + title: Streetlights API + version: '1.0.0' + description: | + The Smartylighting Streetlights API allows you to remotely manage the city lights. + + ### Check out its awesome features: + + * Turn a specific streetlight on/off 🌃 + * Dim a specific streetlight 😎 + * Receive real-time information about environmental lighting conditions 📈 + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + +servers: + production: + url: test.mosquitto.org:{port} + protocol: mqtt + description: Test broker + variables: + port: + description: Secure connection (TLS) is available through port 8883. + default: '1883' + enum: + - '1883' + - '8883' + security: + - apiKey: [] + - supportedOauthFlows: + - streetlights:on + - streetlights:off + - streetlights:dim + - openIdConnectWellKnown: [] + +defaultContentType: application/json + +channels: + smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured: + description: The topic on which measured values may be produced and consumed. + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + publish: + summary: Inform about environmental lighting conditions of a particular streetlight. + operationId: receiveLightMeasurement + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/lightMeasured' + + smartylighting/streetlights/1/0/action/{streetlightId}/turn/on: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + subscribe: + operationId: turnOn + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + + smartylighting/streetlights/1/0/action/{streetlightId}/turn/off: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + subscribe: + operationId: turnOff + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + + smartylighting/streetlights/1/0/action/{streetlightId}/dim: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + subscribe: + operationId: dimLight + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/dimLight' + +components: + messages: + lightMeasured: + name: lightMeasured + title: Light measured + summary: Inform about environmental lighting conditions of a particular streetlight. + contentType: application/json + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/lightMeasuredPayload" + turnOnOff: + name: turnOnOff + title: Turn on/off + summary: Command a particular streetlight to turn the lights on or off. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/turnOnOffPayload" + dimLight: + name: dimLight + title: Dim light + summary: Command a particular streetlight to dim the lights. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/dimLightPayload" + + schemas: + lightMeasuredPayload: + type: object + properties: + lumens: + type: integer + minimum: 0 + description: Light intensity measured in lumens. + sentAt: + $ref: "#/components/schemas/sentAt" + turnOnOffPayload: + type: object + properties: + command: + type: string + enum: + - on + - off + description: Whether to turn on or off the light. + sentAt: + $ref: "#/components/schemas/sentAt" + dimLightPayload: + type: object + properties: + percentage: + type: integer + description: Percentage to which the light should be dimmed to. + minimum: 0 + maximum: 100 + sentAt: + $ref: "#/components/schemas/sentAt" + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + + securitySchemes: + apiKey: + type: apiKey + in: user + description: Provide your API key as the user and leave the password empty. + supportedOauthFlows: + type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: 'https://authserver.example/auth' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + password: + tokenUrl: 'https://authserver.example/token' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + clientCredentials: + tokenUrl: 'https://authserver.example/token' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + authorizationCode: + authorizationUrl: 'https://authserver.example/auth' + tokenUrl: 'https://authserver.example/token' + refreshUrl: 'https://authserver.example/refresh' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + openIdConnectWellKnown: + type: openIdConnect + openIdConnectUrl: 'https://authserver.example/.well-known' + + parameters: + streetlightId: + description: The ID of the streetlight. + schema: + type: string + + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + + operationTraits: + kafka: + bindings: + kafka: + clientId: my-app-id diff --git a/src/__tests__/spectral.test.ts b/src/__tests__/spectral.test.ts index 8fe27ba1c8..481d3ffef4 100644 --- a/src/__tests__/spectral.test.ts +++ b/src/__tests__/spectral.test.ts @@ -9,17 +9,22 @@ import { Spectral } from '../spectral'; import { IResolver, IRunRule, RuleFunction } from '../types'; const oasRuleset = JSON.parse(JSON.stringify(require('../rulesets/oas/index.json'))); +const aasRuleset = JSON.parse(JSON.stringify(require('../rulesets/aas/index.json'))); const oasRulesetRules: Dictionary = oasRuleset.rules; +const aasRulesetRules: Dictionary = aasRuleset.rules; describe('spectral', () => { describe('loadRuleset', () => { - test('should support loading built-in rulesets', async () => { + test.each([ + ['spectral:oas', oasRulesetRules], + ['spectral:aas', aasRulesetRules], + ])('should support loading "%s" built-in ruleset', async (rulesetName, rules) => { const s = new Spectral(); - await s.loadRuleset('spectral:oas'); + await s.loadRuleset(rulesetName); expect(s.rules).toEqual( expect.objectContaining( - Object.entries(oasRulesetRules).reduce>((oasRules, [name, rule]) => { + Object.entries(rules).reduce>((oasRules, [name, rule]) => { oasRules[name] = { name, ...rule, @@ -34,24 +39,27 @@ describe('spectral', () => { ); }); - test('should support loading multiple times the built-in ruleset', async () => { - const s = new Spectral(); - await s.loadRuleset(['spectral:oas', 'spectral:oas']); + test.each([['spectral:oas'], ['spectral:aas']])( + 'should support loading multiple times the built-in ruleset "%s"', + async rulesetName => { + const s = new Spectral(); + await s.loadRuleset([rulesetName, rulesetName]); - expect(s.rules).toEqual( - Object.entries(oasRulesetRules).reduce>((oasRules, [name, rule]) => { - oasRules[name] = { - name, - ...rule, - formats: expect.arrayContaining([expect.any(String)]), - severity: expect.any(Number), - then: expect.any(Object), - }; - - return oasRules; - }, {}), - ); - }); + expect(s.rules).toEqual( + Object.entries(oasRulesetRules).reduce>((oasRules, [name, rule]) => { + oasRules[name] = { + name, + ...rule, + formats: expect.arrayContaining([expect.any(String)]), + severity: expect.any(Number), + then: expect.any(Object), + }; + + return oasRules; + }, {}), + ); + }, + ); }); describe('setRules & mergeRules', () => { diff --git a/src/assets.ts b/src/assets.ts index 7e9d8543f3..4ad5486e3d 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -6,6 +6,7 @@ function resolveSpectralRuleset(ruleset: string) { export const RESOLVE_ALIASES: Dictionary = { 'spectral:oas': resolveSpectralRuleset('oas'), + 'spectral:aas': resolveSpectralRuleset('aas'), }; export const STATIC_ASSETS: Dictionary = {}; diff --git a/src/cli/services/linter/linter.ts b/src/cli/services/linter/linter.ts index 8184256097..7b784c86c0 100644 --- a/src/cli/services/linter/linter.ts +++ b/src/cli/services/linter/linter.ts @@ -1,5 +1,6 @@ import { Document } from '../../../document'; import { + isAsyncApiv2, isJSONSchema, isJSONSchemaDraft2019_09, isJSONSchemaDraft4, @@ -21,6 +22,7 @@ import { getResolver } from './utils/getResolver'; const KNOWN_FORMATS: Array<[string, FormatLookup, string]> = [ ['oas2', isOpenApiv2, 'OpenAPI 2.0 (Swagger) detected'], ['oas3', isOpenApiv3, 'OpenAPI 3.x detected'], + ['aas2', isAsyncApiv2, 'AsyncAPI 2.x detected'], ['json-schema', isJSONSchema, 'JSON Schema detected'], ['json-schema-loose', isJSONSchemaLoose, 'JSON Schema (loose) detected'], ['json-schema-draft4', isJSONSchemaDraft4, 'JSON Schema Draft 4 detected'], diff --git a/src/formats/__tests__/asyncapi.test.ts b/src/formats/__tests__/asyncapi.test.ts new file mode 100644 index 0000000000..789b1a5c4c --- /dev/null +++ b/src/formats/__tests__/asyncapi.test.ts @@ -0,0 +1,22 @@ +import { isAsyncApiv2 } from '../asyncapi'; + +describe('AsyncApi format', () => { + describe('AsyncApi 2.0', () => { + it.each(['2.0.0', '2', '2.0'])('recognizes %s version correctly', version => { + expect(isAsyncApiv2({ asyncapi: version })).toBe(true); + }); + + it('does not recognize invalid document', () => { + expect(isAsyncApiv2({ asyncapi: '3.0' })).toBe(false); + expect(isAsyncApiv2({ asyncapi: '1.0' })).toBe(false); + expect(isAsyncApiv2({ asyncapi: 2 })).toBe(false); + expect(isAsyncApiv2({ openapi: '4.0' })).toBe(false); + expect(isAsyncApiv2({ openapi: '2.0' })).toBe(false); + expect(isAsyncApiv2({ openapi: null })).toBe(false); + expect(isAsyncApiv2({ swagger: null })).toBe(false); + expect(isAsyncApiv2({ swagger: '3.0' })).toBe(false); + expect(isAsyncApiv2({})).toBe(false); + expect(isAsyncApiv2(null)).toBe(false); + }); + }); +}); diff --git a/src/formats/asyncapi.ts b/src/formats/asyncapi.ts new file mode 100644 index 0000000000..f50852e0fd --- /dev/null +++ b/src/formats/asyncapi.ts @@ -0,0 +1,6 @@ +import { bearsAStringPropertyNamed } from './bearsAStringPropertyNamed'; + +type MaybeAsyncApi2 = Partial<{ asyncapi: unknown }>; + +export const isAsyncApiv2 = (document: unknown) => + bearsAStringPropertyNamed(document, 'asyncapi') && parseFloat(String((document as MaybeAsyncApi2).asyncapi)) === 2; diff --git a/src/formats/bearsAStringPropertyNamed.ts b/src/formats/bearsAStringPropertyNamed.ts new file mode 100644 index 0000000000..cf778a53c1 --- /dev/null +++ b/src/formats/bearsAStringPropertyNamed.ts @@ -0,0 +1,5 @@ +import { isObject } from 'lodash'; + +export const bearsAStringPropertyNamed = (document: unknown, propertyName: string) => { + return isObject(document) && propertyName in document && typeof document[propertyName] === 'string'; +}; diff --git a/src/formats/index.ts b/src/formats/index.ts index 54f1929ebb..78a2a4f1c0 100644 --- a/src/formats/index.ts +++ b/src/formats/index.ts @@ -1,2 +1,3 @@ export * from './openapi'; +export * from './asyncapi'; export * from './json-schema'; diff --git a/src/formats/openapi.ts b/src/formats/openapi.ts index 5d2e2b85d4..f3ff4acb86 100644 --- a/src/formats/openapi.ts +++ b/src/formats/openapi.ts @@ -1,4 +1,4 @@ -import { isObject } from 'lodash'; +import { bearsAStringPropertyNamed } from './bearsAStringPropertyNamed'; type MaybeOAS2 = Partial<{ swagger: unknown }>; type MaybeOAS3 = Partial<{ openapi: unknown }>; diff --git a/src/rulesets/aas/index.json b/src/rulesets/aas/index.json new file mode 100644 index 0000000000..0af7d7ca35 --- /dev/null +++ b/src/rulesets/aas/index.json @@ -0,0 +1,297 @@ +{ + "rules": { + "contact-properties": { + "description": "Contact object should have `name`, `url` and `email`.", + "recommended": true, + "formats": ["aas2"], + "severity": 2, + "type": "style", + "given": "$.info.contact", + "then": [ + { + "field": "name", + "function": "truthy" + }, + { + "field": "url", + "function": "truthy" + }, + { + "field": "email", + "function": "truthy" + } + ] + }, + "example-value-or-externalValue": { + "description": "Example should have either a `value` or `externalValue` field.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$..example", + "then": { + "function": "xor", + "functionOptions": { + "properties": [ + "externalValue", + "value" + ] + } + } + }, + "info-contact": { + "description": "Info object should contain `contact` object.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$", + "then": { + "field": "info.contact", + "function": "truthy" + } + }, + "info-description": { + "description": "AsyncAPI object info `description` must be present and non-empty string.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$", + "then": { + "field": "info.description", + "function": "truthy" + }, + "tags": [ + "api" + ] + }, + "info-license": { + "description": "AsyncAPI object info `license` must be present and non-empty string.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$", + "then": { + "field": "info.license", + "function": "truthy" + }, + "tags": [ + "api" + ] + }, + "license-url": { + "description": "License object should include `url`.", + "recommended": false, + "formats": ["aas2"], + "type": "style", + "given": "$", + "then": { + "field": "info.license.url", + "function": "truthy" + } + }, + "asyncapi-tags-alphabetical": { + "description": "AsyncAPI object should have alphabetical `tags`.", + "recommended": false, + "formats": ["aas2"], + "type": "style", + "given": "$", + "then": { + "field": "tags", + "function": "alphabetical", + "functionOptions": { + "keyedBy": "name" + } + } + }, + "operation-description": { + "description": "Operation `description` must be present and non-empty string.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]", + "then": { + "field": "description", + "function": "truthy" + }, + "tags": [ + "operation" + ] + }, + "operation-operationId": { + "description": "Operation should have an `operationId`.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]", + "then": { + "field": "operationId", + "function": "truthy" + }, + "tags": [ + "operation" + ] + }, + "path-declarations-must-exist": { + "description": "Path parameter declarations cannot be empty, ex.`/companies/{}` is invalid.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.paths", + "then": { + "field": "@key", + "function": "pattern", + "functionOptions": { + "notMatch": "{}" + } + }, + "tags": [ + "given" + ] + }, + "path-keys-no-trailing-slash": { + "description": "paths should not end with a slash.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.paths", + "then": { + "field": "@key", + "function": "pattern", + "functionOptions": { + "notMatch": ".+\\/$" + } + }, + "tags": [ + "given" + ] + }, + "path-not-include-query": { + "description": "given keys should not include a query string.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.paths", + "then": { + "field": "@key", + "function": "pattern", + "functionOptions": { + "notMatch": "\\?" + } + }, + "tags": [ + "given" + ] + }, + "tag-description": { + "description": "Tag object should have a `description`.", + "recommended": false, + "formats": ["aas2"], + "type": "style", + "given": "$.tags[*]", + "then": { + "field": "description", + "function": "truthy" + } + }, + "asyncapi-servers": { + "description": "Define at least one server.", + "recommended": true, + "formats": ["aas2"], + "given": "$", + "then": { + "field": "servers", + "function": "schema", + "functionOptions": { + "schema": { + "type": "object", + "minProperties": 1 + } + } + } + }, + "asyncapi-parameter-description": { + "description": "Parameter objects should have a `description`.", + "recommended": false, + "formats": ["aas2"], + "given": "$..['parameters']", + "then": { + "field": "description", + "function": "truthy" + }, + "tags": [ + "parameters" + ] + }, + "asyncapi-server-not-example.com": { + "description": "Server URL should not point at example.com.", + "recommended": false, + "formats": ["aas2"], + "given": "$.servers[*].url", + "then": { + "function": "pattern", + "functionOptions": { + "notMatch": "example\\.com" + } + } + }, + "asyncapi-server-trailing-slash": { + "description": "Server URL should not have a trailing slash.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "given": "$.servers[*].url", + "then": { + "function": "pattern", + "functionOptions": { + "notMatch": "/$" + } + } + }, + "asyncapi-valid-schema-example": { + "description": "Examples must be valid against their defined schema.", + "message": "{{error}}", + "severity": 0, + "recommended": false, + "formats": ["aas2"], + "type": "validation", + "given": "$.components.schemas..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]", + "then": { + "function": "schemaPath", + "functionOptions": { + "field": "example", + "schemaPath": "$" + } + } + }, + "asyncapi-schema": { + "description": "Validate structure of AsyncAPI v2.0.0 Specification.", + "message": "{{error}}", + "severity": 0, + "recommended": true, + "formats": ["aas2"], + "type": "validation", + "given": "$", + "then": { + "function": "schema", + "functionOptions": { + "schema": { + "$ref": "./schemas/asyncapi-2.0.0.json" + } + } + } + }, + "asyncapi-unused-components-schema": { + "description": "Potentially unused components schema has been detected.", + "recommended": true, + "formats": ["aas2"], + "type": "style", + "resolved": false, + "given": "$.components.schemas", + "then": { + "function": "unreferencedReusableObject", + "functionOptions": { + "reusableObjectsLocation": "#/components/schemas" + } + } + } + } + } \ No newline at end of file diff --git a/src/rulesets/aas/schemas/schema.aas2.json b/src/rulesets/aas/schemas/schema.aas2.json new file mode 100644 index 0000000000..dec4ed5af5 --- /dev/null +++ b/src/rulesets/aas/schemas/schema.aas2.json @@ -0,0 +1,1358 @@ +{ + "title": "AsyncAPI 2.0.0 schema.", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "asyncapi", + "info", + "channels" + ], + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "asyncapi": { + "type": "string", + "enum": [ + "2.0.0" + ], + "description": "The AsyncAPI specification version of this document." + }, + "id": { + "type": "string", + "description": "A unique id representing the application.", + "format": "uri" + }, + "info": { + "$ref": "#/definitions/info" + }, + "servers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/server" + } + }, + "defaultContentType": { + "type": "string" + }, + "channels": { + "$ref": "#/definitions/channels" + }, + "components": { + "$ref": "#/definitions/components" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "definitions": { + "Reference": { + "type": "object", + "required": [ + "$ref" + ], + "properties": { + "$ref": { + "$ref": "#/definitions/ReferenceObject" + } + } + }, + "ReferenceObject": { + "type": "string", + "format": "uri-reference" + }, + "info": { + "type": "object", + "description": "General information about the API.", + "required": [ + "version", + "title" + ], + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. CommonMark is allowed." + }, + "termsOfService": { + "type": "string", + "description": "A URL to the Terms of Service for the API. MUST be in the format of a URL.", + "format": "uri" + }, + "contact": { + "$ref": "#/definitions/contact" + }, + "license": { + "$ref": "#/definitions/license" + } + } + }, + "contact": { + "type": "object", + "description": "Contact information for the owners of the API.", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the contact person/organization." + }, + "url": { + "type": "string", + "description": "The URL pointing to the contact information.", + "format": "uri" + }, + "email": { + "type": "string", + "description": "The email address of the contact person/organization.", + "format": "email" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + } + }, + "license": { + "type": "object", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the license type. It's encouraged to use an OSI compatible license." + }, + "url": { + "type": "string", + "description": "The URL pointing to the license.", + "format": "uri" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + } + }, + "server": { + "type": "object", + "description": "An object representing a Server.", + "required": [ + "url", + "protocol" + ], + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "protocol": { + "type": "string", + "description": "The transfer protocol." + }, + "protocolVersion": { + "type": "string" + }, + "variables": { + "$ref": "#/definitions/serverVariables" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/definitions/SecurityRequirement" + } + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + } + } + }, + "serverVariables": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/serverVariable" + } + }, + "serverVariable": { + "type": "object", + "description": "An object representing a Server Variable for server URL template substitution.", + "minProperties": 1, + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + }, + "examples": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "channels": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri-template", + "minLength": 1 + }, + "additionalProperties": { + "$ref": "#/definitions/channelItem" + } + }, + "components": { + "type": "object", + "description": "An object to hold a set of reusable objects for different aspects of the AsyncAPI Specification.", + "additionalProperties": false, + "properties": { + "schemas": { + "$ref": "#/definitions/schemas" + }, + "messages": { + "$ref": "#/definitions/messages" + }, + "securitySchemes": { + "type": "object", + "patternProperties": { + "^[\\w\\d\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/SecurityScheme" + } + ] + } + } + }, + "parameters": { + "$ref": "#/definitions/parameters" + }, + "correlationIds": { + "type": "object", + "patternProperties": { + "^[\\w\\d\\.\\-_]+$": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/correlationId" + } + ] + } + } + }, + "operationTraits": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/operationTrait" + } + }, + "messageTraits": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/messageTrait" + } + }, + "serverBindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/bindingsObject" + } + }, + "channelBindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/bindingsObject" + } + }, + "operationBindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/bindingsObject" + } + }, + "messageBindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/bindingsObject" + } + } + } + }, + "schemas": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "description": "JSON objects describing schemas the API uses." + }, + "messages": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/message" + }, + "description": "JSON objects describing the messages being consumed and produced by the API." + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/parameter" + }, + "description": "JSON objects describing re-usable channel parameters." + }, + "schema": { + "allOf": [ + { + "$ref": "http://json-schema.org/draft-07/schema#" + }, + { + "type": "object", + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "boolean" + } + ], + "default": {} + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + } + ], + "default": {} + }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + }, + "oneOf": { + "type": "array", + "minItems": 2, + "items": { + "$ref": "#/definitions/schema" + } + }, + "anyOf": { + "type": "array", + "minItems": 2, + "items": { + "$ref": "#/definitions/schema" + } + }, + "not": { + "$ref": "#/definitions/schema" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "propertyNames": { + "$ref": "#/definitions/schema" + }, + "contains": { + "$ref": "#/definitions/schema" + }, + "discriminator": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "deprecated": { + "type": "boolean", + "default": false + } + } + } + ] + }, + "externalDocs": { + "type": "object", + "additionalProperties": false, + "description": "information about external documentation", + "required": [ + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + } + }, + "channelItem": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "minProperties": 1, + "properties": { + "$ref": { + "$ref": "#/definitions/ReferenceObject" + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/parameter" + } + }, + "description": { + "type": "string", + "description": "A description of the channel." + }, + "publish": { + "$ref": "#/definitions/operation" + }, + "subscribe": { + "$ref": "#/definitions/operation" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + } + } + }, + "parameter": { + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "schema": { + "$ref": "#/definitions/schema" + }, + "location": { + "type": "string", + "description": "A runtime expression that specifies the location of the parameter value", + "pattern": "^\\$message\\.(header|payload)\\#(\\/(([^\\/~])|(~[01]))*)*" + }, + "$ref": { + "$ref": "#/definitions/ReferenceObject" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "traits": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/operationTrait" + }, + { + "type": "array", + "items": [ + { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/operationTrait" + } + ] + }, + { + "type": "object", + "additionalItems": true + } + ] + } + ] + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "operationId": { + "type": "string" + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + }, + "message": { + "$ref": "#/definitions/message" + } + } + }, + "message": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "oneOf": [ + { + "type": "object", + "required": [ + "oneOf" + ], + "additionalProperties": false, + "properties": { + "oneOf": { + "type": "array", + "items": { + "$ref": "#/definitions/message" + } + } + } + }, + { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "schemaFormat": { + "type": "string" + }, + "contentType": { + "type": "string" + }, + "headers": { + "$ref": "#/definitions/schema" + }, + "payload": {}, + "correlationId": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/correlationId" + } + ] + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "summary": { + "type": "string", + "description": "A brief summary of the message." + }, + "name": { + "type": "string", + "description": "Name of the message." + }, + "title": { + "type": "string", + "description": "A human-friendly title for the message." + }, + "description": { + "type": "string", + "description": "A longer description of the message. CommonMark is allowed." + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": { + "type": "object" + } + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + }, + "traits": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/messageTrait" + }, + { + "type": "array", + "items": [ + { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/messageTrait" + } + ] + }, + { + "type": "object", + "additionalItems": true + } + ] + } + ] + } + } + } + } + ] + } + ] + }, + "bindingsObject": { + "type": "object", + "additionalProperties": true, + "properties": { + "http": {}, + "ws": {}, + "amqp": {}, + "amqp1": {}, + "mqtt": {}, + "mqtt5": {}, + "kafka": {}, + "nats": {}, + "jms": {}, + "sns": {}, + "sqs": {}, + "stomp": {}, + "redis": {} + } + }, + "correlationId": { + "type": "object", + "required": [ + "location" + ], + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "description": { + "type": "string", + "description": "A optional description of the correlation ID. GitHub Flavored Markdown is allowed." + }, + "location": { + "type": "string", + "description": "A runtime expression that specifies the location of the correlation ID", + "pattern": "^\\$message\\.(header|payload)\\#(\\/(([^\\/~])|(~[01]))*)*" + } + } + }, + "specificationExtension": { + "description": "Any property starting with x- is valid.", + "additionalProperties": true, + "additionalItems": true + }, + "tag": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + } + }, + "operationTrait": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "operationId": { + "type": "string" + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + } + } + }, + "messageTrait": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "properties": { + "schemaFormat": { + "type": "string" + }, + "contentType": { + "type": "string" + }, + "headers": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/schema" + } + ] + }, + "correlationId": { + "oneOf": [ + { + "$ref": "#/definitions/Reference" + }, + { + "$ref": "#/definitions/correlationId" + } + ] + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/tag" + }, + "uniqueItems": true + }, + "summary": { + "type": "string", + "description": "A brief summary of the message." + }, + "name": { + "type": "string", + "description": "Name of the message." + }, + "title": { + "type": "string", + "description": "A human-friendly title for the message." + }, + "description": { + "type": "string", + "description": "A longer description of the message. CommonMark is allowed." + }, + "externalDocs": { + "$ref": "#/definitions/externalDocs" + }, + "deprecated": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": { + "type": "object" + } + }, + "bindings": { + "$ref": "#/definitions/bindingsObject" + } + } + }, + "SecurityScheme": { + "oneOf": [ + { + "$ref": "#/definitions/userPassword" + }, + { + "$ref": "#/definitions/apiKey" + }, + { + "$ref": "#/definitions/X509" + }, + { + "$ref": "#/definitions/symmetricEncryption" + }, + { + "$ref": "#/definitions/asymmetricEncryption" + }, + { + "$ref": "#/definitions/HTTPSecurityScheme" + }, + { + "$ref": "#/definitions/oauth2Flows" + }, + { + "$ref": "#/definitions/openIdConnect" + } + ] + }, + "userPassword": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "userPassword" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "apiKey": { + "type": "object", + "required": [ + "type", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "in": { + "type": "string", + "enum": [ + "user", + "password" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "X509": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "X509" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "symmetricEncryption": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "symmetricEncryption" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "asymmetricEncryption": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "asymmetricEncryption" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "HTTPSecurityScheme": { + "oneOf": [ + { + "$ref": "#/definitions/NonBearerHTTPSecurityScheme" + }, + { + "$ref": "#/definitions/BearerHTTPSecurityScheme" + }, + { + "$ref": "#/definitions/APIKeyHTTPSecurityScheme" + } + ] + }, + "NonBearerHTTPSecurityScheme": { + "not": { + "type": "object", + "properties": { + "scheme": { + "type": "string", + "enum": [ + "bearer" + ] + } + } + }, + "type": "object", + "required": [ + "scheme", + "type" + ], + "properties": { + "scheme": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "http" + ] + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "BearerHTTPSecurityScheme": { + "type": "object", + "required": [ + "type", + "scheme" + ], + "properties": { + "scheme": { + "type": "string", + "enum": [ + "bearer" + ] + }, + "bearerFormat": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "http" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "APIKeyHTTPSecurityScheme": { + "type": "object", + "required": [ + "type", + "name", + "in" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "httpApiKey" + ] + }, + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "cookie" + ] + }, + "description": { + "type": "string" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "oauth2Flows": { + "type": "object", + "required": [ + "type", + "flows" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "description": { + "type": "string" + }, + "flows": { + "type": "object", + "properties": { + "implicit": { + "allOf": [ + { + "$ref": "#/definitions/oauth2Flow" + }, + { + "required": [ + "authorizationUrl", + "scopes" + ] + }, + { + "not": { + "required": [ + "tokenUrl" + ] + } + } + ] + }, + "password": { + "allOf": [ + { + "$ref": "#/definitions/oauth2Flow" + }, + { + "required": [ + "tokenUrl", + "scopes" + ] + }, + { + "not": { + "required": [ + "authorizationUrl" + ] + } + } + ] + }, + "clientCredentials": { + "allOf": [ + { + "$ref": "#/definitions/oauth2Flow" + }, + { + "required": [ + "tokenUrl", + "scopes" + ] + }, + { + "not": { + "required": [ + "authorizationUrl" + ] + } + } + ] + }, + "authorizationCode": { + "allOf": [ + { + "$ref": "#/definitions/oauth2Flow" + }, + { + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ] + } + ] + } + }, + "additionalProperties": false, + "minProperties": 1 + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + } + }, + "oauth2Flow": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/definitions/oauth2Scopes" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "oauth2Scopes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "openIdConnect": { + "type": "object", + "required": [ + "type", + "openIdConnectUrl" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "openIdConnect" + ] + }, + "description": { + "type": "string" + }, + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "patternProperties": { + "^x-[\\w\\d\\.\\-\\_]+$": { + "$ref": "#/definitions/specificationExtension" + } + }, + "additionalProperties": false + }, + "SecurityRequirement": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + } +} \ No newline at end of file