@redocly/openapi-core 1.0.0-beta.109 → 1.0.0-beta.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/config/config-resolvers.js +44 -25
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +1 -0
- package/lib/config/load.d.ts +8 -2
- package/lib/config/load.js +4 -2
- package/lib/config/types.d.ts +10 -0
- package/lib/config/utils.js +2 -2
- package/lib/rules/ajv.d.ts +1 -1
- package/lib/rules/ajv.js +5 -5
- package/lib/rules/common/assertions/asserts.d.ts +3 -5
- package/lib/rules/common/assertions/asserts.js +137 -97
- package/lib/rules/common/assertions/index.js +2 -6
- package/lib/rules/common/assertions/utils.d.ts +12 -6
- package/lib/rules/common/assertions/utils.js +33 -20
- package/lib/rules/common/no-ambiguous-paths.js +1 -1
- package/lib/rules/common/no-identical-paths.js +4 -4
- package/lib/rules/common/operation-2xx-response.js +2 -2
- package/lib/rules/common/operation-4xx-response.js +2 -2
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/path-params-defined.js +7 -2
- package/lib/rules/common/response-contains-header.js +2 -2
- package/lib/rules/common/security-defined.js +10 -5
- package/lib/rules/common/spec.js +14 -12
- package/lib/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/other/stats.d.ts +1 -1
- package/lib/rules/other/stats.js +1 -1
- package/lib/rules/utils.d.ts +1 -0
- package/lib/rules/utils.js +18 -2
- package/lib/types/oas2.js +6 -6
- package/lib/types/oas3.js +11 -11
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +30 -5
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +13 -1
- package/lib/visitors.d.ts +7 -6
- package/lib/visitors.js +11 -3
- package/package.json +3 -5
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
- package/src/__tests__/lint.test.ts +88 -0
- package/src/__tests__/utils.test.ts +11 -0
- package/src/__tests__/walk.test.ts +2 -2
- package/src/config/__tests__/config-resolvers.test.ts +62 -1
- package/src/config/__tests__/config.test.ts +5 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
- package/src/config/__tests__/load.test.ts +1 -1
- package/src/config/__tests__/resolve-plugins.test.ts +3 -3
- package/src/config/config-resolvers.ts +30 -6
- package/src/config/config.ts +2 -0
- package/src/config/load.ts +10 -4
- package/src/config/types.ts +13 -0
- package/src/config/utils.ts +1 -0
- package/src/rules/ajv.ts +4 -4
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +37 -0
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +37 -0
- package/src/rules/common/__tests__/path-params-defined.test.ts +69 -0
- package/src/rules/common/__tests__/security-defined.test.ts +6 -6
- package/src/rules/common/__tests__/spec.test.ts +125 -0
- package/src/rules/common/assertions/__tests__/asserts.test.ts +491 -428
- package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
- package/src/rules/common/assertions/asserts.ts +155 -97
- package/src/rules/common/assertions/index.ts +2 -11
- package/src/rules/common/assertions/utils.ts +66 -36
- package/src/rules/common/no-ambiguous-paths.ts +1 -1
- package/src/rules/common/no-identical-paths.ts +4 -4
- package/src/rules/common/operation-2xx-response.ts +2 -2
- package/src/rules/common/operation-4xx-response.ts +2 -2
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/path-params-defined.ts +9 -2
- package/src/rules/common/response-contains-header.ts +6 -1
- package/src/rules/common/security-defined.ts +10 -5
- package/src/rules/common/spec.ts +15 -11
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
- package/src/rules/oas3/__tests__/response-contains-header.test.ts +116 -0
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/other/stats.ts +1 -1
- package/src/rules/utils.ts +24 -1
- package/src/types/oas2.ts +6 -6
- package/src/types/oas3.ts +11 -11
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +30 -4
- package/src/utils.ts +13 -0
- package/src/visitors.ts +25 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -13,10 +13,11 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
|
|
|
13
13
|
}
|
|
14
14
|
>();
|
|
15
15
|
|
|
16
|
+
const operationsWithoutSecurity: Location[] = [];
|
|
16
17
|
let eachOperationHasSecurity: boolean = true;
|
|
17
18
|
|
|
18
19
|
return {
|
|
19
|
-
|
|
20
|
+
Root: {
|
|
20
21
|
leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) {
|
|
21
22
|
for (const [name, scheme] of referencedSchemes.entries()) {
|
|
22
23
|
if (scheme.defined) continue;
|
|
@@ -31,9 +32,12 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
|
|
|
31
32
|
if (root.security || eachOperationHasSecurity) {
|
|
32
33
|
return;
|
|
33
34
|
} else {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
for (const operationLocation of operationsWithoutSecurity) {
|
|
36
|
+
report({
|
|
37
|
+
message: `Every operation should have security defined on it or on the root level.`,
|
|
38
|
+
location: operationLocation.key(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
43
|
},
|
|
@@ -51,9 +55,10 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
|
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
},
|
|
54
|
-
Operation(operation: Oas2Operation | Oas3Operation) {
|
|
58
|
+
Operation(operation: Oas2Operation | Oas3Operation, { location }: UserContext) {
|
|
55
59
|
if (!operation?.security) {
|
|
56
60
|
eachOperationHasSecurity = false;
|
|
61
|
+
operationsWithoutSecurity.push(location);
|
|
57
62
|
}
|
|
58
63
|
},
|
|
59
64
|
};
|
package/src/rules/common/spec.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Oas3Rule, Oas2Rule } from '../../visitors';
|
|
2
2
|
import { isNamedType } from '../../types';
|
|
3
|
-
import { oasTypeOf, matchesJsonSchemaType, getSuggest } from '../utils';
|
|
3
|
+
import { oasTypeOf, matchesJsonSchemaType, getSuggest, validateSchemaEnumType } from '../utils';
|
|
4
4
|
import { isRef } from '../../ref-utils';
|
|
5
5
|
import { isPlainObject } from '../../utils';
|
|
6
|
+
import { UserContext } from '../../walk';
|
|
6
7
|
|
|
7
8
|
export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
8
9
|
return {
|
|
@@ -114,17 +115,20 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
114
115
|
propValue = resolve(propValue).node;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
if (propSchema.enum) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.join(', ')}.`,
|
|
124
|
-
from: refLocation,
|
|
125
|
-
suggest: getSuggest(propValue, propSchema.enum),
|
|
126
|
-
});
|
|
118
|
+
if (propSchema.items && propSchema.items?.enum && Array.isArray(propValue)) {
|
|
119
|
+
for (let i = 0; i < propValue.length; i++) {
|
|
120
|
+
validateSchemaEnumType(propSchema.items?.enum, propValue[i], propName, refLocation, {
|
|
121
|
+
report,
|
|
122
|
+
location: location.child([propName, i]),
|
|
123
|
+
} as UserContext);
|
|
127
124
|
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (propSchema.enum) {
|
|
128
|
+
validateSchemaEnumType(propSchema.enum, propValue, propName, refLocation, {
|
|
129
|
+
report,
|
|
130
|
+
location: location.child([propName]),
|
|
131
|
+
} as UserContext);
|
|
128
132
|
} else if (propSchema.type && !matchesJsonSchemaType(propValue, propSchema.type, false)) {
|
|
129
133
|
report({
|
|
130
134
|
message: `Expected type \`${propSchema.type}\` but got \`${propValueType}\`.`,
|
|
@@ -128,7 +128,7 @@ describe('no-invalid-media-type-examples', () => {
|
|
|
128
128
|
"source": "foobar.yaml",
|
|
129
129
|
},
|
|
130
130
|
],
|
|
131
|
-
"message": "Example value must conform to the schema: must NOT have
|
|
131
|
+
"message": "Example value must conform to the schema: must NOT have unevaluated properties \`c\`.",
|
|
132
132
|
"ruleId": "no-invalid-media-type-examples",
|
|
133
133
|
"severity": "error",
|
|
134
134
|
"suggest": Array [],
|
|
@@ -137,7 +137,7 @@ describe('no-invalid-media-type-examples', () => {
|
|
|
137
137
|
`);
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
it('should not on
|
|
140
|
+
it('should not report on valid example with allowAdditionalProperties', async () => {
|
|
141
141
|
const document = parseYamlToDocument(
|
|
142
142
|
outdent`
|
|
143
143
|
openapi: 3.0.0
|
|
@@ -177,6 +177,55 @@ describe('no-invalid-media-type-examples', () => {
|
|
|
177
177
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
+
it('should not report on valid example with allowAdditionalProperties and allOf and $ref', async () => {
|
|
181
|
+
const document = parseYamlToDocument(
|
|
182
|
+
outdent`
|
|
183
|
+
openapi: 3.0.0
|
|
184
|
+
components:
|
|
185
|
+
schemas:
|
|
186
|
+
C:
|
|
187
|
+
properties:
|
|
188
|
+
c:
|
|
189
|
+
type: string
|
|
190
|
+
paths:
|
|
191
|
+
/pet:
|
|
192
|
+
get:
|
|
193
|
+
responses:
|
|
194
|
+
200:
|
|
195
|
+
content:
|
|
196
|
+
application/json:
|
|
197
|
+
example:
|
|
198
|
+
a: "string"
|
|
199
|
+
b: 13
|
|
200
|
+
c: "string"
|
|
201
|
+
schema:
|
|
202
|
+
type: object
|
|
203
|
+
allOf:
|
|
204
|
+
- $ref: '#/components/schemas/C'
|
|
205
|
+
properties:
|
|
206
|
+
a:
|
|
207
|
+
type: string
|
|
208
|
+
b:
|
|
209
|
+
type: number
|
|
210
|
+
|
|
211
|
+
`,
|
|
212
|
+
'foobar.yaml'
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const results = await lintDocument({
|
|
216
|
+
externalRefResolver: new BaseResolver(),
|
|
217
|
+
document,
|
|
218
|
+
config: await makeConfig({
|
|
219
|
+
'no-invalid-media-type-examples': {
|
|
220
|
+
severity: 'error',
|
|
221
|
+
allowAdditionalProperties: false,
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
227
|
+
});
|
|
228
|
+
|
|
180
229
|
it('should not on invalid examples', async () => {
|
|
181
230
|
const document = parseYamlToDocument(
|
|
182
231
|
outdent`
|
|
@@ -270,4 +270,120 @@ describe('Oas3 response-contains-header', () => {
|
|
|
270
270
|
});
|
|
271
271
|
expect(results).toMatchInlineSnapshot(`Array []`);
|
|
272
272
|
});
|
|
273
|
+
|
|
274
|
+
it('should not report response object containing header name upper cased', async () => {
|
|
275
|
+
const document = parseYamlToDocument(outdent`
|
|
276
|
+
openapi: 3.0.3
|
|
277
|
+
info:
|
|
278
|
+
version: 3.0.0
|
|
279
|
+
paths:
|
|
280
|
+
/store/subscribe:
|
|
281
|
+
post:
|
|
282
|
+
responses:
|
|
283
|
+
'200':
|
|
284
|
+
description: successful operation
|
|
285
|
+
headers:
|
|
286
|
+
X-Test-Header:
|
|
287
|
+
description: calls per hour allowed by the user
|
|
288
|
+
schema:
|
|
289
|
+
type: integer
|
|
290
|
+
format: int32
|
|
291
|
+
`);
|
|
292
|
+
|
|
293
|
+
const results = await lintDocument({
|
|
294
|
+
externalRefResolver: new BaseResolver(),
|
|
295
|
+
document,
|
|
296
|
+
config: await makeConfig({
|
|
297
|
+
'response-contains-header': {
|
|
298
|
+
severity: 'error',
|
|
299
|
+
names: { '2XX': ['x-test-header'] },
|
|
300
|
+
},
|
|
301
|
+
}),
|
|
302
|
+
});
|
|
303
|
+
expect(results).toMatchInlineSnapshot(`Array []`);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should not report response object containing header name in the rule upper cased', async () => {
|
|
307
|
+
const document = parseYamlToDocument(outdent`
|
|
308
|
+
openapi: 3.0.3
|
|
309
|
+
info:
|
|
310
|
+
version: 3.0.0
|
|
311
|
+
paths:
|
|
312
|
+
/store/subscribe:
|
|
313
|
+
post:
|
|
314
|
+
responses:
|
|
315
|
+
'200':
|
|
316
|
+
description: successful operation
|
|
317
|
+
headers:
|
|
318
|
+
x-test-header:
|
|
319
|
+
description: calls per hour allowed by the user
|
|
320
|
+
schema:
|
|
321
|
+
type: integer
|
|
322
|
+
format: int32
|
|
323
|
+
`);
|
|
324
|
+
|
|
325
|
+
const results = await lintDocument({
|
|
326
|
+
externalRefResolver: new BaseResolver(),
|
|
327
|
+
document,
|
|
328
|
+
config: await makeConfig({
|
|
329
|
+
'response-contains-header': {
|
|
330
|
+
severity: 'error',
|
|
331
|
+
names: { '2XX': ['X-Test-Header'] },
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
});
|
|
335
|
+
expect(results).toMatchInlineSnapshot(`Array []`);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should report even if the response is null', async () => {
|
|
339
|
+
const document = parseYamlToDocument(
|
|
340
|
+
outdent`
|
|
341
|
+
openapi: 3.0.0
|
|
342
|
+
paths:
|
|
343
|
+
'/test/':
|
|
344
|
+
put:
|
|
345
|
+
responses:
|
|
346
|
+
'200': null
|
|
347
|
+
`,
|
|
348
|
+
'foobar.yaml'
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const results = await lintDocument({
|
|
352
|
+
externalRefResolver: new BaseResolver(),
|
|
353
|
+
document,
|
|
354
|
+
config: await makeConfig({
|
|
355
|
+
'response-contains-header': {
|
|
356
|
+
severity: 'error',
|
|
357
|
+
names: { '2XX': ['X-Test-Header'] },
|
|
358
|
+
},
|
|
359
|
+
}),
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(results).toMatchInlineSnapshot(`
|
|
363
|
+
Array [
|
|
364
|
+
Object {
|
|
365
|
+
"location": Array [
|
|
366
|
+
Object {
|
|
367
|
+
"pointer": "#/paths/~1test~1/put/responses/200/headers",
|
|
368
|
+
"reportOnKey": true,
|
|
369
|
+
"source": Source {
|
|
370
|
+
"absoluteRef": "foobar.yaml",
|
|
371
|
+
"body": "openapi: 3.0.0
|
|
372
|
+
paths:
|
|
373
|
+
'/test/':
|
|
374
|
+
put:
|
|
375
|
+
responses:
|
|
376
|
+
'200': null",
|
|
377
|
+
"mimeType": undefined,
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
"message": "Response object must contain a \\"X-Test-Header\\" header.",
|
|
382
|
+
"ruleId": "response-contains-header",
|
|
383
|
+
"severity": "error",
|
|
384
|
+
"suggest": Array [],
|
|
385
|
+
},
|
|
386
|
+
]
|
|
387
|
+
`);
|
|
388
|
+
});
|
|
273
389
|
});
|
|
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const RequestMimeType: Oas3Rule = ({ allowedValues }) => {
|
|
7
7
|
return {
|
|
8
|
-
|
|
8
|
+
Paths: {
|
|
9
9
|
RequestBody: {
|
|
10
10
|
leave(requestBody: Oas3RequestBody, ctx: UserContext) {
|
|
11
11
|
validateMimeTypeOAS3({ type: 'consumes', value: requestBody }, ctx, allowedValues);
|
|
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const ResponseMimeType: Oas3Rule = ({ allowedValues }) => {
|
|
7
7
|
return {
|
|
8
|
-
|
|
8
|
+
Paths: {
|
|
9
9
|
Response: {
|
|
10
10
|
leave(response: Oas3Response, ctx: UserContext) {
|
|
11
11
|
validateMimeTypeOAS3({ type: 'produces', value: response }, ctx, allowedValues);
|
package/src/rules/other/stats.ts
CHANGED
package/src/rules/utils.ts
CHANGED
|
@@ -109,7 +109,8 @@ export function validateExample(
|
|
|
109
109
|
message: `Example value must conform to the schema: ${error.message}.`,
|
|
110
110
|
location: {
|
|
111
111
|
...new Location(dataLoc.source, error.instancePath),
|
|
112
|
-
reportOnKey:
|
|
112
|
+
reportOnKey:
|
|
113
|
+
error.keyword === 'unevaluatedProperties' || error.keyword === 'additionalProperties',
|
|
113
114
|
},
|
|
114
115
|
from: location,
|
|
115
116
|
suggest: error.suggest,
|
|
@@ -137,3 +138,25 @@ export function getAdditionalPropertiesOption(opts: Record<string, any>): boolea
|
|
|
137
138
|
showWarningForDeprecatedField('disallowAdditionalProperties', 'allowAdditionalProperties');
|
|
138
139
|
return !opts.disallowAdditionalProperties;
|
|
139
140
|
}
|
|
141
|
+
|
|
142
|
+
export function validateSchemaEnumType(
|
|
143
|
+
schemaEnum: string[],
|
|
144
|
+
propertyValue: string,
|
|
145
|
+
propName: string,
|
|
146
|
+
refLocation: Location | undefined,
|
|
147
|
+
{ report, location }: UserContext
|
|
148
|
+
) {
|
|
149
|
+
if (!schemaEnum) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!schemaEnum.includes(propertyValue === null ? 'null' : propertyValue)) {
|
|
153
|
+
report({
|
|
154
|
+
location,
|
|
155
|
+
message: `\`${propName}\` can be one of the following only: ${schemaEnum
|
|
156
|
+
.map((type) => `"${type}"`)
|
|
157
|
+
.join(', ')}.`,
|
|
158
|
+
from: refLocation,
|
|
159
|
+
suggest: getSuggest(propertyValue, schemaEnum),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/types/oas2.ts
CHANGED
|
@@ -11,7 +11,7 @@ const Root: NodeType = {
|
|
|
11
11
|
schemes: { type: 'array', items: { type: 'string' } },
|
|
12
12
|
consumes: { type: 'array', items: { type: 'string' } },
|
|
13
13
|
produces: { type: 'array', items: { type: 'string' } },
|
|
14
|
-
paths: '
|
|
14
|
+
paths: 'Paths',
|
|
15
15
|
definitions: 'NamedSchemas',
|
|
16
16
|
parameters: 'NamedParameters',
|
|
17
17
|
responses: 'NamedResponses',
|
|
@@ -51,7 +51,7 @@ const License: NodeType = {
|
|
|
51
51
|
required: ['name'],
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
const
|
|
54
|
+
const Paths: NodeType = {
|
|
55
55
|
properties: {},
|
|
56
56
|
additionalProperties: (_value: any, key: string) =>
|
|
57
57
|
key.startsWith('/') ? 'PathItem' : undefined,
|
|
@@ -82,7 +82,7 @@ const Operation: NodeType = {
|
|
|
82
82
|
consumes: { type: 'array', items: { type: 'string' } },
|
|
83
83
|
produces: { type: 'array', items: { type: 'string' } },
|
|
84
84
|
parameters: listOf('Parameter'),
|
|
85
|
-
responses: '
|
|
85
|
+
responses: 'Responses',
|
|
86
86
|
schemes: { type: 'array', items: { type: 'string' } },
|
|
87
87
|
deprecated: { type: 'boolean' },
|
|
88
88
|
security: listOf('SecurityRequirement'),
|
|
@@ -180,7 +180,7 @@ const ParameterItems: NodeType = {
|
|
|
180
180
|
},
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
-
const
|
|
183
|
+
const Responses: NodeType = {
|
|
184
184
|
properties: {
|
|
185
185
|
default: 'Response',
|
|
186
186
|
},
|
|
@@ -376,14 +376,14 @@ export const Oas2Types: Record<string, NodeType> = {
|
|
|
376
376
|
Info,
|
|
377
377
|
Contact,
|
|
378
378
|
License,
|
|
379
|
-
|
|
379
|
+
Paths,
|
|
380
380
|
PathItem,
|
|
381
381
|
Parameter,
|
|
382
382
|
ParameterItems,
|
|
383
383
|
Operation,
|
|
384
384
|
Examples,
|
|
385
385
|
Header,
|
|
386
|
-
|
|
386
|
+
Responses,
|
|
387
387
|
Response,
|
|
388
388
|
Schema,
|
|
389
389
|
Xml,
|
package/src/types/oas3.ts
CHANGED
|
@@ -10,7 +10,7 @@ const Root: NodeType = {
|
|
|
10
10
|
security: listOf('SecurityRequirement'),
|
|
11
11
|
tags: listOf('Tag'),
|
|
12
12
|
externalDocs: 'ExternalDocs',
|
|
13
|
-
paths: '
|
|
13
|
+
paths: 'Paths',
|
|
14
14
|
components: 'Components',
|
|
15
15
|
'x-webhooks': 'WebhooksMap',
|
|
16
16
|
},
|
|
@@ -88,7 +88,7 @@ const License: NodeType = {
|
|
|
88
88
|
required: ['name'],
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const Paths: NodeType = {
|
|
92
92
|
properties: {},
|
|
93
93
|
additionalProperties: (_value: any, key: string) =>
|
|
94
94
|
key.startsWith('/') ? 'PathItem' : undefined,
|
|
@@ -153,7 +153,7 @@ const Operation: NodeType = {
|
|
|
153
153
|
security: listOf('SecurityRequirement'),
|
|
154
154
|
servers: listOf('Server'),
|
|
155
155
|
requestBody: 'RequestBody',
|
|
156
|
-
responses: '
|
|
156
|
+
responses: 'Responses',
|
|
157
157
|
deprecated: { type: 'boolean' },
|
|
158
158
|
callbacks: 'CallbacksMap',
|
|
159
159
|
'x-codeSamples': listOf('XCodeSample'),
|
|
@@ -190,7 +190,7 @@ const MediaType: NodeType = {
|
|
|
190
190
|
schema: 'Schema',
|
|
191
191
|
example: { isExample: true },
|
|
192
192
|
examples: 'ExamplesMap',
|
|
193
|
-
encoding: '
|
|
193
|
+
encoding: 'EncodingMap',
|
|
194
194
|
},
|
|
195
195
|
};
|
|
196
196
|
|
|
@@ -234,7 +234,7 @@ const Header: NodeType = {
|
|
|
234
234
|
requiredOneOf: ['schema', 'content'],
|
|
235
235
|
};
|
|
236
236
|
|
|
237
|
-
const
|
|
237
|
+
const Responses: NodeType = {
|
|
238
238
|
properties: { default: 'Response' },
|
|
239
239
|
additionalProperties: (_v: any, key: string) =>
|
|
240
240
|
responseCodeRegexp.test(key) ? 'Response' : undefined,
|
|
@@ -408,7 +408,7 @@ const AuthorizationCode: NodeType = {
|
|
|
408
408
|
required: ['authorizationUrl', 'tokenUrl', 'scopes'],
|
|
409
409
|
};
|
|
410
410
|
|
|
411
|
-
const
|
|
411
|
+
const OAuth2Flows: NodeType = {
|
|
412
412
|
properties: {
|
|
413
413
|
implicit: 'ImplicitFlow',
|
|
414
414
|
password: 'PasswordFlow',
|
|
@@ -425,7 +425,7 @@ const SecurityScheme: NodeType = {
|
|
|
425
425
|
in: { type: 'string', enum: ['query', 'header', 'cookie'] },
|
|
426
426
|
scheme: { type: 'string' },
|
|
427
427
|
bearerFormat: { type: 'string' },
|
|
428
|
-
flows: '
|
|
428
|
+
flows: 'OAuth2Flows',
|
|
429
429
|
openIdConnectUrl: { type: 'string' },
|
|
430
430
|
},
|
|
431
431
|
required(value) {
|
|
@@ -470,7 +470,7 @@ export const Oas3Types: Record<string, NodeType> = {
|
|
|
470
470
|
Info,
|
|
471
471
|
Contact,
|
|
472
472
|
License,
|
|
473
|
-
|
|
473
|
+
Paths,
|
|
474
474
|
PathItem,
|
|
475
475
|
Parameter,
|
|
476
476
|
Operation,
|
|
@@ -482,10 +482,10 @@ export const Oas3Types: Record<string, NodeType> = {
|
|
|
482
482
|
Example,
|
|
483
483
|
ExamplesMap: mapOf('Example'),
|
|
484
484
|
Encoding,
|
|
485
|
-
|
|
485
|
+
EncodingMap: mapOf('Encoding'),
|
|
486
486
|
Header,
|
|
487
487
|
HeadersMap: mapOf('Header'),
|
|
488
|
-
|
|
488
|
+
Responses,
|
|
489
489
|
Response,
|
|
490
490
|
Link,
|
|
491
491
|
Schema,
|
|
@@ -508,7 +508,7 @@ export const Oas3Types: Record<string, NodeType> = {
|
|
|
508
508
|
PasswordFlow,
|
|
509
509
|
ClientCredentials,
|
|
510
510
|
AuthorizationCode,
|
|
511
|
-
|
|
511
|
+
OAuth2Flows,
|
|
512
512
|
SecurityScheme,
|
|
513
513
|
XCodeSample,
|
|
514
514
|
WebhooksMap,
|
package/src/types/oas3_1.ts
CHANGED
|
@@ -9,7 +9,7 @@ const Root: NodeType = {
|
|
|
9
9
|
security: listOf('SecurityRequirement'),
|
|
10
10
|
tags: listOf('Tag'),
|
|
11
11
|
externalDocs: 'ExternalDocs',
|
|
12
|
-
paths: '
|
|
12
|
+
paths: 'Paths',
|
|
13
13
|
webhooks: 'WebhooksMap',
|
|
14
14
|
components: 'Components',
|
|
15
15
|
jsonSchemaDialect: { type: 'string' },
|
|
@@ -69,7 +69,7 @@ const Operation: NodeType = {
|
|
|
69
69
|
security: listOf('SecurityRequirement'),
|
|
70
70
|
servers: listOf('Server'),
|
|
71
71
|
requestBody: 'RequestBody',
|
|
72
|
-
responses: '
|
|
72
|
+
responses: 'Responses',
|
|
73
73
|
deprecated: { type: 'boolean' },
|
|
74
74
|
callbacks: mapOf('Callback'),
|
|
75
75
|
'x-codeSamples': listOf('XCodeSample'),
|
|
@@ -176,7 +176,7 @@ const SecurityScheme: NodeType = {
|
|
|
176
176
|
in: { type: 'string', enum: ['query', 'header', 'cookie'] },
|
|
177
177
|
scheme: { type: 'string' },
|
|
178
178
|
bearerFormat: { type: 'string' },
|
|
179
|
-
flows: '
|
|
179
|
+
flows: 'OAuth2Flows',
|
|
180
180
|
openIdConnectUrl: { type: 'string' },
|
|
181
181
|
},
|
|
182
182
|
required(value) {
|
|
@@ -66,7 +66,7 @@ const nodeTypesList = [
|
|
|
66
66
|
'Info',
|
|
67
67
|
'Contact',
|
|
68
68
|
'License',
|
|
69
|
-
'
|
|
69
|
+
'Paths',
|
|
70
70
|
'PathItem',
|
|
71
71
|
'Parameter',
|
|
72
72
|
'Operation',
|
|
@@ -78,10 +78,10 @@ const nodeTypesList = [
|
|
|
78
78
|
'Example',
|
|
79
79
|
'ExamplesMap',
|
|
80
80
|
'Encoding',
|
|
81
|
-
'
|
|
81
|
+
'EncodingMap',
|
|
82
82
|
'Header',
|
|
83
83
|
'HeadersMap',
|
|
84
|
-
'
|
|
84
|
+
'Responses',
|
|
85
85
|
'Response',
|
|
86
86
|
'Link',
|
|
87
87
|
'LinksMap',
|
|
@@ -104,7 +104,7 @@ const nodeTypesList = [
|
|
|
104
104
|
'PasswordFlow',
|
|
105
105
|
'ClientCredentials',
|
|
106
106
|
'AuthorizationCode',
|
|
107
|
-
'
|
|
107
|
+
'OAuth2Flows',
|
|
108
108
|
'SecurityScheme',
|
|
109
109
|
'XCodeSample',
|
|
110
110
|
'WebhooksMap',
|
|
@@ -165,6 +165,12 @@ const ConfigRoot: NodeType = {
|
|
|
165
165
|
doNotResolveExamples: { type: 'boolean' },
|
|
166
166
|
},
|
|
167
167
|
},
|
|
168
|
+
files: {
|
|
169
|
+
type: 'array',
|
|
170
|
+
items: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
168
174
|
},
|
|
169
175
|
};
|
|
170
176
|
|
|
@@ -187,6 +193,12 @@ const ConfigApisProperties: NodeType = {
|
|
|
187
193
|
...ConfigStyleguide.properties,
|
|
188
194
|
'features.openapi': 'ConfigReferenceDocs',
|
|
189
195
|
'features.mockServer': 'ConfigMockServer',
|
|
196
|
+
files: {
|
|
197
|
+
type: 'array',
|
|
198
|
+
items: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
190
202
|
},
|
|
191
203
|
required: ['root'],
|
|
192
204
|
};
|
|
@@ -275,6 +287,10 @@ const Assert: NodeType = {
|
|
|
275
287
|
ref: (value: string | boolean) =>
|
|
276
288
|
typeof value === 'string' ? { type: 'string' } : { type: 'boolean' },
|
|
277
289
|
},
|
|
290
|
+
additionalProperties: (_value: unknown, key: string) => {
|
|
291
|
+
if (/^\w+\/\w+$/.test(key)) return { type: 'object' };
|
|
292
|
+
return;
|
|
293
|
+
},
|
|
278
294
|
required: ['subject'],
|
|
279
295
|
};
|
|
280
296
|
|
|
@@ -874,6 +890,16 @@ const ConfigReferenceDocs: NodeType = {
|
|
|
874
890
|
unstable_externalDescription: { type: 'boolean' }, // deprecated
|
|
875
891
|
unstable_ignoreMimeParameters: { type: 'boolean' },
|
|
876
892
|
untrustedDefinition: { type: 'boolean' },
|
|
893
|
+
mockServer: {
|
|
894
|
+
properties: {
|
|
895
|
+
url: { type: 'string' },
|
|
896
|
+
position: { enum: ['first', 'last', 'replace', 'off'] },
|
|
897
|
+
description: { type: 'string' },
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
showAccessMode: { type: 'boolean' },
|
|
901
|
+
preserveOriginalExtensionsName: { type: 'boolean' },
|
|
902
|
+
markdownHeadingsAnchorLevel: { type: 'number' },
|
|
877
903
|
},
|
|
878
904
|
additionalProperties: { type: 'string' },
|
|
879
905
|
};
|
package/src/utils.ts
CHANGED
|
@@ -231,3 +231,16 @@ export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
|
|
|
231
231
|
export function identity<T>(value: T): T {
|
|
232
232
|
return value;
|
|
233
233
|
}
|
|
234
|
+
|
|
235
|
+
export function pickDefined<T extends Record<string, unknown>>(
|
|
236
|
+
obj?: T
|
|
237
|
+
): Record<string, unknown> | undefined {
|
|
238
|
+
if (!obj) return undefined;
|
|
239
|
+
const res: Record<string, unknown> = {};
|
|
240
|
+
for (const key in obj) {
|
|
241
|
+
if (obj[key] !== undefined) {
|
|
242
|
+
res[key] = obj[key];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return res;
|
|
246
|
+
}
|