-
-
Notifications
You must be signed in to change notification settings - Fork 202
/
index.ts
126 lines (115 loc) 路 3.82 KB
/
index.ts
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
126
import * as fs from 'fs';
import * as path from 'path';
import * as $RefParser from 'json-schema-ref-parser';
import { OpenAPISchemaValidator } from './openapi.schema.validator';
import { BasePath } from './base.path';
import {
OpenAPIFrameworkArgs,
OpenAPIFrameworkInit,
OpenAPIFrameworkVisitor,
OpenAPIV3,
} from './types';
export class OpenAPIFramework {
private readonly args: OpenAPIFrameworkArgs;
private readonly loggingPrefix: string = 'openapi.validator: ';
constructor(args: OpenAPIFrameworkArgs) {
this.args = args;
}
public async initialize(
visitor: OpenAPIFrameworkVisitor,
): Promise<OpenAPIFrameworkInit> {
const args = this.args;
const apiDoc = await this.loadSpec(args.apiDoc, args.$refParser);
const basePathObs = this.getBasePathsFromServers(apiDoc.servers);
const basePaths = Array.from(
basePathObs.reduce((acc, bp) => {
bp.all().forEach((path) => acc.add(path));
return acc;
}, new Set<string>()),
);
const validateApiDoc =
'validateApiDoc' in args ? !!args.validateApiDoc : true;
const validator = new OpenAPISchemaValidator({
version: apiDoc.openapi,
// extensions: this.apiDoc[`x-${args.name}-schema-extension`],
});
if (validateApiDoc) {
const apiDocValidation = validator.validate(apiDoc);
if (apiDocValidation.errors.length) {
console.error(`${this.loggingPrefix}Validating schema`);
console.error(
`${this.loggingPrefix}validation errors`,
JSON.stringify(apiDocValidation.errors, null, ' '),
);
throw new Error(
`${this.loggingPrefix}args.apiDoc was invalid. See the output.`,
);
}
}
const getApiDoc = () => {
return apiDoc;
};
this.sortApiDocTags(apiDoc);
if (visitor.visitApi) {
// const basePaths = basePathObs;
visitor.visitApi({
basePaths,
getApiDoc,
});
}
return {
apiDoc,
basePaths,
};
}
private loadSpec(
filePath: string | object,
$refParser: { mode: 'bundle' | 'dereference' } = { mode: 'bundle' },
): Promise<OpenAPIV3.Document> {
// Because of this issue ( https://github.com/APIDevTools/json-schema-ref-parser/issues/101#issuecomment-421755168 )
// We need this workaround ( use '$RefParser.dereference' instead of '$RefParser.bundle' ) if asked by user
if (typeof filePath === 'string') {
const origCwd = process.cwd();
const specDir = path.resolve(origCwd, path.dirname(filePath));
const absolutePath = path.resolve(origCwd, filePath);
if (fs.existsSync(absolutePath)) {
// Get document, or throw exception on error
try {
process.chdir(specDir);
return $refParser.mode === 'dereference'
? $RefParser.dereference(absolutePath)
: $RefParser.bundle(absolutePath);
} finally {
process.chdir(origCwd);
}
} else {
throw new Error(
`${this.loggingPrefix}spec could not be read at ${filePath}`,
);
}
}
return $refParser.mode === 'dereference'
? $RefParser.dereference(filePath)
: $RefParser.bundle(filePath);
}
private sortApiDocTags(apiDoc: OpenAPIV3.Document): void {
if (apiDoc && Array.isArray(apiDoc.tags)) {
apiDoc.tags.sort((a, b): number => {
return a.name < b.name ? -1 : 1;
});
}
}
private getBasePathsFromServers(
servers: OpenAPIV3.ServerObject[],
): BasePath[] {
if (!servers || servers.length === 0) {
return [new BasePath({ url: '' })];
}
const basePathsMap: { [key: string]: BasePath } = {};
for (const server of servers) {
const basePath = new BasePath(server);
basePathsMap[basePath.expressPath] = basePath;
}
return Object.keys(basePathsMap).map((key) => basePathsMap[key]);
}
}