/
index.ts
127 lines (116 loc) 路 3.8 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
127
import * as fs from 'fs';
import * as jsYaml from 'js-yaml';
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.copy(await this.loadSpec(args.apiDoc));
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 this.copy(apiDoc);
};
this.sortApiDocTags(apiDoc);
if (visitor.visitApi) {
// const basePaths = basePathObs;
visitor.visitApi({
basePaths,
getApiDoc,
});
}
return {
apiDoc,
basePaths,
};
}
private loadSpec(filePath: string | object): 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 (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);
const docWithRefs = jsYaml.safeLoad(
fs.readFileSync(absolutePath, 'utf8'),
{ json: true },
);
return $RefParser.dereference(docWithRefs);
} finally {
process.chdir(origCwd);
}
} else {
throw new Error(
`${this.loggingPrefix}spec could not be read at ${filePath}`,
);
}
}
return $RefParser.dereference(filePath);
}
private copy<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
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.path] = basePath;
}
return Object.keys(basePathsMap).map(key => basePathsMap[key]);
}
}