Skip to content

feat: add new feature to supress the warnings #1805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 21, 2025
17 changes: 17 additions & 0 deletions .changeset/1805.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@asyncapi/cli': minor
---

feat: add new feature to supress the warnings

- ab94c15: Add a new flag that is --x-suppress-warnings to supress the warning in the validate command
- 55819cd: Allow to pass multiple warnings to supress and add check to verify the warning is correct or not
- 885fc71: Update src/core/parser.ts

Co-authored-by: Souvik De <souvikde.ns@gmail.com>
- 16b22de: Update src/core/flags/validate.flags.ts

Co-authored-by: Souvik De <souvikde.ns@gmail.com>
- de1caad: Add another flag to supressallwarnings and update test case


39 changes: 24 additions & 15 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -792,33 +792,42 @@ _See code: [src/commands/start/preview.ts](https://github.com/asyncapi/cli/blob/

validate asyncapi file

```
USAGE
$ asyncapi validate [SPEC-FILE] [-h] [-w] [--log-diagnostics] [--diagnostics-format
json|stylish|junit|html|text|teamcity|pretty] [--fail-severity error|warn|info|hint] [-o <value>] [--score]
[--proxyHost <value>] [--proxyPort <value>]
[--proxyHost <value>] [--proxyPort <value>] [--suppressWarnings <value>...] [--suppressAllWarnings]

ARGUMENTS
SPEC-FILE spec path, url, or context-name

FLAGS
-h, --help Show CLI help.
-o, --output=<value> The output file name. Omitting this flag the result will be printed in the console.
-w, --watch Enable watch mode
--diagnostics-format=<option> [default: stylish] format to use for validation diagnostics
-o, --output=<value> The output file name. Omitting this flag will print the result to the console.
-w, --watch Enable watch mode.
--diagnostics-format=<option> [default: stylish] Format to use for validation diagnostics.
<options: json|stylish|junit|html|text|teamcity|pretty>
--fail-severity=<option> [default: error] diagnostics of this level or above will trigger a failure exit
code
--fail-severity=<option> [default: error] Diagnostics of this level or above will trigger a failure exit
code.
<options: error|warn|info|hint>
--[no-]log-diagnostics log validation diagnostics or not
--proxyHost=<value> Name of the ProxyHost
--proxyPort=<value> Port number number for the proxyHost.
--[no-]log-diagnostics Log validation diagnostics or not.
--proxyHost=<value> Name of the proxy host.
--proxyPort=<value> Port number for the proxy host.
--score Compute the score of the AsyncAPI document. Scoring is based on whether the
document has description, license, server and/or channels.
document has description, license, server, and/or channels.
--suppressWarnings=<value> One or more warning codes to suppress from the diagnostics output. Can be passed
multiple times.
--suppressAllWarnings Suppress all warnings from the diagnostics output, regardless of their code.

DESCRIPTION
validate asyncapi file
```
Validate an AsyncAPI file.

EXAMPLES
$ asyncapi validate ./asyncapi.yml
$ asyncapi validate ./asyncapi.yml --suppressWarnings asyncapi-id asyncapi-info-contact
You need to pass the ID(s) of the warning(s) that you want to suppress during validation.
$ asyncapi validate ./asyncapi.yml --suppressAllWarnings
Use this flag to completely suppress all warnings from the validation output.


_See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.16.4/src/commands/validate.ts)_
<!-- commandsstop -->
See code: [src/commands/validate.ts](https://github.com/asyncapi/cli/blob/v2.16.4/src/commands/validate.ts)_
<!-- commandsstop -->
18 changes: 15 additions & 3 deletions src/commands/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,40 @@ export default class Validate extends Command {
};

static args = {
'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}),
'spec-file': Args.string({ description: 'spec path, url, or context-name', required: false }),
};

async run() {
const { args, flags } = await this.parse(Validate); //NOSONAR
let filePath = args['spec-file'];
const proxyHost = flags['proxyHost'];
const proxyPort = flags['proxyPort'];

if (proxyHost && proxyPort) {
const proxyUrl = `http://${proxyHost}:${proxyPort}`;
filePath = `${filePath}+${proxyUrl}`; // Update filePath with proxyUrl
}

this.specFile = await load(filePath);
const watchMode = flags.watch;

if (flags['score']) {
const { document } = await parse(this,this.specFile);
const { document } = await parse(this, this.specFile);
this.log(`The score of the asyncapi document is ${await calculateScore(document)}`);
}

if (watchMode) {
specWatcher({ spec: this.specFile, handler: this, handlerName: 'validate' });
}
const result = await validate(this, this.specFile, flags as ValidateOptions);

// Prepare validate options
const validateOptions: ValidateOptions = {
...flags,
suppressWarnings: flags['suppressWarnings'],
suppressAllWarnings: flags['suppressAllWarnings'],
};

const result = await validate(this, this.specFile, validateOptions);
this.metricsMetadata.validation_result = result;

if (result === ValidationStatus.INVALID) {
Expand Down
14 changes: 12 additions & 2 deletions src/core/flags/validate.flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@ export const validateFlags = () => {
score: Flags.boolean({
description: 'Compute the score of the AsyncAPI document. Scoring is based on whether the document has description, license, server and/or channels.',
required: false,
default: false
})
default: false,
}),
suppressWarnings: Flags.string({
description: 'List of warning codes to suppress from the validation output.',
required: false,
multiple: true,
}),
suppressAllWarnings: Flags.boolean({
description: 'Suppress all warnings from the validation output.',
required: false,
default: false,
}),
};
};
70 changes: 67 additions & 3 deletions src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
resolver: {
cache: false,
}
}
},

});

