Skip to content

Commit

Permalink
feat: create rule for v3 core ruleset
Browse files Browse the repository at this point in the history
  • Loading branch information
dishafaujdar committed Jan 4, 2025
1 parent 8710f53 commit e552578
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/rich-elephants-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@asyncapi/parser": minor
---

feat: create the rule `asyncapi3-channel-servers` for the v3 rule core ruleset
54 changes: 54 additions & 0 deletions packages/parser/src/ruleset/v3/functions/channelServers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { createRulesetFunction } from '@stoplight/spectral-core';

import type { IFunctionResult } from '@stoplight/spectral-core';

export const channelServers = createRulesetFunction<
{ servers?: Record<string, unknown>; channels?: Record<string, { servers?: Array<string> }> },
null
>(
{
input: {
type: 'object',
properties: {
servers: {
type: 'object',
},
channels: {
type: 'object',
additionalProperties: {
type: 'object',
properties: {
servers: {
type: 'array',
items: {
type: 'string',
},
},
},
},
},
},
},
options: null,
},
(targetVal) => {
const results: IFunctionResult[] = [];
if (!targetVal.channels) return results;
const serverNames = Object.keys(targetVal.servers ?? {});

Object.entries(targetVal.channels ?? {}).forEach(([channelAddress, channel]) => {
if (!channel.servers) return;

channel.servers.forEach((serverName, index) => {
if (!serverNames.includes(serverName)) {
results.push({
message: 'Channel contains server that are not defined on the "servers" object.',
path: ['channels', channelAddress, 'servers', index],
});
}
});
});

return results;
},
);
11 changes: 11 additions & 0 deletions packages/parser/src/ruleset/v3/ruleset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
},
],
},
]);

0 comments on commit e552578

Please sign in to comment.