/
body.parse.ts
94 lines (87 loc) 路 3.13 KB
/
body.parse.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
import { Ajv } from 'ajv';
import { ContentType, validationError } from '../util';
import { OpenAPIV3, BodySchema } from '../../framework/types';
export class BodySchemaParser {
private _apiDoc: OpenAPIV3.Document;
private ajv: Ajv;
constructor(ajv: Ajv, apiDoc: OpenAPIV3.Document) {
this.ajv = ajv;
this._apiDoc = apiDoc;
}
public parse(
path: string,
pathSchema: OpenAPIV3.OperationObject,
contentType: ContentType,
): BodySchema {
// TODO should return OpenAPIV3.SchemaObject instead
let schemaRequestBody = pathSchema.requestBody;
if (schemaRequestBody?.hasOwnProperty('$ref')) {
// TODO use ajv.getSchema instead
const ref = (<OpenAPIV3.ReferenceObject>schemaRequestBody).$ref;
const id = ref.replace(/^.+\//i, '');
schemaRequestBody = this._apiDoc.components.requestBodies[id];
}
const requestBody = <OpenAPIV3.RequestBodyObject>schemaRequestBody;
if (requestBody?.hasOwnProperty('content')) {
return this.toSchema(path, contentType, requestBody);
// if (requestBody.required) required.push('body');
}
return {};
}
private toSchema(
path: string,
contentType: ContentType,
requestBody: OpenAPIV3.RequestBodyObject,
): BodySchema {
if (requestBody.content) {
let content = null;
for (const type of contentType.equivalents()) {
content = requestBody.content[type];
if (content) break;
}
if (!content) {
const msg =
contentType.contentType === 'not_provided'
? 'media type not specified'
: `unsupported media type ${contentType.contentType}`;
throw validationError(415, path, msg);
}
const schema = this.cleanseContentSchema(contentType, requestBody);
return schema ?? content.schema ?? {};
}
return {};
}
private cleanseContentSchema(
contentType: ContentType,
requestBody: OpenAPIV3.RequestBodyObject,
): BodySchema {
const bodyContentSchema =
requestBody.content[contentType.contentType] &&
requestBody.content[contentType.contentType].schema;
let bodyContentRefSchema = null;
if (bodyContentSchema && '$ref' in bodyContentSchema) {
const resolved = this.ajv.getSchema(bodyContentSchema.$ref);
const schema = <OpenAPIV3.SchemaObject>resolved?.schema;
bodyContentRefSchema = schema?.properties ? { ...schema } : null;
}
// handle readonly / required request body refs
// don't need to copy schema if validator gets its own copy of the api spec
// currently all middlware i.e. req and res validators share the spec
const schema = bodyContentRefSchema || bodyContentSchema;
if (schema && schema.properties) {
Object.keys(schema.properties).forEach(prop => {
const propertyValue = schema.properties[prop];
const required = schema.required;
if (propertyValue.readOnly && required) {
const index = required.indexOf(prop);
if (index > -1) {
schema.required = required
.slice(0, index)
.concat(required.slice(index + 1));
}
}
});
return schema;
}
}
}