parser.registerSchemaParser(AvroSchemaParser());
Expand Down Expand Up @@ -86,10 +87,73 @@
'diagnostics-format'?: `${OutputFormat}`;
'fail-severity'?: SeverityKind;
'output'?: string;
suppressWarnings?: string[];
suppressAllWarnings?: boolean;
}

export async function validate(command: Command, specFile: Specification, options: ValidateOptions = {}) {
const diagnostics = await parser.validate(specFile.text(), { source: specFile.getSource() });
export async function validate(

Check warning on line 94 in src/core/parser.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed
command: Command,
specFile: Specification,
options: ValidateOptions = {}
) {
const suppressAllWarnings = options.suppressAllWarnings ?? false;
const suppressedWarnings = options.suppressWarnings ?? [];
let activeParser: Parser;

// Helper to build a parser with given rules turned off
const buildCustomParser = (rulesToSuppress: string[]) =>
new Parser({
ruleset: {
extends: [],
rules: Object.fromEntries(rulesToSuppress.map(rule => [rule, 'off'])),
},
__unstable: {
resolver: {
cache: false,
},
},
});

if (suppressAllWarnings) {
// Run the default parser to discover all rule codes
const diagnostics = await parser.validate(specFile.text(), {
source: specFile.getSource(),
});
const allRuleNames = Array.from(
new Set(diagnostics.map(d => d.code).filter((c): c is string => typeof c === 'string'))
);
activeParser = buildCustomParser(allRuleNames);
} else if (suppressedWarnings.length === 0) {
activeParser = parser;
} else {
try {
activeParser = buildCustomParser(suppressedWarnings);
} catch (e: any) {
const msg = e.message || '';
const matches = [...msg.matchAll(/Cannot extend non-existing rule: "([^"]+)"/g)];
const invalidRules = matches.map(m => m[1]);
if (invalidRules.length > 0) {
for (const rule of invalidRules) {
command.log(`Warning: '${rule}' is not a known rule and will be ignored.`);
}
const validRules = suppressedWarnings.filter(rule => !invalidRules.includes(rule));
activeParser = buildCustomParser(validRules);
} else {
throw e;
}
}
}

// Register schema parsers
activeParser.registerSchemaParser(AvroSchemaParser());
activeParser.registerSchemaParser(OpenAPISchemaParser());
activeParser.registerSchemaParser(RamlDTSchemaParser());
activeParser.registerSchemaParser(ProtoBuffSchemaParser());

const diagnostics = await activeParser.validate(specFile.text(), {
source: specFile.getSource(),
});

return logDiagnostics(diagnostics, command, specFile, options);
}

Expand Down
79 changes: 79 additions & 0 deletions test/integration/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,83 @@ describe('validate', () => {
done();
});
});

describe('validate command and suppression of the single warning', () => {
test
.stdout()
.command([
'validate',
path.join('test', 'fixtures', 'asyncapi_v1.yml'),
'--suppressWarnings',
'asyncapi-id'
])
.it('should suppress specified warnings and still validate correctly', (ctx, done) => {
expect(ctx.stdout).to.include('asyncapi_v1.yml');
expect(ctx.stdout).to.match(/is valid/i); // General validity check
expect(ctx.stdout).to.not.include('asyncapi-id'); // Ensure warning is suppressed
done();
});
});
describe('validate command and suppression of multiple warnings', () => {
test
.stdout()
.command([
'validate',
path.join('test', 'fixtures', 'asyncapi_v1.yml'),
'--suppressWarnings',
'asyncapi-id',
'suppressWarnings',
'asyncapi2-tags'
])
.it('should suppress multiple specified warnings and still validate correctly', (ctx, done) => {
expect(ctx.stdout).to.not.include('asyncapi-id'); // Suppressed warning #1
expect(ctx.stdout).to.not.include('asyncapi2-tags'); // Suppressed warning #2
done();
});
});

describe('validate command without suppression', () => {
test
.stdout()
.command([
'validate',
path.join('test', 'fixtures', 'asyncapi_v1.yml'),
])
.it('should include the asyncapi-id warning when not suppressed', (ctx, done) => {
expect(ctx.stdout).to.include('asyncapi-id'); // Should show up if not suppressed
done();
});
});
describe('validate command with an invalid suppression rule', () => {
test
.stdout()
.command([
'validate',
path.join('test', 'fixtures', 'asyncapi_v1.yml'),
'--suppressWarnings',
'non-existing-rule'
])
.it('should warn about the unknown rule and not suppress anything', (ctx, done) => {
expect(ctx.stdout).to.contains('Warning: \'non-existing-rule\' is not a known rule and will be ignored.');
expect(ctx.stdout).to.include('asyncapi-id');
done();
});
});
describe('validate command with mixed valid and invalid suppressed warnings', () => {
test
.stdout()
.command([
'validate',
path.join('test', 'fixtures', 'asyncapi_v1.yml'),
'--suppressWarnings',
'asyncapi-id',
'--suppressWarnings',
'foobar'
])
.it('should suppress valid rules and warn about invalid ones', (ctx, done) => {
expect(ctx.stdout).to.not.include('asyncapi-id');
expect(ctx.stdout).to.contains('Warning: \'foobar\' is not a known rule');
done();
});
});
});
Loading