@redocly/openapi-core 1.0.0-beta.108 → 1.0.0-beta.110
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/benchmark/benches/resolve-with-no-external.bench.js +1 -1
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +4 -4
- package/lib/config/all.js +3 -1
- package/lib/config/config-resolvers.js +22 -4
- 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/minimal.js +3 -1
- package/lib/config/recommended.js +3 -1
- package/lib/config/rules.js +1 -1
- package/lib/config/types.d.ts +17 -0
- package/lib/config/utils.d.ts +2 -2
- package/lib/config/utils.js +44 -6
- package/lib/decorators/common/registry-dependencies.js +1 -1
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +22 -1
- package/lib/lint.js +2 -2
- package/lib/redocly/registry-api.d.ts +0 -1
- package/lib/redocly/registry-api.js +5 -4
- package/lib/resolve.js +3 -1
- 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 +1 -1
- package/lib/rules/common/operation-2xx-response.js +1 -1
- package/lib/rules/common/operation-4xx-response.js +1 -1
- package/lib/rules/common/operation-operationId.js +1 -1
- package/lib/rules/common/operation-tag-defined.js +1 -1
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/security-defined.d.ts +2 -0
- package/lib/rules/common/{operation-security-defined.js → security-defined.js} +18 -4
- package/lib/rules/common/spec.js +12 -1
- package/lib/rules/common/tags-alphabetical.js +1 -1
- package/lib/rules/oas2/index.d.ts +1 -1
- package/lib/rules/oas2/index.js +2 -2
- package/lib/rules/oas2/remove-unused-components.js +1 -1
- package/lib/rules/oas2/request-mime-type.js +1 -1
- package/lib/rules/oas2/response-mime-type.js +1 -1
- package/lib/rules/oas3/index.js +6 -2
- package/lib/rules/oas3/no-empty-servers.js +1 -1
- package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -1
- package/lib/rules/oas3/no-unused-components.js +1 -1
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
- package/lib/rules/oas3/remove-unused-components.js +1 -1
- package/lib/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
- package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
- package/lib/rules/other/stats.d.ts +2 -2
- package/lib/rules/other/stats.js +2 -2
- package/lib/rules/utils.js +1 -1
- package/lib/types/oas2.js +5 -5
- package/lib/types/oas3.js +27 -20
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +60 -54
- package/lib/utils.d.ts +3 -3
- package/lib/utils.js +5 -5
- package/lib/visitors.d.ts +11 -11
- package/lib/visitors.js +13 -1
- package/package.json +3 -5
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
- package/src/__tests__/fixtures/extension.js +3 -3
- package/src/__tests__/format.test.ts +76 -0
- package/src/__tests__/lint.test.ts +184 -121
- package/src/__tests__/resolve-http.test.ts +1 -1
- package/src/__tests__/resolve.test.ts +9 -9
- package/src/__tests__/walk.test.ts +78 -10
- package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
- package/src/bundle.ts +4 -4
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -2
- package/src/config/__tests__/config-resolvers.test.ts +37 -1
- package/src/config/__tests__/config.test.ts +5 -0
- package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
- 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-file.yaml +18 -19
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
- package/src/config/__tests__/load.test.ts +13 -16
- package/src/config/__tests__/resolve-plugins.test.ts +3 -3
- package/src/config/__tests__/utils.test.ts +64 -4
- package/src/config/all.ts +3 -1
- package/src/config/config-resolvers.ts +30 -7
- package/src/config/config.ts +2 -0
- package/src/config/load.ts +13 -6
- package/src/config/minimal.ts +3 -1
- package/src/config/recommended.ts +3 -1
- package/src/config/rules.ts +2 -2
- package/src/config/types.ts +24 -0
- package/src/config/utils.ts +103 -13
- package/src/decorators/common/registry-dependencies.ts +1 -1
- package/src/format/format.ts +32 -2
- package/src/lint.ts +2 -2
- package/src/redocly/registry-api.ts +5 -4
- package/src/resolve.ts +3 -1
- package/src/rules/__tests__/utils.test.ts +1 -1
- package/src/rules/ajv.ts +4 -4
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
- package/src/rules/common/__tests__/security-defined.test.ts +175 -0
- package/src/rules/common/__tests__/spec.test.ts +79 -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 +1 -1
- package/src/rules/common/operation-2xx-response.ts +1 -1
- package/src/rules/common/operation-4xx-response.ts +1 -1
- package/src/rules/common/operation-operationId.ts +1 -1
- package/src/rules/common/operation-tag-defined.ts +1 -1
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +19 -4
- package/src/rules/common/spec.ts +15 -1
- package/src/rules/common/tags-alphabetical.ts +1 -1
- package/src/rules/oas2/index.ts +2 -2
- package/src/rules/oas2/remove-unused-components.ts +1 -1
- package/src/rules/oas2/request-mime-type.ts +1 -1
- package/src/rules/oas2/response-mime-type.ts +1 -1
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
- package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
- package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
- package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
- package/src/rules/oas3/index.ts +6 -2
- package/src/rules/oas3/no-empty-servers.ts +1 -1
- package/src/rules/oas3/no-server-variables-empty-enum.ts +1 -1
- package/src/rules/oas3/no-unused-components.ts +1 -1
- package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
- package/src/rules/oas3/remove-unused-components.ts +1 -1
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
- package/src/rules/other/stats.ts +2 -2
- package/src/rules/utils.ts +2 -1
- package/src/types/index.ts +2 -2
- package/src/types/oas2.ts +5 -5
- package/src/types/oas3.ts +27 -20
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +66 -38
- package/src/utils.ts +11 -7
- package/src/visitors.ts +29 -13
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/operation-security-defined.d.ts +0 -2
- package/src/rules/common/__tests__/operation-security-defined.test.ts +0 -69
package/src/rules/common/spec.ts
CHANGED
|
@@ -6,13 +6,18 @@ import { isPlainObject } from '../../utils';
|
|
|
6
6
|
|
|
7
7
|
export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
8
8
|
return {
|
|
9
|
-
any(
|
|
9
|
+
any(
|
|
10
|
+
node: any,
|
|
11
|
+
{ report, type, location, rawLocation, key, resolve, ignoreNextVisitorsOnNode }
|
|
12
|
+
) {
|
|
10
13
|
const nodeType = oasTypeOf(node);
|
|
14
|
+
const refLocation = rawLocation !== location ? rawLocation : undefined;
|
|
11
15
|
|
|
12
16
|
if (type.items) {
|
|
13
17
|
if (nodeType !== 'array') {
|
|
14
18
|
report({
|
|
15
19
|
message: `Expected type \`${type.name}\` (array) but got \`${nodeType}\``,
|
|
20
|
+
from: refLocation,
|
|
16
21
|
});
|
|
17
22
|
ignoreNextVisitorsOnNode();
|
|
18
23
|
}
|
|
@@ -20,6 +25,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
20
25
|
} else if (nodeType !== 'object') {
|
|
21
26
|
report({
|
|
22
27
|
message: `Expected type \`${type.name}\` (object) but got \`${nodeType}\``,
|
|
28
|
+
from: refLocation,
|
|
23
29
|
});
|
|
24
30
|
ignoreNextVisitorsOnNode();
|
|
25
31
|
return;
|
|
@@ -32,6 +38,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
32
38
|
if (!(node as object).hasOwnProperty(propName)) {
|
|
33
39
|
report({
|
|
34
40
|
message: `The field \`${propName}\` must be present on this level.`,
|
|
41
|
+
from: refLocation,
|
|
35
42
|
location: [{ reportOnKey: true }],
|
|
36
43
|
});
|
|
37
44
|
}
|
|
@@ -49,6 +56,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
49
56
|
}
|
|
50
57
|
report({
|
|
51
58
|
message: `The field \`${propName}\` is not allowed here.`,
|
|
59
|
+
from: refLocation,
|
|
52
60
|
location: location.child([propName]).key(),
|
|
53
61
|
});
|
|
54
62
|
}
|
|
@@ -67,6 +75,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
67
75
|
message: `Must contain at least one of the following fields: ${type.requiredOneOf?.join(
|
|
68
76
|
', '
|
|
69
77
|
)}.`,
|
|
78
|
+
from: refLocation,
|
|
70
79
|
location: [{ reportOnKey: true }],
|
|
71
80
|
});
|
|
72
81
|
}
|
|
@@ -91,6 +100,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
91
100
|
report({
|
|
92
101
|
message: `Property \`${propName}\` is not expected here.`,
|
|
93
102
|
suggest: getSuggest(propName, Object.keys(type.properties)),
|
|
103
|
+
from: refLocation,
|
|
94
104
|
location: propLocation.key(),
|
|
95
105
|
});
|
|
96
106
|
continue;
|
|
@@ -111,12 +121,14 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
111
121
|
message: `\`${propName}\` can be one of the following only: ${propSchema.enum
|
|
112
122
|
.map((i) => `"${i}"`)
|
|
113
123
|
.join(', ')}.`,
|
|
124
|
+
from: refLocation,
|
|
114
125
|
suggest: getSuggest(propValue, propSchema.enum),
|
|
115
126
|
});
|
|
116
127
|
}
|
|
117
128
|
} else if (propSchema.type && !matchesJsonSchemaType(propValue, propSchema.type, false)) {
|
|
118
129
|
report({
|
|
119
130
|
message: `Expected type \`${propSchema.type}\` but got \`${propValueType}\`.`,
|
|
131
|
+
from: refLocation,
|
|
120
132
|
location: propLocation,
|
|
121
133
|
});
|
|
122
134
|
} else if (propValueType === 'array' && propSchema.items?.type) {
|
|
@@ -126,6 +138,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
126
138
|
if (!matchesJsonSchemaType(item, itemsType, false)) {
|
|
127
139
|
report({
|
|
128
140
|
message: `Expected type \`${itemsType}\` but got \`${oasTypeOf(item)}\`.`,
|
|
141
|
+
from: refLocation,
|
|
129
142
|
location: propLocation.child([i]),
|
|
130
143
|
});
|
|
131
144
|
}
|
|
@@ -136,6 +149,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
|
|
|
136
149
|
if (propSchema.minimum > node[propName]) {
|
|
137
150
|
report({
|
|
138
151
|
message: `The value of the ${propName} field must be greater than or equal to ${propSchema.minimum}`,
|
|
152
|
+
from: refLocation,
|
|
139
153
|
location: location.child([propName]),
|
|
140
154
|
});
|
|
141
155
|
}
|
|
@@ -5,7 +5,7 @@ import { UserContext } from '../../walk';
|
|
|
5
5
|
|
|
6
6
|
export const TagsAlphabetical: Oas3Rule | Oas2Rule = () => {
|
|
7
7
|
return {
|
|
8
|
-
|
|
8
|
+
Root(root: Oas2Definition | Oas3Definition, { report, location }: UserContext) {
|
|
9
9
|
if (!root.tags) return;
|
|
10
10
|
for (let i = 0; i < root.tags.length - 1; i++) {
|
|
11
11
|
if (root.tags[i].name > root.tags[i + 1].name) {
|
package/src/rules/oas2/index.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { OperationDescription } from '../common/operation-description';
|
|
|
25
25
|
import { PathNotIncludeQuery } from '../common/path-not-include-query';
|
|
26
26
|
import { ParameterDescription } from '../common/parameter-description';
|
|
27
27
|
import { OperationSingularTag } from '../common/operation-singular-tag';
|
|
28
|
-
import {
|
|
28
|
+
import { SecurityDefined } from '../common/security-defined';
|
|
29
29
|
import { NoUnresolvedRefs } from '../no-unresolved-refs';
|
|
30
30
|
import { PathHttpVerbsOrder } from '../common/path-http-verbs-order';
|
|
31
31
|
import { NoIdenticalPaths } from '../common/no-identical-paths';
|
|
@@ -71,7 +71,7 @@ export const rules = {
|
|
|
71
71
|
'path-params-defined': PathParamsDefined as Oas2Rule,
|
|
72
72
|
'parameter-description': ParameterDescription as Oas2Rule,
|
|
73
73
|
'operation-singular-tag': OperationSingularTag as Oas2Rule,
|
|
74
|
-
'
|
|
74
|
+
'security-defined': SecurityDefined as Oas2Rule,
|
|
75
75
|
'no-unresolved-refs': NoUnresolvedRefs as Oas2Rule,
|
|
76
76
|
'no-identical-paths': NoIdenticalPaths as Oas2Rule,
|
|
77
77
|
'no-ambiguous-paths': NoAmbiguousPaths as Oas2Rule,
|
|
@@ -5,7 +5,7 @@ import { validateMimeType } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const RequestMimeType: Oas2Rule = ({ allowedValues }) => {
|
|
7
7
|
return {
|
|
8
|
-
|
|
8
|
+
Root(root: Oas2Definition, ctx: UserContext) {
|
|
9
9
|
validateMimeType({ type: 'consumes', value: root }, ctx, allowedValues);
|
|
10
10
|
},
|
|
11
11
|
Operation: {
|
|
@@ -5,7 +5,7 @@ import { validateMimeType } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const ResponseMimeType: Oas2Rule = ({ allowedValues }) => {
|
|
7
7
|
return {
|
|
8
|
-
|
|
8
|
+
Root(root: Oas2Definition, ctx: UserContext) {
|
|
9
9
|
validateMimeType({ type: 'produces', value: root }, ctx, allowedValues);
|
|
10
10
|
},
|
|
11
11
|
Operation: {
|
|
@@ -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`
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { outdent } from 'outdent';
|
|
2
|
+
import { lintDocument } from '../../../lint';
|
|
3
|
+
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
|
|
4
|
+
import { BaseResolver } from '../../../resolve';
|
|
5
|
+
|
|
6
|
+
describe('Oas3 operation-4xx-problem-details-rfc7807', () => {
|
|
7
|
+
it('should report `4xx` must have content type `application/problem+json` ', async () => {
|
|
8
|
+
const document = parseYamlToDocument(
|
|
9
|
+
outdent`
|
|
10
|
+
openapi: "3.0.0"
|
|
11
|
+
paths:
|
|
12
|
+
/pets:
|
|
13
|
+
get:
|
|
14
|
+
summary: List all pets
|
|
15
|
+
operationId: listPets
|
|
16
|
+
responses:
|
|
17
|
+
'400':
|
|
18
|
+
description: Test
|
|
19
|
+
content:
|
|
20
|
+
application/json:
|
|
21
|
+
schema:
|
|
22
|
+
type: object
|
|
23
|
+
properties:
|
|
24
|
+
type:
|
|
25
|
+
type: string
|
|
26
|
+
title:
|
|
27
|
+
type: string
|
|
28
|
+
`,
|
|
29
|
+
'foobar.yaml'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const results = await lintDocument({
|
|
33
|
+
externalRefResolver: new BaseResolver(),
|
|
34
|
+
document,
|
|
35
|
+
config: await makeConfig({ 'operation-4xx-problem-details-rfc7807': 'error' }),
|
|
36
|
+
});
|
|
37
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
38
|
+
Array [
|
|
39
|
+
Object {
|
|
40
|
+
"location": Array [
|
|
41
|
+
Object {
|
|
42
|
+
"pointer": "#/paths/~1pets/get/responses/400",
|
|
43
|
+
"reportOnKey": true,
|
|
44
|
+
"source": "foobar.yaml",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
"message": "Response \`4xx\` must have content-type \`application/problem+json\`.",
|
|
48
|
+
"ruleId": "operation-4xx-problem-details-rfc7807",
|
|
49
|
+
"severity": "error",
|
|
50
|
+
"suggest": Array [],
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should report `application/problem+json` must have `type` property', async () => {
|
|
57
|
+
const document = parseYamlToDocument(
|
|
58
|
+
outdent`
|
|
59
|
+
openapi: "3.0.0"
|
|
60
|
+
paths:
|
|
61
|
+
/pets:
|
|
62
|
+
get:
|
|
63
|
+
summary: List all pets
|
|
64
|
+
operationId: listPets
|
|
65
|
+
responses:
|
|
66
|
+
'400':
|
|
67
|
+
description: Test
|
|
68
|
+
content:
|
|
69
|
+
application/problem+json:
|
|
70
|
+
schema:
|
|
71
|
+
type: object
|
|
72
|
+
properties:
|
|
73
|
+
title:
|
|
74
|
+
type: string
|
|
75
|
+
`,
|
|
76
|
+
'foobar.yaml'
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const results = await lintDocument({
|
|
80
|
+
externalRefResolver: new BaseResolver(),
|
|
81
|
+
document,
|
|
82
|
+
config: await makeConfig({ 'operation-4xx-problem-details-rfc7807': 'error' }),
|
|
83
|
+
});
|
|
84
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
85
|
+
Array [
|
|
86
|
+
Object {
|
|
87
|
+
"location": Array [
|
|
88
|
+
Object {
|
|
89
|
+
"pointer": "#/paths/~1pets/get/responses/400/content/application~1problem+json/schema/properties/type",
|
|
90
|
+
"reportOnKey": true,
|
|
91
|
+
"source": "foobar.yaml",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
"message": "SchemaProperties object should contain \`type\` field.",
|
|
95
|
+
"ruleId": "operation-4xx-problem-details-rfc7807",
|
|
96
|
+
"severity": "error",
|
|
97
|
+
"suggest": Array [],
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should report `application/problem+json` must have `schema` property', async () => {
|
|
104
|
+
const document = parseYamlToDocument(
|
|
105
|
+
outdent`
|
|
106
|
+
openapi: "3.0.0"
|
|
107
|
+
paths:
|
|
108
|
+
/pets:
|
|
109
|
+
get:
|
|
110
|
+
summary: List all pets
|
|
111
|
+
operationId: listPets
|
|
112
|
+
responses:
|
|
113
|
+
'400':
|
|
114
|
+
description: Test
|
|
115
|
+
content:
|
|
116
|
+
application/problem+json:
|
|
117
|
+
example: asd
|
|
118
|
+
`,
|
|
119
|
+
'foobar.yaml'
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const results = await lintDocument({
|
|
123
|
+
externalRefResolver: new BaseResolver(),
|
|
124
|
+
document,
|
|
125
|
+
config: await makeConfig({ 'operation-4xx-problem-details-rfc7807': 'error' }),
|
|
126
|
+
});
|
|
127
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
128
|
+
Array [
|
|
129
|
+
Object {
|
|
130
|
+
"location": Array [
|
|
131
|
+
Object {
|
|
132
|
+
"pointer": "#/paths/~1pets/get/responses/400/content/application~1problem+json/schema",
|
|
133
|
+
"reportOnKey": true,
|
|
134
|
+
"source": "foobar.yaml",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
"message": "MediaType object should contain \`schema\` field.",
|
|
138
|
+
"ruleId": "operation-4xx-problem-details-rfc7807",
|
|
139
|
+
"severity": "error",
|
|
140
|
+
"suggest": Array [],
|
|
141
|
+
},
|
|
142
|
+
]
|
|
143
|
+
`);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -47,6 +47,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
47
47
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
48
48
|
Array [
|
|
49
49
|
Object {
|
|
50
|
+
"from": undefined,
|
|
50
51
|
"location": Array [
|
|
51
52
|
Object {
|
|
52
53
|
"pointer": "#/info/contact/url",
|
|
@@ -60,6 +61,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
60
61
|
"suggest": Array [],
|
|
61
62
|
},
|
|
62
63
|
Object {
|
|
64
|
+
"from": undefined,
|
|
63
65
|
"location": Array [
|
|
64
66
|
Object {
|
|
65
67
|
"pointer": "#/info/contact/email",
|
|
@@ -73,6 +75,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
73
75
|
"suggest": Array [],
|
|
74
76
|
},
|
|
75
77
|
Object {
|
|
78
|
+
"from": undefined,
|
|
76
79
|
"location": Array [
|
|
77
80
|
Object {
|
|
78
81
|
"pointer": "#/info/license",
|
|
@@ -103,6 +106,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
103
106
|
"suggest": Array [],
|
|
104
107
|
},
|
|
105
108
|
Object {
|
|
109
|
+
"from": undefined,
|
|
106
110
|
"location": Array [
|
|
107
111
|
Object {
|
|
108
112
|
"pointer": "#/servers/0/variables/a/enum/0",
|
|
@@ -116,6 +120,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
116
120
|
"suggest": Array [],
|
|
117
121
|
},
|
|
118
122
|
Object {
|
|
123
|
+
"from": undefined,
|
|
119
124
|
"location": Array [
|
|
120
125
|
Object {
|
|
121
126
|
"pointer": "#/tags/0",
|
|
@@ -129,6 +134,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
129
134
|
"suggest": Array [],
|
|
130
135
|
},
|
|
131
136
|
Object {
|
|
137
|
+
"from": undefined,
|
|
132
138
|
"location": Array [
|
|
133
139
|
Object {
|
|
134
140
|
"pointer": "#/tags/1",
|
|
@@ -175,6 +181,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
175
181
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
176
182
|
Array [
|
|
177
183
|
Object {
|
|
184
|
+
"from": undefined,
|
|
178
185
|
"location": Array [
|
|
179
186
|
Object {
|
|
180
187
|
"pointer": "#/components1",
|
|
@@ -203,6 +210,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
203
210
|
"suggest": Array [],
|
|
204
211
|
},
|
|
205
212
|
Object {
|
|
213
|
+
"from": undefined,
|
|
206
214
|
"location": Array [
|
|
207
215
|
Object {
|
|
208
216
|
"pointer": "#/info/contact/test",
|
|
@@ -244,6 +252,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
244
252
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
245
253
|
Array [
|
|
246
254
|
Object {
|
|
255
|
+
"from": undefined,
|
|
247
256
|
"location": Array [
|
|
248
257
|
Object {
|
|
249
258
|
"pointer": "#/",
|
|
@@ -270,6 +279,7 @@ describe('Oas3 Structural visitor basic', () => {
|
|
|
270
279
|
"suggest": Array [],
|
|
271
280
|
},
|
|
272
281
|
Object {
|
|
282
|
+
"from": undefined,
|
|
273
283
|
"location": Array [
|
|
274
284
|
Object {
|
|
275
285
|
"pointer": "#/info",
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { makeConfig, parseYamlToDocument } from '../../../../__tests__/utils';
|
|
2
|
+
import { outdent } from 'outdent';
|
|
3
|
+
import { lintDocument } from '../../../lint';
|
|
4
|
+
import { BaseResolver } from '../../../resolve';
|
|
5
|
+
|
|
6
|
+
describe('Oas3 spec-components-invalid-map-name', () => {
|
|
7
|
+
it('should report about invalid keys inside components', async () => {
|
|
8
|
+
const document = parseYamlToDocument(outdent`
|
|
9
|
+
openapi: 3.0.0
|
|
10
|
+
info:
|
|
11
|
+
version: 3.0.0
|
|
12
|
+
components:
|
|
13
|
+
parameters:
|
|
14
|
+
my Param:
|
|
15
|
+
name: param
|
|
16
|
+
description: param
|
|
17
|
+
in: path
|
|
18
|
+
examples:
|
|
19
|
+
invalid identifier:
|
|
20
|
+
description: 'Some description'
|
|
21
|
+
value: 21
|
|
22
|
+
responses:
|
|
23
|
+
400 status:
|
|
24
|
+
description: bad request
|
|
25
|
+
schemas:
|
|
26
|
+
first schema:
|
|
27
|
+
type: integer
|
|
28
|
+
format: int64
|
|
29
|
+
`);
|
|
30
|
+
const results = await lintDocument({
|
|
31
|
+
externalRefResolver: new BaseResolver(),
|
|
32
|
+
document,
|
|
33
|
+
config: await makeConfig({
|
|
34
|
+
'spec-components-invalid-map-name': 'error',
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(results).toMatchInlineSnapshot(`
|
|
39
|
+
Array [
|
|
40
|
+
Object {
|
|
41
|
+
"location": Array [
|
|
42
|
+
Object {
|
|
43
|
+
"pointer": "#/components/parameters/my Param",
|
|
44
|
+
"reportOnKey": true,
|
|
45
|
+
"source": Source {
|
|
46
|
+
"absoluteRef": "",
|
|
47
|
+
"body": "openapi: 3.0.0
|
|
48
|
+
info:
|
|
49
|
+
version: 3.0.0
|
|
50
|
+
components:
|
|
51
|
+
parameters:
|
|
52
|
+
my Param:
|
|
53
|
+
name: param
|
|
54
|
+
description: param
|
|
55
|
+
in: path
|
|
56
|
+
examples:
|
|
57
|
+
invalid identifier:
|
|
58
|
+
description: 'Some description'
|
|
59
|
+
value: 21
|
|
60
|
+
responses:
|
|
61
|
+
400 status:
|
|
62
|
+
description: bad request
|
|
63
|
+
schemas:
|
|
64
|
+
first schema:
|
|
65
|
+
type: integer
|
|
66
|
+
format: int64 ",
|
|
67
|
+
"mimeType": undefined,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
"message": "The map key in parameters \\"my Param\\" does not match the regular expression \\"^[a-zA-Z0-9\\\\.\\\\-_]+$\\"",
|
|
72
|
+
"ruleId": "spec-components-invalid-map-name",
|
|
73
|
+
"severity": "error",
|
|
74
|
+
"suggest": Array [],
|
|
75
|
+
},
|
|
76
|
+
Object {
|
|
77
|
+
"location": Array [
|
|
78
|
+
Object {
|
|
79
|
+
"pointer": "#/components/schemas/first schema",
|
|
80
|
+
"reportOnKey": true,
|
|
81
|
+
"source": Source {
|
|
82
|
+
"absoluteRef": "",
|
|
83
|
+
"body": "openapi: 3.0.0
|
|
84
|
+
info:
|
|
85
|
+
version: 3.0.0
|
|
86
|
+
components:
|
|
87
|
+
parameters:
|
|
88
|
+
my Param:
|
|
89
|
+
name: param
|
|
90
|
+
description: param
|
|
91
|
+
in: path
|
|
92
|
+
examples:
|
|
93
|
+
invalid identifier:
|
|
94
|
+
description: 'Some description'
|
|
95
|
+
value: 21
|
|
96
|
+
responses:
|
|
97
|
+
400 status:
|
|
98
|
+
description: bad request
|
|
99
|
+
schemas:
|
|
100
|
+
first schema:
|
|
101
|
+
type: integer
|
|
102
|
+
format: int64 ",
|
|
103
|
+
"mimeType": undefined,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
"message": "The map key in schemas \\"first schema\\" does not match the regular expression \\"^[a-zA-Z0-9\\\\.\\\\-_]+$\\"",
|
|
108
|
+
"ruleId": "spec-components-invalid-map-name",
|
|
109
|
+
"severity": "error",
|
|
110
|
+
"suggest": Array [],
|
|
111
|
+
},
|
|
112
|
+
Object {
|
|
113
|
+
"location": Array [
|
|
114
|
+
Object {
|
|
115
|
+
"pointer": "#/components/responses/400 status",
|
|
116
|
+
"reportOnKey": true,
|
|
117
|
+
"source": Source {
|
|
118
|
+
"absoluteRef": "",
|
|
119
|
+
"body": "openapi: 3.0.0
|
|
120
|
+
info:
|
|
121
|
+
version: 3.0.0
|
|
122
|
+
components:
|
|
123
|
+
parameters:
|
|
124
|
+
my Param:
|
|
125
|
+
name: param
|
|
126
|
+
description: param
|
|
127
|
+
in: path
|
|
128
|
+
examples:
|
|
129
|
+
invalid identifier:
|
|
130
|
+
description: 'Some description'
|
|
131
|
+
value: 21
|
|
132
|
+
responses:
|
|
133
|
+
400 status:
|
|
134
|
+
description: bad request
|
|
135
|
+
schemas:
|
|
136
|
+
first schema:
|
|
137
|
+
type: integer
|
|
138
|
+
format: int64 ",
|
|
139
|
+
"mimeType": undefined,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
"message": "The map key in responses \\"400 status\\" does not match the regular expression \\"^[a-zA-Z0-9\\\\.\\\\-_]+$\\"",
|
|
144
|
+
"ruleId": "spec-components-invalid-map-name",
|
|
145
|
+
"severity": "error",
|
|
146
|
+
"suggest": Array [],
|
|
147
|
+
},
|
|
148
|
+
Object {
|
|
149
|
+
"location": Array [
|
|
150
|
+
Object {
|
|
151
|
+
"pointer": "#/components/examples/invalid identifier",
|
|
152
|
+
"reportOnKey": true,
|
|
153
|
+
"source": Source {
|
|
154
|
+
"absoluteRef": "",
|
|
155
|
+
"body": "openapi: 3.0.0
|
|
156
|
+
info:
|
|
157
|
+
version: 3.0.0
|
|
158
|
+
components:
|
|
159
|
+
parameters:
|
|
160
|
+
my Param:
|
|
161
|
+
name: param
|
|
162
|
+
description: param
|
|
163
|
+
in: path
|
|
164
|
+
examples:
|
|
165
|
+
invalid identifier:
|
|
166
|
+
description: 'Some description'
|
|
167
|
+
value: 21
|
|
168
|
+
responses:
|
|
169
|
+
400 status:
|
|
170
|
+
description: bad request
|
|
171
|
+
schemas:
|
|
172
|
+
first schema:
|
|
173
|
+
type: integer
|
|
174
|
+
format: int64 ",
|
|
175
|
+
"mimeType": undefined,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
"message": "The map key in examples \\"invalid identifier\\" does not match the regular expression \\"^[a-zA-Z0-9\\\\.\\\\-_]+$\\"",
|
|
180
|
+
"ruleId": "spec-components-invalid-map-name",
|
|
181
|
+
"severity": "error",
|
|
182
|
+
"suggest": Array [],
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
`);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should not report a key of the example does not match the regular expression', async () => {
|
|
189
|
+
const document = parseYamlToDocument(outdent`
|
|
190
|
+
openapi: 3.0.0
|
|
191
|
+
info:
|
|
192
|
+
version: 3.0.0
|
|
193
|
+
paths:
|
|
194
|
+
/store/subscribe:
|
|
195
|
+
post:
|
|
196
|
+
parameters:
|
|
197
|
+
- name: petId
|
|
198
|
+
in: path
|
|
199
|
+
schema:
|
|
200
|
+
type: integer
|
|
201
|
+
format: int64
|
|
202
|
+
examples:
|
|
203
|
+
valid-identifier-1.key:
|
|
204
|
+
description: 'Some description'
|
|
205
|
+
value: 21
|
|
206
|
+
`);
|
|
207
|
+
const results = await lintDocument({
|
|
208
|
+
externalRefResolver: new BaseResolver(),
|
|
209
|
+
document,
|
|
210
|
+
config: await makeConfig({
|
|
211
|
+
'spec-components-invalid-map-name': 'error',
|
|
212
|
+
}),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(results).toMatchInlineSnapshot(`Array []`);
|
|
216
|
+
});
|
|
217
|
+
});
|