-
-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathparse.ts
More file actions
125 lines (109 loc) · 4.89 KB
/
Copy pathparse.ts
File metadata and controls
125 lines (109 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { AsyncAPIDocumentInterface, ParserAPIVersion } from './models';
import { applyUniqueIds, customOperations } from './custom-operations';
import { validate } from './validate';
import { copy } from './stringify';
import { createAsyncAPIDocument } from './document';
import { createDetailedAsyncAPI, mergePatch, setExtension, createUncaghtDiagnostic, hasErrorDiagnostic } from './utils';
import { xParserSpecParsed, xParserApiVersion } from './constants';
import type { Spectral, RulesetFunctionContext } from '@stoplight/spectral-core';
import { Document } from '@stoplight/spectral-core';
import type { Parser } from './parser';
import type { ResolverOptions } from './resolver';
import type { ValidateOptions } from './validate';
import type { Input, Diagnostic, DetailedAsyncAPI } from './types';
export interface ParseOutput {
document: AsyncAPIDocumentInterface | undefined;
diagnostics: Diagnostic[];
extras?: {
document: Document;
}
}
export interface ParseOptions {
source?: string;
applyTraits?: boolean;
parseSchemas?: boolean;
validateOptions?: Omit<ValidateOptions, 'source'>;
/**
* If `true`, the parser will throw an `AsyncAPIParseError` when the input
* document fails validation (i.e. one or more diagnostics with
* `severity === DiagnosticSeverity.Error` are produced). The thrown error
* carries the diagnostics on its `diagnostics` property.
*
* Defaults to `false` to preserve the historical "return undefined document
* alongside diagnostics" behavior. See https://github.com/asyncapi/parser-js/issues/878
*/
throwOnError?: boolean;
__unstable?: {
resolver?: Omit<ResolverOptions, 'cache'>;
};
}
const defaultOptions: ParseOptions = {
applyTraits: true,
parseSchemas: true,
throwOnError: false,
validateOptions: {},
__unstable: {},
};
export async function parse(parser: Parser, spectral: Spectral, asyncapi: Input, options: ParseOptions = {}): Promise<ParseOutput> {
let spectralDocument: Document | undefined;
try {
options = mergePatch<ParseOptions>(defaultOptions, options);
// `./src/validate.ts` enforces 'string' type on both YAML and JSON later in
// code, and parses them both using the same `@stoplight/yaml`, so forceful
// normalization of YAML to JSON here has no practical application. It only
// causes `range` to be reported incorrectly in `diagnostics` by misleading
// `Parser` into thinking it's dealing with JSON instead of YAML, creating
// the bug described in https://github.com/asyncapi/parser-js/issues/936
const { validated, diagnostics, extras } = await validate(parser, spectral, asyncapi, { ...options.validateOptions, source: options.source, __unstable: options.__unstable });
if (validated === undefined) {
// Issue #878: when validation fails the parser used to silently return
// a result with `document: undefined`, which made it easy for callers to
// miss the failure (e.g. `result.document()` would throw a confusing
// "is not a function" error). With `throwOnError` enabled we surface the
// failure with a descriptive error instead.
if (options.throwOnError && hasErrorDiagnostic(diagnostics)) {
throw new AsyncAPIParseError(
'AsyncAPI document failed validation. See the `diagnostics` property for details.',
diagnostics,
extras?.document
);
}
return {
document: undefined,
diagnostics,
extras
};
}
spectralDocument = extras.document;
const inventory: RulesetFunctionContext['documentInventory'] = (spectralDocument as any).__documentInventory;
// unfreeze the object - Spectral makes resolved document "freezed"
const validatedDoc = copy(validated as Record<string, any>);
// Apply unique ids which are used as part of iterating between channels <-> operations <-> messages
applyUniqueIds(validatedDoc);
const detailed = createDetailedAsyncAPI(validatedDoc, asyncapi as DetailedAsyncAPI['input'], options.source);
const document = createAsyncAPIDocument(detailed);
setExtension(xParserSpecParsed, true, document);
setExtension(xParserApiVersion, ParserAPIVersion, document);
await customOperations(parser, document, detailed, inventory, options);
return {
document,
diagnostics,
extras,
};
} catch (err: unknown) {
if (err instanceof AsyncAPIParseError) {
throw err;
}
return { document: undefined, diagnostics: createUncaghtDiagnostic(err, 'Error thrown during AsyncAPI document parsing', spectralDocument), extras: undefined };
}
}
export class AsyncAPIParseError extends Error {
public readonly diagnostics: Diagnostic[];
public readonly document?: Document;
constructor(message: string, diagnostics: Diagnostic[], document?: Document) {
super(message);
this.name = 'AsyncAPIParseError';
this.diagnostics = diagnostics;
this.document = document;
}
}