diff --git a/.changeset/rich-elephants-applaud.md b/.changeset/rich-elephants-applaud.md new file mode 100644 index 000000000..4ec4ba2ac --- /dev/null +++ b/.changeset/rich-elephants-applaud.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/parser": minor +--- + +feat: create the rule `asyncapi3-channel-servers` for the v3 rule core ruleset diff --git a/packages/parser/src/ruleset/v2/functions/channelServers.ts b/packages/parser/src/ruleset/functions/channelServers.ts similarity index 100% rename from packages/parser/src/ruleset/v2/functions/channelServers.ts rename to packages/parser/src/ruleset/functions/channelServers.ts diff --git a/packages/parser/src/ruleset/v2/ruleset.ts b/packages/parser/src/ruleset/v2/ruleset.ts index f5e0d3274..b03a7549f 100644 --- a/packages/parser/src/ruleset/v2/ruleset.ts +++ b/packages/parser/src/ruleset/v2/ruleset.ts @@ -4,7 +4,7 @@ import { AsyncAPIFormats } from '../formats'; import { truthy, pattern } from '@stoplight/spectral-functions'; import { channelParameters } from './functions/channelParameters'; -import { channelServers } from './functions/channelServers'; +import { channelServers } from '../functions/channelServers'; import { checkId } from './functions/checkId'; import { messageExamples } from './functions/messageExamples'; import { asyncApi2MessageExamplesParserRule } from './functions/messageExamples-spectral-rule-v2'; diff --git a/packages/parser/src/ruleset/v3/ruleset.ts b/packages/parser/src/ruleset/v3/ruleset.ts index 62e7ecb51..b85187d22 100644 --- a/packages/parser/src/ruleset/v3/ruleset.ts +++ b/packages/parser/src/ruleset/v3/ruleset.ts @@ -3,6 +3,7 @@ import { AsyncAPIFormats } from '../formats'; import { operationMessagesUnambiguity } from './functions/operationMessagesUnambiguity'; import { pattern } from '@stoplight/spectral-functions'; +import { channelServers } from '../functions/channelServers'; export const v3CoreRuleset = { description: 'Core AsyncAPI 3.x.x ruleset.', @@ -57,6 +58,16 @@ export const v3CoreRuleset = { }, }, }, + 'asyncapi3-channel-servers': { + description: 'Channel servers must be defined in the "servers" object.', + message: '{{error}}', + severity: 'error', + recommended: true, + given: '$', + then: { + function: channelServers, + }, + }, 'asyncapi3-channel-no-query-nor-fragment': { description: 'Channel address should not include query ("?") or fragment ("#") delimiter.', severity: 'error', diff --git a/packages/parser/test/ruleset/rules/v3/asyncapi3-channel-servers.spec.ts b/packages/parser/test/ruleset/rules/v3/asyncapi3-channel-servers.spec.ts new file mode 100644 index 000000000..e567132b1 --- /dev/null +++ b/packages/parser/test/ruleset/rules/v3/asyncapi3-channel-servers.spec.ts @@ -0,0 +1,140 @@ +import { testRule, DiagnosticSeverity } from '../../tester'; + +testRule('asyncapi3-channel-servers', [ + { + name: 'valid case', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + channels: { + channel: { + servers: ['development'], + }, + }, + }, + errors: [], + }, + + { + name: 'valid case - without defined servers', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + channels: { + channel: {}, + }, + }, + errors: [], + }, + + { + name: 'valid case - without defined servers in the root', + document: { + asyncapi: '3.0.0', + channels: { + channel: {}, + }, + }, + errors: [], + }, + + { + name: 'valid case - without defined channels in the root', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + }, + errors: [], + }, + + { + name: 'valid case - with empty array', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + channels: { + channel: { + servers: [], + }, + }, + }, + errors: [], + }, + + { + name: 'invalid case', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + channels: { + channel: { + servers: ['another-server'], + }, + }, + }, + errors: [ + { + message: 'Channel contains server that are not defined on the "servers" object.', + path: ['channels', 'channel', 'servers', '0'], + severity: DiagnosticSeverity.Error, + }, + ], + }, + + { + name: 'invalid case - one server is defined, another one not', + document: { + asyncapi: '3.0.0', + servers: { + development: {}, + production: {}, + }, + channels: { + channel: { + servers: ['production', 'another-server'], + }, + }, + }, + errors: [ + { + message: 'Channel contains server that are not defined on the "servers" object.', + path: ['channels', 'channel', 'servers', '1'], + severity: DiagnosticSeverity.Error, + }, + ], + }, + + { + name: 'invalid case - without defined servers', + document: { + asyncapi: '3.0.0', + channels: { + channel: { + servers: ['production'], + }, + }, + }, + errors: [ + { + message: 'Channel contains server that are not defined on the "servers" object.', + path: ['channels', 'channel', 'servers', '0'], + severity: DiagnosticSeverity.Error, + }, + ], + }, +]);