Skip to content

Commit

Permalink
refactor: add rule to check if AsyncAPI document is defined or not (#583
Browse files Browse the repository at this point in the history
)
  • Loading branch information
magicmatatjahu authored Sep 1, 2022
1 parent a40b685 commit 28ef37b
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 6 deletions.
6 changes: 6 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// @ts-ignore
import specs from '@asyncapi/specs';

export const xParserSpecParsed = 'x-parser-spec-parsed';
export const xParserSpecStringified = 'x-parser-spec-stringified';

Expand All @@ -11,3 +14,6 @@ export const xParserOriginalTraits = 'x-parser-original-traits';
export const xParserCircular = 'x-parser-circular';

export const EXTENSION_REGEX = /^x-[\w\d\.\-\_]+$/;

// Only >=2.0.0 versions are supported
export const specVersions = Object.keys(specs).filter((version: string) => !['1.0.0', '1.1.0', '1.2.0', '2.0.0-rc1', '2.0.0-rc2'].includes(version));
4 changes: 2 additions & 2 deletions src/schema-parser/asyncapi-schema-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Ajv from "ajv";
// @ts-ignore
import specs from '@asyncapi/specs';

import { specVersions } from '../constants';

import type { ErrorObject, ValidateFunction } from "ajv";
import type { AsyncAPISchema, SchemaValidateResult } from '../types';
import type { SchemaParser, ParseSchemaInput, ValidateSchemaInput } from "../schema-parser";
Expand All @@ -11,8 +13,6 @@ const ajv = new Ajv({
strict: false,
logger: false,
});
// Only versions compatible with JSON Schema Draf-07 are supported.
const specVersions = Object.keys(specs).filter((version: string) => !['1.0.0', '1.1.0', '1.2.0', '2.0.0-rc1', '2.0.0-rc2'].includes(version));

export function AsyncAPISchemaParser(): SchemaParser {
return {
Expand Down
2 changes: 1 addition & 1 deletion src/schema-parser/spectral-rule-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { ValidateSchemaInput } from './index';
import type { SchemaValidateResult } from '../types';
import type { v2 } from '../spec-types';

export function aas2schemaParserRule(parser: Parser): RuleDefinition {
export function asyncApi2SchemaParserRule(parser: Parser): RuleDefinition {
return {
description: 'Custom schema must be correctly formatted from the point of view of the used format.',
formats: [aas2_0, aas2_1, aas2_2, aas2_3, aas2_4],
Expand Down
48 changes: 45 additions & 3 deletions src/spectral.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { RulesetDefinition } from "@stoplight/spectral-core";
import { createRulesetFunction } from '@stoplight/spectral-core';
import { asyncapi as aasRuleset } from "@stoplight/spectral-rulesets";

import { aas2schemaParserRule } from './schema-parser/spectral-rule-v2';
import { asyncApi2SchemaParserRule } from './schema-parser/spectral-rule-v2';
import { specVersions } from './constants';
import { isObject } from './utils';

import type { RuleDefinition, RulesetDefinition } from "@stoplight/spectral-core";
import type { Parser } from "./parser";
import type { MaybeAsyncAPI } from "./types";

export function configureSpectral(parser: Parser) {
const ruleset = configureRuleset(parser);
Expand All @@ -14,11 +18,49 @@ function configureRuleset(parser: Parser): RulesetDefinition {
return {
extends: [aasRuleset],
rules: {
'asyncapi-schemas-v2': aas2schemaParserRule(parser),
'asyncapi-is-asyncapi': asyncApi2IsAsyncApi(),
'asyncapi-schemas-v2': asyncApi2SchemaParserRule(parser),
// We do not use these rules from the official ruleset due to the fact
// that the given rules validate only AsyncAPI Schemas and prevent defining schemas in other formats
'asyncapi-payload-unsupported-schemaFormat': 'off',
'asyncapi-payload': 'off',
},
} as RulesetDefinition;
}

function asyncApi2IsAsyncApi(): RuleDefinition {
return {
description: 'The input must be a document with a supported version of AsyncAPI.',
formats: [(_: unknown) => true], // run rule for all inputs
message: '{{error}}',
severity: 'error',
type: 'validation',
recommended: true,
given: '$',
then: {
function: createRulesetFunction<MaybeAsyncAPI, null>(
{
input: null,
options: null,
},
function asyncApi2IsAsyncAPI(targetVal) {
if (!isObject(targetVal) || typeof targetVal.asyncapi !== 'string') {
return [
{
message: 'This is not an AsyncAPI document. The "asyncapi" field as string is missing.',
path: [],
}
];
} else if (!specVersions.includes(targetVal.asyncapi)) {
return [
{
message: `Version "${targetVal.asyncapi}" is not supported. Please use "${specVersions[specVersions.length - 1]}" (latest) version of the specification.`,
path: [],
}
];
}
}
),
},
}
}
71 changes: 71 additions & 0 deletions test/spectral.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Parser } from '../src/parser';
import { validate } from '../src/lint';

import { specVersions } from '../src/constants';

import type { ISpectralDiagnostic } from '@stoplight/spectral-core';
import type { SchemaValidateResult } from '../src/types';

describe('Custom Spectral instance', function() {
const parser = new Parser();

describe('asyncapi-is-asyncapi Spectral rule', function() {
it('should throw error when input is not an AsyncAPI document (empty input case)', async function() {
const { diagnostics } = await validate(parser, '');

expect(diagnostics.length > 0).toEqual(true);
const filteredDiagnostics = filterDiagnostics(diagnostics, 'asyncapi-is-asyncapi');

const expectedResult: SchemaValidateResult[] = [
{
message: 'This is not an AsyncAPI document. The "asyncapi" field as string is missing.',
path: []
},
];

expect(filteredDiagnostics).toEqual(expectedResult.map(e => expect.objectContaining(e)));
});

it('should throw error when input is not an AsyncAPI document (another spec case)', async function() {
const document = {
openapi: '3.0.0',
}
const { diagnostics } = await validate(parser, document as any);

expect(diagnostics.length > 0).toEqual(true);
const filteredDiagnostics = filterDiagnostics(diagnostics, 'asyncapi-is-asyncapi');

const expectedResult: SchemaValidateResult[] = [
{
message: 'This is not an AsyncAPI document. The "asyncapi" field as string is missing.',
path: []
},
];

expect(filteredDiagnostics).toEqual(expectedResult.map(e => expect.objectContaining(e)));
});

it('should throw error when input is an unsupported version of AsyncAPI', async function() {
const document = {
asyncapi: '2.1.37',
}
const { diagnostics } = await validate(parser, document as any);

expect(diagnostics.length > 0).toEqual(true);
const filteredDiagnostics = filterDiagnostics(diagnostics, 'asyncapi-is-asyncapi');

const expectedResult: SchemaValidateResult[] = [
{
message: `Version "2.1.37" is not supported. Please use "${specVersions[specVersions.length - 1]}" (latest) version of the specification.`,
path: []
},
];

expect(filteredDiagnostics).toEqual(expectedResult.map(e => expect.objectContaining(e)));
});
});
});

function filterDiagnostics(diagnostics: ISpectralDiagnostic[], code: string) {
return diagnostics.filter(d => d.code === code);
}

0 comments on commit 28ef37b

Please sign in to comment.