-
-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: init parse, lint and validate functions (#487)
- Loading branch information
1 parent
f08ad28
commit 136a632
Showing
11 changed files
with
401 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
export * from './models'; | ||
|
||
export { lint, validate } from './lint'; | ||
export { parse } from './parse'; | ||
export { stringify, unstringify } from './stringify'; | ||
|
||
export type { LintOptions, ValidateOptions, ValidateOutput } from './lint'; | ||
export type { StringifyOptions } from './stringify'; | ||
export type { ParseOptions } from './parse'; | ||
export type { ParserInput, ParserOutput, Diagnostic } from './types'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { | ||
IConstructorOpts, | ||
IRunOpts, | ||
Spectral, | ||
Ruleset, | ||
RulesetDefinition, | ||
} from "@stoplight/spectral-core"; | ||
import { asyncapi as aasRuleset } from "@stoplight/spectral-rulesets"; | ||
|
||
import { toAsyncAPIDocument, normalizeInput, hasWarningDiagnostic, hasErrorDiagnostic } from "./utils"; | ||
|
||
import type { AsyncAPIDocument } from "./models/asyncapi"; | ||
import type { ParserInput, Diagnostic } from "./types"; | ||
|
||
export interface LintOptions extends IConstructorOpts, IRunOpts { | ||
ruleset?: RulesetDefinition | Ruleset; | ||
} | ||
|
||
export interface ValidateOptions extends LintOptions { | ||
allowedSeverity?: { | ||
warning?: boolean; | ||
}; | ||
} | ||
|
||
export interface ValidateOutput { | ||
validated: unknown; | ||
diagnostics: Diagnostic[]; | ||
} | ||
|
||
export async function lint(asyncapi: ParserInput, options?: LintOptions): Promise<Diagnostic[] | undefined> { | ||
if (toAsyncAPIDocument(asyncapi)) { | ||
return; | ||
} | ||
const document = normalizeInput(asyncapi as Exclude<ParserInput, AsyncAPIDocument>); | ||
return (await validate(document, options)).diagnostics; | ||
} | ||
|
||
export async function validate(asyncapi: string, options?: ValidateOptions): Promise<ValidateOutput> { | ||
const { ruleset, allowedSeverity, ...restOptions } = normalizeOptions(options); | ||
const spectral = new Spectral(restOptions); | ||
|
||
spectral.setRuleset(ruleset!); | ||
let { resolved, results } = await spectral.runWithResolved(asyncapi); | ||
|
||
if ( | ||
hasErrorDiagnostic(results) || | ||
(!allowedSeverity?.warning && hasWarningDiagnostic(results)) | ||
) { | ||
resolved = undefined; | ||
} | ||
|
||
return { validated: resolved, diagnostics: results }; | ||
} | ||
|
||
const defaultOptions: ValidateOptions = { | ||
// TODO: fix that type | ||
ruleset: aasRuleset as any, | ||
allowedSeverity: { | ||
warning: true, | ||
} | ||
}; | ||
function normalizeOptions(options?: ValidateOptions): ValidateOptions { | ||
if (!options || typeof options !== 'object') { | ||
return defaultOptions; | ||
} | ||
// shall copy | ||
options = { ...defaultOptions, ...options }; | ||
|
||
// severity | ||
options.allowedSeverity = { ...defaultOptions.allowedSeverity, ...(options.allowedSeverity || {}) }; | ||
|
||
return options; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { AsyncAPIDocument } from "./models"; | ||
import { normalizeInput, toAsyncAPIDocument } from "./utils"; | ||
import { validate } from "./lint"; | ||
|
||
import type { ParserInput, ParserOutput } from './types'; | ||
import type { ValidateOptions } from './lint'; | ||
|
||
export interface ParseOptions { | ||
applyTraits?: boolean; | ||
validateOptions?: ValidateOptions; | ||
} | ||
|
||
export async function parse(asyncapi: ParserInput, options?: ParseOptions): Promise<ParserOutput> { | ||
let maybeDocument = toAsyncAPIDocument(asyncapi); | ||
if (maybeDocument) { | ||
return { | ||
source: asyncapi, | ||
parsed: maybeDocument, | ||
diagnostics: [], | ||
}; | ||
} | ||
|
||
try { | ||
const document = normalizeInput(asyncapi as Exclude<ParserInput, AsyncAPIDocument>); | ||
options = normalizeOptions(options); | ||
|
||
const { validated, diagnostics } = await validate(document, options.validateOptions); | ||
if (validated === undefined) { | ||
return { | ||
source: asyncapi, | ||
parsed: undefined, | ||
diagnostics, | ||
}; | ||
} | ||
|
||
const parsed = new AsyncAPIDocument(validated as Record<string, unknown>); | ||
return { | ||
source: asyncapi, | ||
parsed, | ||
diagnostics, | ||
}; | ||
} catch(err) { | ||
// TODO: throw proper error | ||
throw Error(); | ||
} | ||
} | ||
|
||
const defaultOptions: ParseOptions = { | ||
applyTraits: true, | ||
}; | ||
function normalizeOptions(options?: ParseOptions): ParseOptions { | ||
if (!options || typeof options !== 'object') { | ||
return defaultOptions; | ||
} | ||
// shall copy | ||
options = { ...defaultOptions, ...options }; | ||
|
||
// traits | ||
if (options.applyTraits === undefined) { | ||
options.applyTraits = true; | ||
} | ||
|
||
return options; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { ISpectralDiagnostic } from '@stoplight/spectral-core'; | ||
import type { AsyncAPIDocument } from './models/asyncapi'; | ||
|
||
export type MaybeAsyncAPI = { asyncapi: unknown } & Record<string, unknown>; | ||
export type ParserInput = string | MaybeAsyncAPI | AsyncAPIDocument; | ||
|
||
export type Diagnostic = ISpectralDiagnostic; | ||
|
||
export interface ParserOutput { | ||
source: ParserInput; | ||
parsed: AsyncAPIDocument | undefined; | ||
diagnostics: Diagnostic[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { lint, validate } from '../src/lint'; | ||
import { hasErrorDiagnostic, hasWarningDiagnostic } from '../src/utils'; | ||
|
||
describe('lint() & validate()', function() { | ||
describe('lint()', function() { | ||
it('should lint invalid document', async function() { | ||
const document = { | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
} | ||
|
||
const diagnostics = await lint(document); | ||
if (!diagnostics) { | ||
return; | ||
} | ||
|
||
expect(diagnostics.length > 0).toEqual(true); | ||
expect(hasErrorDiagnostic(diagnostics)).toEqual(true); | ||
expect(hasWarningDiagnostic(diagnostics)).toEqual(true); | ||
}); | ||
|
||
it('should lint valid document', async function() { | ||
const document = { | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
channels: {} | ||
} | ||
|
||
const diagnostics = await lint(document); | ||
if (!diagnostics) { | ||
return; | ||
} | ||
|
||
expect(diagnostics.length > 0).toEqual(true); | ||
expect(hasErrorDiagnostic(diagnostics)).toEqual(false); | ||
expect(hasWarningDiagnostic(diagnostics)).toEqual(true); | ||
}); | ||
}); | ||
|
||
describe('validate()', function() { | ||
it('should validate invalid document', async function() { | ||
const document = JSON.stringify({ | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
}, undefined, 2); | ||
const { validated, diagnostics } = await validate(document); | ||
|
||
expect(validated).toBeUndefined(); | ||
expect(diagnostics.length > 0).toEqual(true); | ||
}); | ||
|
||
it('should validate valid document', async function() { | ||
const document = JSON.stringify({ | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
channels: {} | ||
}, undefined, 2); | ||
const { validated, diagnostics } = await validate(document); | ||
|
||
expect(validated).not.toBeUndefined(); | ||
expect(diagnostics.length > 0).toEqual(true); | ||
}); | ||
|
||
it('should validate valid document - do not allow warning severity', async function() { | ||
const document = JSON.stringify({ | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
channels: {} | ||
}, undefined, 2); | ||
const { validated, diagnostics } = await validate(document, { allowedSeverity: { warning: false } }); | ||
|
||
expect(validated).toBeUndefined(); | ||
expect(diagnostics.length > 0).toEqual(true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { AsyncAPIDocument } from '../src/models/asyncapi'; | ||
import { parse } from '../src/parse'; | ||
|
||
describe('parse()', function() { | ||
it('should parse valid document', async function() { | ||
const document = { | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
channels: {} | ||
} | ||
const { parsed, diagnostics } = await parse(document); | ||
|
||
expect(parsed).toBeInstanceOf(AsyncAPIDocument); | ||
expect(diagnostics.length > 0).toEqual(true); | ||
}); | ||
|
||
it('should parse invalid document', async function() { | ||
const document = { | ||
asyncapi: '2.0.0', | ||
info: { | ||
title: 'Valid AsyncApi document', | ||
version: '1.0', | ||
}, | ||
} | ||
const { parsed, diagnostics } = await parse(document); | ||
|
||
expect(parsed).toEqual(undefined); | ||
expect(diagnostics.length > 0).toEqual(true); | ||
}); | ||
}); |
Oops, something went wrong.