@redocly/openapi-core 1.0.0-beta.98 → 1.0.0-beta.99
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/lib/bundle.d.ts +2 -0
- package/lib/bundle.js +6 -3
- package/lib/config/all.js +1 -0
- package/lib/ref-utils.js +1 -0
- package/lib/rules/common/response-contains-header.d.ts +2 -0
- package/lib/rules/common/response-contains-header.js +29 -0
- package/lib/rules/common/scalar-property-missing-example.d.ts +2 -0
- package/lib/rules/common/scalar-property-missing-example.js +41 -0
- package/lib/rules/oas2/index.d.ts +3 -0
- package/lib/rules/oas2/index.js +6 -0
- package/lib/rules/oas2/response-contains-property.d.ts +2 -0
- package/lib/rules/oas2/response-contains-property.js +38 -0
- package/lib/rules/oas3/index.js +6 -0
- package/lib/rules/oas3/response-contains-property.d.ts +2 -0
- package/lib/rules/oas3/response-contains-property.js +40 -0
- package/lib/types/oas3.js +17 -6
- package/lib/types/oas3_1.js +9 -7
- package/lib/types/redocly-yaml.js +10 -0
- package/lib/typings/openapi.d.ts +3 -2
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +3 -1
- package/package.json +1 -1
- package/{__tests__ → src/__tests__}/__snapshots__/bundle.test.ts.snap +26 -0
- package/{__tests__ → src/__tests__}/bundle.test.ts +30 -6
- package/{__tests__ → src/__tests__}/codeframes.test.ts +3 -3
- package/{__tests__ → src/__tests__}/fixtures/extension.js +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/definitions.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/examples.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/external-request-body.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/externalref.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/hosted.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/openapi-with-external-refs-conflicting-names.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/openapi-with-external-refs.yaml +0 -0
- package/src/__tests__/fixtures/refs/openapi-with-url-refs.yaml +18 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/param-b.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/param-c.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/rename.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/requestBody.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/schema-a.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/simple.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/refs/vendor.schema.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/External.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/External2.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/description.md +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/externalInfo.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/externalLicense.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/openapi-with-back.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/openapi-with-md-description.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/openapi.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/schemas/type-a.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/schemas/type-b.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/transitive/a.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/transitive/components.yaml +0 -0
- package/{__tests__ → src/__tests__}/fixtures/resolve/transitive/schemas.yaml +0 -0
- package/src/__tests__/lint.test.ts +13 -0
- package/{__tests__ → src/__tests__}/login.test.ts +1 -1
- package/{__tests__ → src/__tests__}/normalizeVisitors.test.ts +4 -4
- package/{__tests__ → src/__tests__}/ref-utils.test.ts +5 -5
- package/{__tests__ → src/__tests__}/resolve-http.test.ts +4 -4
- package/{__tests__ → src/__tests__}/resolve.test.ts +4 -4
- package/src/__tests__/utils.test.ts +12 -1
- package/{__tests__ → src/__tests__}/walk.test.ts +5 -5
- package/src/bundle.ts +18 -3
- package/src/config/__tests__/config-resolvers.test.ts +1 -1
- package/src/config/all.ts +1 -0
- package/src/ref-utils.ts +1 -0
- package/src/rules/common/__tests__/scalar-property-missing-example.test.ts +207 -0
- package/src/rules/common/response-contains-header.ts +30 -0
- package/src/rules/common/scalar-property-missing-example.ts +55 -0
- package/src/rules/oas2/__tests__/response-contains-header.test.ts +174 -0
- package/src/rules/oas2/__tests__/response-contains-property.test.ts +155 -0
- package/src/rules/oas2/index.ts +6 -0
- package/src/rules/oas2/response-contains-property.ts +36 -0
- package/src/rules/oas3/__tests__/response-contains-header.test.ts +273 -0
- package/src/rules/oas3/__tests__/response-contains-property.test.ts +403 -0
- package/src/rules/oas3/index.ts +6 -0
- package/src/rules/oas3/response-contains-property.ts +38 -0
- package/src/types/oas3.ts +15 -6
- package/src/types/oas3_1.ts +9 -7
- package/src/types/redocly-yaml.ts +10 -0
- package/src/typings/openapi.ts +2 -1
- package/src/utils.ts +3 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/__tests__/lint.test.ts +0 -17
|
File without changes
|
|
@@ -4,6 +4,7 @@ import { lintFromString, lintConfig, lintDocument } from '../lint';
|
|
|
4
4
|
import { BaseResolver } from '../resolve';
|
|
5
5
|
import { loadConfig } from '../config/load';
|
|
6
6
|
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
|
|
7
|
+
import { detectOpenAPI } from '../oas-types';
|
|
7
8
|
|
|
8
9
|
describe('lint', () => {
|
|
9
10
|
it('lintFromString should work', async () => {
|
|
@@ -179,4 +180,16 @@ describe('lint', () => {
|
|
|
179
180
|
|
|
180
181
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
181
182
|
});
|
|
183
|
+
|
|
184
|
+
it('detect OpenAPI should throw an error when version is not string', () => {
|
|
185
|
+
const testDocument = parseYamlToDocument(
|
|
186
|
+
outdent`
|
|
187
|
+
openapi: 3.0
|
|
188
|
+
`,
|
|
189
|
+
'',
|
|
190
|
+
);
|
|
191
|
+
expect(() => detectOpenAPI(testDocument.parsed)).toThrow(
|
|
192
|
+
`Invalid OpenAPI version: should be a string but got "number"`,
|
|
193
|
+
);
|
|
194
|
+
});
|
|
182
195
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { normalizeVisitors, VisitorLevelContext } from '../
|
|
2
|
-
import { Oas3RuleSet } from '../
|
|
3
|
-
import { Oas3Types } from '../
|
|
4
|
-
import { normalizeTypes } from '../
|
|
1
|
+
import { normalizeVisitors, VisitorLevelContext } from '../visitors';
|
|
2
|
+
import { Oas3RuleSet } from '../oas-types';
|
|
3
|
+
import { Oas3Types } from '../types/oas3';
|
|
4
|
+
import { normalizeTypes } from '../types';
|
|
5
5
|
|
|
6
6
|
describe('Normalize visitors', () => {
|
|
7
7
|
it('should work correctly for single rule', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import outdent from 'outdent';
|
|
2
|
-
import { parseYamlToDocument } from '
|
|
3
|
-
import { parseRef, refBaseName } from '../
|
|
4
|
-
import { lintDocument } from '../
|
|
5
|
-
import { LintConfig } from '../
|
|
6
|
-
import { BaseResolver } from '../
|
|
2
|
+
import { parseYamlToDocument } from '../../__tests__/utils';
|
|
3
|
+
import { parseRef, refBaseName } from '../ref-utils';
|
|
4
|
+
import { lintDocument } from '../lint';
|
|
5
|
+
import { LintConfig } from '../config';
|
|
6
|
+
import { BaseResolver } from '../resolve';
|
|
7
7
|
|
|
8
8
|
describe('ref-utils', () => {
|
|
9
9
|
it(`should unescape refs with '/'`, () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { outdent } from 'outdent';
|
|
2
2
|
|
|
3
|
-
import { resolveDocument, BaseResolver } from '../
|
|
4
|
-
import { parseYamlToDocument } from '
|
|
5
|
-
import { Oas3Types } from '../
|
|
6
|
-
import { normalizeTypes } from '../
|
|
3
|
+
import { resolveDocument, BaseResolver } from '../resolve';
|
|
4
|
+
import { parseYamlToDocument } from '../../__tests__/utils';
|
|
5
|
+
import { Oas3Types } from '../types/oas3';
|
|
6
|
+
import { normalizeTypes } from '../types';
|
|
7
7
|
|
|
8
8
|
describe('Resolve http-headers', () => {
|
|
9
9
|
it('should use matching http-headers', async () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { outdent } from 'outdent';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
|
-
import { resolveDocument, BaseResolver, Document } from '../
|
|
5
|
-
import { parseYamlToDocument } from '
|
|
6
|
-
import { Oas3Types } from '../
|
|
7
|
-
import { normalizeTypes } from '../
|
|
4
|
+
import { resolveDocument, BaseResolver, Document } from '../resolve';
|
|
5
|
+
import { parseYamlToDocument } from '../../__tests__/utils';
|
|
6
|
+
import { Oas3Types } from '../types/oas3';
|
|
7
|
+
import { normalizeTypes } from '../types';
|
|
8
8
|
|
|
9
9
|
describe('collect refs', () => {
|
|
10
10
|
it('should resolve local refs', async () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pickObjectProps, omitObjectProps, slash } from '../utils';
|
|
1
|
+
import { pickObjectProps, omitObjectProps, slash, getMatchingStatusCodeRange } from '../utils';
|
|
2
2
|
|
|
3
3
|
describe('utils', () => {
|
|
4
4
|
const testObject = {
|
|
@@ -71,4 +71,15 @@ describe('utils', () => {
|
|
|
71
71
|
expect(slash(extended)).toBe(extended);
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
describe('getMatchingStatusCodeRange', () => {
|
|
76
|
+
it('should get the generalized form of status codes', () => {
|
|
77
|
+
expect(getMatchingStatusCodeRange('202')).toEqual('2XX');
|
|
78
|
+
expect(getMatchingStatusCodeRange(400)).toEqual('4XX');
|
|
79
|
+
});
|
|
80
|
+
it('should fail on a wrong input', () => {
|
|
81
|
+
expect(getMatchingStatusCodeRange('2002')).toEqual('2002');
|
|
82
|
+
expect(getMatchingStatusCodeRange(4000)).toEqual('4000');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
74
85
|
});
|
|
@@ -2,12 +2,12 @@ import outdent from 'outdent';
|
|
|
2
2
|
import each from 'jest-each';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
|
|
5
|
-
import { lintDocument } from '../
|
|
5
|
+
import { lintDocument } from '../lint';
|
|
6
6
|
|
|
7
|
-
import { parseYamlToDocument, replaceSourceWithRef, makeConfigForRuleset } from '
|
|
8
|
-
import { BaseResolver, Document } from '../
|
|
9
|
-
import { listOf } from '../
|
|
10
|
-
import { Oas3RuleSet } from '../
|
|
7
|
+
import { parseYamlToDocument, replaceSourceWithRef, makeConfigForRuleset } from '../../__tests__/utils';
|
|
8
|
+
import { BaseResolver, Document } from '../resolve';
|
|
9
|
+
import { listOf } from '../types';
|
|
10
|
+
import { Oas3RuleSet } from '../oas-types';
|
|
11
11
|
|
|
12
12
|
describe('walk order', () => {
|
|
13
13
|
it('should run visitors', async () => {
|
package/src/bundle.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { Oas3_1Types } from './types/oas3_1';
|
|
|
7
7
|
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
|
8
8
|
import { WalkContext, walkDocument, UserContext, ResolveResult } from './walk';
|
|
9
9
|
import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
|
|
10
|
-
import { isRef, Location, refBaseName
|
|
10
|
+
import {isAbsoluteUrl, isRef, Location, refBaseName} from './ref-utils';
|
|
11
11
|
import { initRules } from './config/rules';
|
|
12
12
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
13
13
|
import { isPlainObject } from './utils';
|
|
@@ -35,6 +35,7 @@ export async function bundle(opts: {
|
|
|
35
35
|
base?: string;
|
|
36
36
|
skipRedoclyRegistryRefs?: boolean;
|
|
37
37
|
removeUnusedComponents?: boolean;
|
|
38
|
+
keepUrlRefs?: boolean;
|
|
38
39
|
}) {
|
|
39
40
|
const {
|
|
40
41
|
ref,
|
|
@@ -71,6 +72,7 @@ export async function bundleDocument(opts: {
|
|
|
71
72
|
dereference?: boolean;
|
|
72
73
|
skipRedoclyRegistryRefs?: boolean;
|
|
73
74
|
removeUnusedComponents?: boolean;
|
|
75
|
+
keepUrlRefs?: boolean;
|
|
74
76
|
}) {
|
|
75
77
|
const {
|
|
76
78
|
document,
|
|
@@ -80,6 +82,7 @@ export async function bundleDocument(opts: {
|
|
|
80
82
|
dereference = false,
|
|
81
83
|
skipRedoclyRegistryRefs = false,
|
|
82
84
|
removeUnusedComponents = false,
|
|
85
|
+
keepUrlRefs = false,
|
|
83
86
|
} = opts;
|
|
84
87
|
const oasVersion = detectOpenAPI(document.parsed);
|
|
85
88
|
const oasMajorVersion = openAPIMajor(oasVersion);
|
|
@@ -128,7 +131,14 @@ export async function bundleDocument(opts: {
|
|
|
128
131
|
{
|
|
129
132
|
severity: 'error',
|
|
130
133
|
ruleId: 'bundler',
|
|
131
|
-
visitor: makeBundleVisitor(
|
|
134
|
+
visitor: makeBundleVisitor(
|
|
135
|
+
oasMajorVersion,
|
|
136
|
+
dereference,
|
|
137
|
+
skipRedoclyRegistryRefs,
|
|
138
|
+
document,
|
|
139
|
+
resolvedRefMap,
|
|
140
|
+
keepUrlRefs,
|
|
141
|
+
),
|
|
132
142
|
},
|
|
133
143
|
...decorators,
|
|
134
144
|
] as any,
|
|
@@ -199,7 +209,8 @@ function makeBundleVisitor(
|
|
|
199
209
|
dereference: boolean,
|
|
200
210
|
skipRedoclyRegistryRefs: boolean,
|
|
201
211
|
rootDocument: Document,
|
|
202
|
-
resolvedRefMap: ResolvedRefMap
|
|
212
|
+
resolvedRefMap: ResolvedRefMap,
|
|
213
|
+
keepUrlRefs: boolean,
|
|
203
214
|
) {
|
|
204
215
|
let components: Record<string, Record<string, any>>;
|
|
205
216
|
|
|
@@ -224,6 +235,10 @@ function makeBundleVisitor(
|
|
|
224
235
|
return;
|
|
225
236
|
}
|
|
226
237
|
|
|
238
|
+
if (keepUrlRefs && isAbsoluteUrl(node.$ref)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
227
242
|
const componentType = mapTypeToComponent(ctx.type.name, version);
|
|
228
243
|
if (!componentType) {
|
|
229
244
|
replaceRef(node, resolved, ctx);
|
|
@@ -165,7 +165,7 @@ describe('resolveLint', () => {
|
|
|
165
165
|
const lintConfig = {
|
|
166
166
|
// This points to ./fixtures/resolve-remote-configs/remote-config.yaml
|
|
167
167
|
extends: [
|
|
168
|
-
'https://raw.githubusercontent.com/Redocly/
|
|
168
|
+
'https://raw.githubusercontent.com/Redocly/redocly-cli/master/packages/core/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml',
|
|
169
169
|
],
|
|
170
170
|
};
|
|
171
171
|
|
package/src/config/all.ts
CHANGED
package/src/ref-utils.ts
CHANGED
|
@@ -0,0 +1,207 @@
|
|
|
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 scalar-property-missing-example', () => {
|
|
7
|
+
it('should report on a scalar property missing example', async () => {
|
|
8
|
+
const document = parseYamlToDocument(
|
|
9
|
+
outdent`
|
|
10
|
+
openapi: 3.0.0
|
|
11
|
+
components:
|
|
12
|
+
schemas:
|
|
13
|
+
User:
|
|
14
|
+
type: object
|
|
15
|
+
properties:
|
|
16
|
+
email:
|
|
17
|
+
description: User email address
|
|
18
|
+
type: string
|
|
19
|
+
format: email
|
|
20
|
+
`,
|
|
21
|
+
'foobar.yaml',
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const results = await lintDocument({
|
|
25
|
+
externalRefResolver: new BaseResolver(),
|
|
26
|
+
document,
|
|
27
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
31
|
+
Array [
|
|
32
|
+
Object {
|
|
33
|
+
"location": Array [
|
|
34
|
+
Object {
|
|
35
|
+
"pointer": "#/components/schemas/User/properties/email",
|
|
36
|
+
"reportOnKey": true,
|
|
37
|
+
"source": "foobar.yaml",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
"message": "Scalar property should have \\"example\\" defined.",
|
|
41
|
+
"ruleId": "scalar-property-missing-example",
|
|
42
|
+
"severity": "error",
|
|
43
|
+
"suggest": Array [],
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
`);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('Oas3.1 scalar-property-missing-example', () => {
|
|
51
|
+
it('should report on a scalar property missing example', async () => {
|
|
52
|
+
const document = parseYamlToDocument(
|
|
53
|
+
outdent`
|
|
54
|
+
openapi: 3.1.0
|
|
55
|
+
components:
|
|
56
|
+
schemas:
|
|
57
|
+
User:
|
|
58
|
+
type: object
|
|
59
|
+
properties:
|
|
60
|
+
email:
|
|
61
|
+
description: User email address
|
|
62
|
+
type: string
|
|
63
|
+
format: email
|
|
64
|
+
`,
|
|
65
|
+
'foobar.yaml',
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const results = await lintDocument({
|
|
69
|
+
externalRefResolver: new BaseResolver(),
|
|
70
|
+
document,
|
|
71
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
75
|
+
Array [
|
|
76
|
+
Object {
|
|
77
|
+
"location": Array [
|
|
78
|
+
Object {
|
|
79
|
+
"pointer": "#/components/schemas/User/properties/email",
|
|
80
|
+
"reportOnKey": true,
|
|
81
|
+
"source": "foobar.yaml",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
"message": "Scalar property should have \\"example\\" or \\"examples\\" defined.",
|
|
85
|
+
"ruleId": "scalar-property-missing-example",
|
|
86
|
+
"severity": "error",
|
|
87
|
+
"suggest": Array [],
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
`);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should not report on a scalar property with an example', async () => {
|
|
94
|
+
const document = parseYamlToDocument(
|
|
95
|
+
outdent`
|
|
96
|
+
openapi: 3.1.0
|
|
97
|
+
components:
|
|
98
|
+
schemas:
|
|
99
|
+
User:
|
|
100
|
+
type: object
|
|
101
|
+
properties:
|
|
102
|
+
email:
|
|
103
|
+
description: User email address
|
|
104
|
+
type: string
|
|
105
|
+
format: email
|
|
106
|
+
example: john.smith@example.com
|
|
107
|
+
`,
|
|
108
|
+
'foobar.yaml',
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const results = await lintDocument({
|
|
112
|
+
externalRefResolver: new BaseResolver(),
|
|
113
|
+
document,
|
|
114
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should not report on a scalar property with an examples', async () => {
|
|
121
|
+
const document = parseYamlToDocument(
|
|
122
|
+
outdent`
|
|
123
|
+
openapi: 3.1.0
|
|
124
|
+
components:
|
|
125
|
+
schemas:
|
|
126
|
+
User:
|
|
127
|
+
type: object
|
|
128
|
+
properties:
|
|
129
|
+
email:
|
|
130
|
+
description: User email address
|
|
131
|
+
type: string
|
|
132
|
+
format: email
|
|
133
|
+
examples:
|
|
134
|
+
- john.smith@example.com
|
|
135
|
+
- other@example.com
|
|
136
|
+
`,
|
|
137
|
+
'foobar.yaml',
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const results = await lintDocument({
|
|
141
|
+
externalRefResolver: new BaseResolver(),
|
|
142
|
+
document,
|
|
143
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should not report on a non-scalar property missing an example', async () => {
|
|
150
|
+
const document = parseYamlToDocument(
|
|
151
|
+
outdent`
|
|
152
|
+
openapi: 3.1.0
|
|
153
|
+
components:
|
|
154
|
+
schemas:
|
|
155
|
+
Pet:
|
|
156
|
+
type: object
|
|
157
|
+
required:
|
|
158
|
+
- photoUrls
|
|
159
|
+
properties:
|
|
160
|
+
photoUrls:
|
|
161
|
+
description: The list of URL to a cute photos featuring pet
|
|
162
|
+
type: array
|
|
163
|
+
maxItems: 20
|
|
164
|
+
xml:
|
|
165
|
+
name: photoUrl
|
|
166
|
+
wrapped: true
|
|
167
|
+
items:
|
|
168
|
+
type: string
|
|
169
|
+
format: url
|
|
170
|
+
`,
|
|
171
|
+
'foobar.yaml',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const results = await lintDocument({
|
|
175
|
+
externalRefResolver: new BaseResolver(),
|
|
176
|
+
document,
|
|
177
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should not report on a scalar property of binary format missing an example', async () => {
|
|
184
|
+
const document = parseYamlToDocument(
|
|
185
|
+
outdent`
|
|
186
|
+
openapi: 3.1.0
|
|
187
|
+
components:
|
|
188
|
+
schemas:
|
|
189
|
+
User:
|
|
190
|
+
type: object
|
|
191
|
+
properties:
|
|
192
|
+
responses:
|
|
193
|
+
type: string
|
|
194
|
+
format: binary
|
|
195
|
+
`,
|
|
196
|
+
'foobar.yaml',
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const results = await lintDocument({
|
|
200
|
+
externalRefResolver: new BaseResolver(),
|
|
201
|
+
document,
|
|
202
|
+
config: await makeConfig({ 'scalar-property-missing-example': 'error' }),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Oas2Rule, Oas3Rule } from '../../visitors';
|
|
2
|
+
import { UserContext } from '../../walk';
|
|
3
|
+
import { Oas3Response } from '../../typings/openapi';
|
|
4
|
+
import { Oas2Response } from '../../typings/swagger';
|
|
5
|
+
import { getMatchingStatusCodeRange } from '../../utils';
|
|
6
|
+
|
|
7
|
+
export const ResponseContainsHeader: Oas3Rule | Oas2Rule = (options) => {
|
|
8
|
+
const names: Record<string, string[]> = options.names || {};
|
|
9
|
+
return {
|
|
10
|
+
Operation: {
|
|
11
|
+
Response: {
|
|
12
|
+
enter: (response: Oas2Response | Oas3Response, { report, location, key }: UserContext) => {
|
|
13
|
+
const expectedHeaders =
|
|
14
|
+
names[key] ||
|
|
15
|
+
names[getMatchingStatusCodeRange(key)] ||
|
|
16
|
+
names[getMatchingStatusCodeRange(key).toLowerCase()] ||
|
|
17
|
+
[];
|
|
18
|
+
for (const expectedHeader of expectedHeaders) {
|
|
19
|
+
if (!response.headers?.[expectedHeader]) {
|
|
20
|
+
report({
|
|
21
|
+
message: `Response object must contain a "${expectedHeader}" header.`,
|
|
22
|
+
location: location.child('headers').key(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Oas2Rule, Oas3Rule } from '../../visitors';
|
|
2
|
+
import type { UserContext } from '../../walk';
|
|
3
|
+
import type { Oas2Schema } from '../../typings/swagger';
|
|
4
|
+
import type { Oas3Schema, Oas3_1Schema } from '../../typings/openapi';
|
|
5
|
+
import { OasVersion } from '../../oas-types';
|
|
6
|
+
|
|
7
|
+
const SCALAR_TYPES = ['string', 'integer', 'number', 'boolean', 'null'];
|
|
8
|
+
|
|
9
|
+
export const ScalarPropertyMissingExample: Oas3Rule | Oas2Rule = () => {
|
|
10
|
+
return {
|
|
11
|
+
SchemaProperties(
|
|
12
|
+
properties: { [name: string]: Oas2Schema | Oas3Schema | Oas3_1Schema },
|
|
13
|
+
{ report, location, oasVersion, resolve }: UserContext,
|
|
14
|
+
) {
|
|
15
|
+
for (const propName of Object.keys(properties)) {
|
|
16
|
+
const propSchema = resolve(properties[propName]).node;
|
|
17
|
+
|
|
18
|
+
if (!propSchema || !isScalarSchema(propSchema)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!propSchema.example && !(propSchema as Oas3_1Schema).examples) {
|
|
23
|
+
report({
|
|
24
|
+
message: `Scalar property should have "example"${
|
|
25
|
+
oasVersion === OasVersion.Version3_1 ? ' or "examples"' : ''
|
|
26
|
+
} defined.`,
|
|
27
|
+
location: location.child(propName).key(),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function isScalarSchema(schema: Oas2Schema | Oas3Schema | Oas3_1Schema) {
|
|
36
|
+
if (!schema.type) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (schema.allOf || (schema as Oas3Schema).anyOf || (schema as Oas3Schema).oneOf) {
|
|
41
|
+
// Skip allOf/oneOf/anyOf as it's complicated to validate it right now.
|
|
42
|
+
// We need core support for checking contrstrains through those keywords.
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (schema.format === 'binary') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(schema.type)) {
|
|
51
|
+
return schema.type.every((t) => SCALAR_TYPES.includes(t));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return SCALAR_TYPES.includes(schema.type);
|
|
55
|
+
}
|