Skip to content

Commit a0d49d4

Browse files
committed
Add additional formatter support to the validate subcommand
1 parent 9414846 commit a0d49d4

File tree

3 files changed

+52
-21
lines changed

3 files changed

+52
-21
lines changed

src/domains/services/validation.service.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import { OpenAPISchemaParser } from '@asyncapi/openapi-schema-parser';
1212
import { DiagnosticSeverity, Parser } from '@asyncapi/parser/cjs';
1313
import { RamlDTSchemaParser } from '@asyncapi/raml-dt-schema-parser';
1414
import { ProtoBuffSchemaParser } from '@asyncapi/protobuf-schema-parser';
15-
import { getDiagnosticSeverity } from '@stoplight/spectral-core';
15+
import { OutputFormat } from '@stoplight/spectral-cli/dist/services/config';
16+
import { version } from '@stoplight/spectral-cli/package.json';
17+
import { getDiagnosticSeverity, Ruleset } from '@stoplight/spectral-core';
1618
import {
1719
html,
1820
json,
@@ -21,6 +23,10 @@ import {
2123
stylish,
2224
teamcity,
2325
text,
26+
githubActions,
27+
sarif,
28+
codeClimate,
29+
markdown
2430
} from '@stoplight/spectral-formatters';
2531
import chalk from 'chalk';
2632
import { promises } from 'fs';
@@ -187,6 +193,11 @@ const formatExtensions: Record<DiagnosticsFormat, string> = {
187193
text: '.txt',
188194
teamcity: '.txt',
189195
pretty: '.txt',
196+
'github-actions': '.txt',
197+
sarif: '.json',
198+
'code-climate': '.json',
199+
gitlab: '.json',
200+
markdown: '.md',
190201
};
191202

192203
const validFormats = [
@@ -197,13 +208,18 @@ const validFormats = [
197208
'text',
198209
'teamcity',
199210
'pretty',
211+
'github-actions',
212+
'sarif',
213+
'code-climate',
214+
'gitlab',
215+
'markdown',
200216
];
201217

202218
export class ValidationService extends BaseService {
203219
private parser: Parser;
204220

205221
constructor(parserOptions: ParserOptions = {}) {
206-
super();
222+
super();
207223
// Create parser with custom GitHub resolver
208224
const customParserOptions = {
209225
...parserOptions,
@@ -498,22 +514,31 @@ export class ValidationService extends BaseService {
498514
};
499515

500516
switch (format) {
501-
case 'stylish':
517+
case OutputFormat.STYLISH:
502518
return this.formatStylish(diagnostics, options);
503-
case 'json':
519+
case OutputFormat.JSON:
504520
return json(diagnostics, options);
505-
case 'junit':
521+
case OutputFormat.JUNIT:
506522
return junit(diagnostics, options);
507-
case 'html':
523+
case OutputFormat.HTML:
508524
return html(diagnostics, options);
509-
case 'text':
525+
case OutputFormat.TEXT:
510526
return text(diagnostics, options);
511-
case 'teamcity':
527+
case OutputFormat.TEAMCITY:
512528
return teamcity(diagnostics, options);
513-
case 'pretty':
529+
case OutputFormat.PRETTY:
514530
return pretty(diagnostics, options);
531+
case OutputFormat.GITHUB_ACTIONS:
532+
return githubActions(diagnostics, options);
533+
case OutputFormat.SARIF:
534+
return sarif(diagnostics, options, { ruleset: new Ruleset({rules: {}}), spectralVersion: version });
535+
case OutputFormat.CODE_CLIMATE:
536+
case OutputFormat.GITLAB:
537+
return codeClimate(diagnostics, options);
538+
case OutputFormat.MARKDOWN:
539+
return markdown(diagnostics, options);
515540
default:
516-
return stylish(diagnostics, options);
541+
return this.formatStylish(diagnostics, options);
517542
}
518543
}
519544

src/interfaces/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ export type DiagnosticsFormat =
1111
| 'html'
1212
| 'text'
1313
| 'teamcity'
14-
| 'pretty';
14+
| 'pretty'
15+
| 'github-actions'
16+
| 'sarif'
17+
| 'code-climate'
18+
| 'gitlab'
19+
| 'markdown'
20+
;
1521

1622
/**
1723
* Severity levels for validation diagnostics.

test/unit/services/validation.service.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('ValidationService', () => {
104104
expect(result.data).to.have.property('status');
105105
expect(result.data).to.have.property('diagnostics');
106106
expect(result.data?.diagnostics).to.be.an('array');
107-
107+
108108
// Should have diagnostics for missing required fields
109109
if (result.data?.diagnostics && result.data.diagnostics.length > 0) {
110110
expect(result.data.diagnostics.some((d: any) => d.message)).to.equal(true);
@@ -119,7 +119,7 @@ describe('ValidationService', () => {
119119
};
120120

121121
const result = await validationService.validateDocument(specFile, options);
122-
122+
123123
expect(result.success).to.equal(true);
124124
if (result.success) {
125125
expect(result.data).to.have.property('diagnostics');
@@ -129,12 +129,12 @@ describe('ValidationService', () => {
129129

130130
it('should handle different output formats', async () => {
131131
const specFile = new Specification(validAsyncAPI);
132-
const formats = ['json', 'junit', 'html', 'text', 'teamcity', 'pretty'] as const;
132+
const formats = ['json', 'junit', 'html', 'text', 'teamcity', 'pretty', 'github-actions', 'sarif', 'code-climate', 'gitlab', 'markdown'] as const;
133133

134134
for (const format of formats) {
135135
const options = { 'diagnostics-format': format };
136136
const result = await validationService.validateDocument(specFile, options);
137-
137+
138138
expect(result.success).to.equal(true);
139139
if (result.success) {
140140
expect(result.data).to.have.property('diagnostics');
@@ -216,21 +216,21 @@ describe('ValidationService', () => {
216216

217217
const result = await validationService.validateDocument(specFile, options);
218218

219-
// The validation succeeds means the validation command is successfully executed it is independent whether
220-
// the document is valid or not
219+
// The validation succeeds means the validation command is successfully executed it is independent whether
220+
// the document is valid or not
221221
expect(result.success).to.equal(true);
222222
if (result.success) {
223223
expect(result.data).to.have.property('status');
224224
expect(result.data?.status).to.equal('invalid');
225225
expect(result.data).to.have.property('diagnostics');
226226
expect(result.data?.diagnostics).to.be.an('array');
227-
227+
228228
// Should have an invalid-ref diagnostic for the private GitHub URL
229229
const invalidRefDiagnostic = result.data?.diagnostics?.find((d: any) => d.code === 'invalid-ref');
230230
// eslint-disable-next-line no-unused-expressions
231231
expect(invalidRefDiagnostic).to.exist;
232232
// Error message varies by platform - macOS shows FetchError, Linux/Windows show "Page not found"
233-
expect(invalidRefDiagnostic?.message).to.satisfy((msg: string) =>
233+
expect(invalidRefDiagnostic?.message).to.satisfy((msg: string) =>
234234
msg.includes('Page not found') || msg.includes('FetchError')
235235
);
236236
expect(invalidRefDiagnostic?.message).to.include('https://github.com/private-org/private-repo/blob/main/schema.yaml');
@@ -244,8 +244,8 @@ describe('ValidationService', () => {
244244
};
245245

246246
const result = await validationService.validateDocument(specFile, options);
247-
// The validation succeeds means the validation command is successfully executed it is independent whether
248-
// the document is valid or not
247+
// The validation succeeds means the validation command is successfully executed it is independent whether
248+
// the document is valid or not
249249
expect(result.success).to.equal(true);
250250
if (result.success) {
251251
expect(result.data).to.have.property('status');

0 commit comments

Comments
 (0)