@redocly/openapi-core 1.0.0-beta.112 → 1.0.0-beta.114
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/config/config-resolvers.js +3 -6
- package/lib/config/config.d.ts +4 -10
- package/lib/config/config.js +1 -1
- package/lib/config/load.js +6 -6
- package/lib/config/rules.d.ts +6 -3
- package/lib/config/rules.js +3 -2
- package/lib/config/types.d.ts +3 -0
- package/lib/ref-utils.d.ts +1 -0
- package/lib/ref-utils.js +5 -1
- package/lib/resolve.js +19 -0
- package/lib/rules/common/assertions/asserts.d.ts +1 -0
- package/lib/rules/common/assertions/asserts.js +16 -2
- package/lib/rules/common/spec.js +6 -0
- package/lib/rules/utils.js +3 -0
- package/lib/types/oas2.js +11 -7
- package/lib/types/oas3.js +15 -10
- package/lib/types/oas3_1.js +1 -0
- package/lib/types/redocly-yaml.js +6 -0
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +7 -1
- package/package.json +1 -1
- package/src/__tests__/bundle.test.ts +46 -0
- package/src/benchmark/benches/rebilly.yaml +36 -28
- package/src/config/__tests__/config-resolvers.test.ts +1 -2
- package/src/config/__tests__/fixtures/load-redocly.yaml +0 -2
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +0 -1
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +0 -1
- package/src/config/config-resolvers.ts +2 -12
- package/src/config/config.ts +6 -5
- package/src/config/load.ts +12 -5
- package/src/config/rules.ts +11 -3
- package/src/config/types.ts +2 -0
- package/src/ref-utils.ts +4 -0
- package/src/resolve.ts +25 -3
- package/src/rules/common/__tests__/spec.test.ts +170 -0
- package/src/rules/common/assertions/__tests__/asserts.test.ts +18 -0
- package/src/rules/common/assertions/asserts.ts +20 -2
- package/src/rules/common/spec.ts +7 -0
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +32 -0
- package/src/rules/utils.ts +4 -0
- package/src/types/oas2.ts +11 -7
- package/src/types/oas3.ts +15 -10
- package/src/types/oas3_1.ts +1 -0
- package/src/types/redocly-yaml.ts +6 -0
- package/src/utils.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4238,10 +4238,11 @@ components:
|
|
|
4238
4238
|
type: integer
|
|
4239
4239
|
readOnly: true
|
|
4240
4240
|
dueReminderTime:
|
|
4241
|
-
description:
|
|
4241
|
+
description: Date and time at which a past due reminder event is triggered.
|
|
4242
4242
|
nullable: true
|
|
4243
|
-
|
|
4244
|
-
|
|
4243
|
+
type: string
|
|
4244
|
+
format: date-time
|
|
4245
|
+
readOnly: true
|
|
4245
4246
|
dueReminderNumber:
|
|
4246
4247
|
description: Number of past due reminder events triggered
|
|
4247
4248
|
type: integer
|
|
@@ -9524,10 +9525,11 @@ components:
|
|
|
9524
9525
|
allOf:
|
|
9525
9526
|
- $ref: '#/components/schemas/ServerTimestamp'
|
|
9526
9527
|
expirationReminderTime:
|
|
9527
|
-
description:
|
|
9528
|
+
description: Date and time at which an expiration reminder event is triggered.
|
|
9528
9529
|
nullable: true
|
|
9529
|
-
|
|
9530
|
-
|
|
9530
|
+
type: string
|
|
9531
|
+
format: date-time
|
|
9532
|
+
readOnly: true
|
|
9531
9533
|
expirationReminderNumber:
|
|
9532
9534
|
description: Number of expiration reminder events triggered
|
|
9533
9535
|
type: integer
|
|
@@ -10226,9 +10228,10 @@ components:
|
|
|
10226
10228
|
readOnly: true
|
|
10227
10229
|
reviewTime:
|
|
10228
10230
|
description: Date and time of manual review.
|
|
10231
|
+
type: string
|
|
10229
10232
|
nullable: true
|
|
10230
|
-
|
|
10231
|
-
|
|
10233
|
+
format: date-time
|
|
10234
|
+
readOnly: true
|
|
10232
10235
|
documentMatches:
|
|
10233
10236
|
$ref: '#/components/schemas/KycDocumentMatches'
|
|
10234
10237
|
_links:
|
|
@@ -10587,19 +10590,22 @@ components:
|
|
|
10587
10590
|
deliveryAddress:
|
|
10588
10591
|
description: Delivery address
|
|
10589
10592
|
nullable: true
|
|
10593
|
+
type: object
|
|
10590
10594
|
allOf:
|
|
10591
10595
|
- $ref: '#/components/schemas/ContactObject'
|
|
10592
10596
|
billingAddress:
|
|
10593
10597
|
description: Billing address
|
|
10594
10598
|
nullable: true
|
|
10599
|
+
type: object
|
|
10595
10600
|
allOf:
|
|
10596
10601
|
- $ref: '#/components/schemas/ContactObject'
|
|
10597
10602
|
riskMetadata:
|
|
10598
10603
|
nullable: true
|
|
10604
|
+
type: object
|
|
10599
10605
|
example: null
|
|
10600
|
-
description:
|
|
10601
|
-
Risk metadata.
|
|
10602
|
-
metadata captured when creating the payment token.
|
|
10606
|
+
description: |-
|
|
10607
|
+
Risk metadata.
|
|
10608
|
+
If null, the value would coalesce to the risk metadata captured when creating the payment token.
|
|
10603
10609
|
allOf:
|
|
10604
10610
|
- $ref: '#/components/schemas/RiskMetadata'
|
|
10605
10611
|
activationTime:
|
|
@@ -10842,18 +10848,16 @@ components:
|
|
|
10842
10848
|
type: string
|
|
10843
10849
|
format: date-time
|
|
10844
10850
|
invoiceTimeShift:
|
|
10845
|
-
description:
|
|
10846
|
-
You can shift issue time and due time of invoices for this
|
|
10847
|
-
subscription.
|
|
10851
|
+
description: |-
|
|
10852
|
+
You can shift issue time and due time of invoices for this subscription.
|
|
10848
10853
|
|
|
10849
|
-
This setting overrides plan settings.
|
|
10850
|
-
`null`.
|
|
10851
|
-
|
|
10852
|
-
To use multiple plans in one subscription they all must have the
|
|
10853
|
-
same billing period,
|
|
10854
|
+
This setting overrides plan settings.
|
|
10855
|
+
To use plan settings, set `null`.
|
|
10854
10856
|
|
|
10857
|
+
To use multiple plans in one subscription they all must have the same billing period,
|
|
10855
10858
|
this property allows to subscribe to different plans.
|
|
10856
10859
|
nullable: true
|
|
10860
|
+
type: object
|
|
10857
10861
|
example: null
|
|
10858
10862
|
allOf:
|
|
10859
10863
|
- $ref: '#/components/schemas/InvoiceTimeShift'
|
|
@@ -10898,19 +10902,21 @@ components:
|
|
|
10898
10902
|
type: integer
|
|
10899
10903
|
readOnly: true
|
|
10900
10904
|
renewalReminderTime:
|
|
10901
|
-
description: Time renewal reminder event will be triggered
|
|
10905
|
+
description: Time renewal reminder event will be triggered.
|
|
10902
10906
|
nullable: true
|
|
10903
|
-
|
|
10904
|
-
|
|
10907
|
+
type: string
|
|
10908
|
+
format: date-time
|
|
10909
|
+
readOnly: true
|
|
10905
10910
|
renewalReminderNumber:
|
|
10906
10911
|
description: Number of renewal reminder events triggered
|
|
10907
10912
|
type: integer
|
|
10908
10913
|
readOnly: true
|
|
10909
10914
|
trialReminderTime:
|
|
10910
|
-
description: Time renewal reminder event will be triggered
|
|
10915
|
+
description: Time renewal reminder event will be triggered.
|
|
10911
10916
|
nullable: true
|
|
10912
|
-
|
|
10913
|
-
|
|
10917
|
+
type: string
|
|
10918
|
+
format: date-time
|
|
10919
|
+
readOnly: true
|
|
10914
10920
|
trialReminderNumber:
|
|
10915
10921
|
description: Number of renewal reminder events triggered
|
|
10916
10922
|
type: integer
|
|
@@ -11807,10 +11813,12 @@ components:
|
|
|
11807
11813
|
paymentInstrument:
|
|
11808
11814
|
$ref: '#/components/schemas/PaymentInstrument'
|
|
11809
11815
|
billingAddress:
|
|
11810
|
-
description:
|
|
11811
|
-
Billing
|
|
11812
|
-
|
|
11816
|
+
description: |-
|
|
11817
|
+
Billing address.
|
|
11818
|
+
If not supplied, the billing address associated
|
|
11819
|
+
with the payment instrument is used, and then customer.
|
|
11813
11820
|
nullable: true
|
|
11821
|
+
type: object
|
|
11814
11822
|
allOf:
|
|
11815
11823
|
- $ref: '#/components/schemas/ContactObject'
|
|
11816
11824
|
requestId:
|
|
@@ -342,7 +342,7 @@ describe('resolveApis', () => {
|
|
|
342
342
|
});
|
|
343
343
|
|
|
344
344
|
describe('resolveConfig', () => {
|
|
345
|
-
it('should add recommended to top level by default', async () => {
|
|
345
|
+
it('should NOT add recommended to top level by default IF there is a config file', async () => {
|
|
346
346
|
const rawConfig: RawConfig = {
|
|
347
347
|
apis: {
|
|
348
348
|
petstore: {
|
|
@@ -373,7 +373,6 @@ describe('resolveConfig', () => {
|
|
|
373
373
|
expect(apis['petstore'].styleguide.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
|
|
374
374
|
|
|
375
375
|
expect(apis['petstore'].styleguide.rules).toEqual({
|
|
376
|
-
...(await recommendedStyleguidePreset).rules,
|
|
377
376
|
'operation-2xx-response': 'warn',
|
|
378
377
|
'operation-4xx-response': 'error',
|
|
379
378
|
});
|
|
@@ -41,25 +41,15 @@ export async function resolveConfig(rawConfig: RawConfig, configPath?: string):
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const resolver = new BaseResolver(getResolveConfig(rawConfig.resolve));
|
|
44
|
-
const configExtends = rawConfig?.styleguide?.extends ?? ['recommended'];
|
|
45
|
-
const recommendedFallback = !rawConfig?.styleguide?.extends;
|
|
46
|
-
const styleguideConfig = {
|
|
47
|
-
...rawConfig?.styleguide,
|
|
48
|
-
extends: configExtends,
|
|
49
|
-
recommendedFallback,
|
|
50
|
-
};
|
|
51
44
|
|
|
52
45
|
const apis = await resolveApis({
|
|
53
|
-
rawConfig
|
|
54
|
-
...rawConfig,
|
|
55
|
-
styleguide: styleguideConfig,
|
|
56
|
-
},
|
|
46
|
+
rawConfig,
|
|
57
47
|
configPath,
|
|
58
48
|
resolver,
|
|
59
49
|
});
|
|
60
50
|
|
|
61
51
|
const styleguide = await resolveStyleguideConfig({
|
|
62
|
-
styleguideConfig,
|
|
52
|
+
styleguideConfig: rawConfig.styleguide,
|
|
63
53
|
configPath,
|
|
64
54
|
resolver,
|
|
65
55
|
});
|
package/src/config/config.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { parseYaml, stringifyYaml } from '../js-yaml';
|
|
|
4
4
|
import { slash, doesYamlFileExist } from '../utils';
|
|
5
5
|
import { NormalizedProblem } from '../walk';
|
|
6
6
|
import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
|
|
7
|
-
import { env } from '../env';
|
|
7
|
+
import { isBrowser, env } from '../env';
|
|
8
8
|
|
|
9
9
|
import type { NodeType } from '../types';
|
|
10
10
|
import type {
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
ResolvedConfig,
|
|
18
18
|
ResolvedStyleguideConfig,
|
|
19
19
|
RuleConfig,
|
|
20
|
+
RuleSettings,
|
|
20
21
|
} from './types';
|
|
21
22
|
import { getResolveConfig } from './utils';
|
|
22
23
|
|
|
@@ -50,7 +51,7 @@ function getIgnoreFilePath(configFile?: string): string | undefined {
|
|
|
50
51
|
? path.join(path.dirname(configFile), IGNORE_FILE)
|
|
51
52
|
: path.join(configFile, IGNORE_FILE);
|
|
52
53
|
} else {
|
|
53
|
-
return
|
|
54
|
+
return isBrowser ? undefined : path.join(process.cwd(), IGNORE_FILE);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -183,7 +184,7 @@ export class StyleguideConfig {
|
|
|
183
184
|
return extendedTypes;
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
getRuleSettings(ruleId: string, oasVersion: OasVersion) {
|
|
187
|
+
getRuleSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
|
|
187
188
|
this._usedRules.add(ruleId);
|
|
188
189
|
this._usedVersions.add(oasVersion);
|
|
189
190
|
const settings = this.rules[oasVersion][ruleId] || 'off';
|
|
@@ -196,7 +197,7 @@ export class StyleguideConfig {
|
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
getPreprocessorSettings(ruleId: string, oasVersion: OasVersion) {
|
|
200
|
+
getPreprocessorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
|
|
200
201
|
this._usedRules.add(ruleId);
|
|
201
202
|
this._usedVersions.add(oasVersion);
|
|
202
203
|
|
|
@@ -210,7 +211,7 @@ export class StyleguideConfig {
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
getDecoratorSettings(ruleId: string, oasVersion: OasVersion) {
|
|
214
|
+
getDecoratorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
|
|
214
215
|
this._usedRules.add(ruleId);
|
|
215
216
|
this._usedVersions.add(oasVersion);
|
|
216
217
|
const settings = this.decorators[oasVersion][ruleId] || 'off';
|
package/src/config/load.ts
CHANGED
|
@@ -15,18 +15,21 @@ async function addConfigMetadata({
|
|
|
15
15
|
customExtends,
|
|
16
16
|
configPath,
|
|
17
17
|
tokens,
|
|
18
|
+
files,
|
|
19
|
+
region,
|
|
18
20
|
}: {
|
|
19
21
|
rawConfig: RawConfig;
|
|
20
22
|
customExtends?: string[];
|
|
21
23
|
configPath?: string;
|
|
22
24
|
tokens?: RegionalTokenWithValidity[];
|
|
25
|
+
files?: string[];
|
|
26
|
+
region?: Region;
|
|
23
27
|
}): Promise<Config> {
|
|
24
28
|
if (customExtends !== undefined) {
|
|
25
29
|
rawConfig.styleguide = rawConfig.styleguide || {};
|
|
26
30
|
rawConfig.styleguide.extends = customExtends;
|
|
27
31
|
} else if (isEmptyObject(rawConfig)) {
|
|
28
|
-
|
|
29
|
-
// rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
|
|
32
|
+
rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
if (tokens?.length) {
|
|
@@ -58,7 +61,10 @@ async function addConfigMetadata({
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
return resolveConfig(
|
|
64
|
+
return resolveConfig(
|
|
65
|
+
{ ...rawConfig, files: files ?? rawConfig.files, region: region ?? rawConfig.region },
|
|
66
|
+
configPath
|
|
67
|
+
);
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
export async function loadConfig(
|
|
@@ -71,8 +77,7 @@ export async function loadConfig(
|
|
|
71
77
|
} = {}
|
|
72
78
|
): Promise<Config> {
|
|
73
79
|
const { configPath = findConfig(), customExtends, processRawConfig, files, region } = options;
|
|
74
|
-
const
|
|
75
|
-
const rawConfig = { ...config, files: files ?? config.files, region: region ?? config.region };
|
|
80
|
+
const rawConfig = await getConfig(configPath, processRawConfig);
|
|
76
81
|
|
|
77
82
|
const redoclyClient = new RedoclyClient();
|
|
78
83
|
const tokens = await redoclyClient.getTokens();
|
|
@@ -82,6 +87,8 @@ export async function loadConfig(
|
|
|
82
87
|
customExtends,
|
|
83
88
|
configPath,
|
|
84
89
|
tokens,
|
|
90
|
+
files,
|
|
91
|
+
region,
|
|
85
92
|
});
|
|
86
93
|
}
|
|
87
94
|
|
package/src/config/rules.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { RuleSet, OasVersion } from '../oas-types';
|
|
2
2
|
import { StyleguideConfig } from './config';
|
|
3
3
|
import { isDefined } from '../utils';
|
|
4
|
+
import type { ProblemSeverity } from '../walk';
|
|
5
|
+
|
|
6
|
+
type InitializedRule = {
|
|
7
|
+
severity: ProblemSeverity;
|
|
8
|
+
ruleId: string;
|
|
9
|
+
visitor: any;
|
|
10
|
+
};
|
|
4
11
|
|
|
5
12
|
export function initRules<T extends Function, P extends RuleSet<T>>(
|
|
6
13
|
rules: P[],
|
|
7
14
|
config: StyleguideConfig,
|
|
8
15
|
type: 'rules' | 'preprocessors' | 'decorators',
|
|
9
16
|
oasVersion: OasVersion
|
|
10
|
-
) {
|
|
17
|
+
): InitializedRule[] {
|
|
11
18
|
return rules
|
|
12
19
|
.flatMap((ruleset) =>
|
|
13
20
|
Object.keys(ruleset).map((ruleId) => {
|
|
@@ -23,19 +30,20 @@ export function initRules<T extends Function, P extends RuleSet<T>>(
|
|
|
23
30
|
if (ruleSettings.severity === 'off') {
|
|
24
31
|
return undefined;
|
|
25
32
|
}
|
|
33
|
+
const severity: ProblemSeverity = ruleSettings.severity;
|
|
26
34
|
|
|
27
35
|
const visitors = rule(ruleSettings);
|
|
28
36
|
|
|
29
37
|
if (Array.isArray(visitors)) {
|
|
30
38
|
return visitors.map((visitor: any) => ({
|
|
31
|
-
severity
|
|
39
|
+
severity,
|
|
32
40
|
ruleId,
|
|
33
41
|
visitor: visitor,
|
|
34
42
|
}));
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
return {
|
|
38
|
-
severity
|
|
46
|
+
severity,
|
|
39
47
|
ruleId,
|
|
40
48
|
visitor: visitors, // note: actually it is only one visitor object
|
|
41
49
|
};
|
package/src/config/types.ts
CHANGED
package/src/ref-utils.ts
CHANGED
package/src/resolve.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { OasRef } from './typings/openapi';
|
|
4
|
-
import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl } from './ref-utils';
|
|
4
|
+
import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl, isAnchor } from './ref-utils';
|
|
5
5
|
import type { YAMLNode, LoadOptions } from 'yaml-ast-parser';
|
|
6
6
|
import { NormalizedNodeType, isNamedType } from './types';
|
|
7
|
-
import { readFileFromUrl, parseYaml } from './utils';
|
|
7
|
+
import { readFileFromUrl, parseYaml, nextTick } from './utils';
|
|
8
8
|
import { ResolveConfig } from './config/types';
|
|
9
9
|
|
|
10
10
|
export type CollectedRefs = Map<string /* absoluteFilePath */, Document>;
|
|
@@ -237,6 +237,7 @@ export async function resolveDocument(opts: {
|
|
|
237
237
|
type: any
|
|
238
238
|
) {
|
|
239
239
|
const rootNodeDocAbsoluteRef = rootNodeDocument.source.absoluteRef;
|
|
240
|
+
const anchorRefsMap: Map<string, any> = new Map();
|
|
240
241
|
|
|
241
242
|
walk(rootNode, type, rootNodeDocAbsoluteRef + rootNodePointer);
|
|
242
243
|
|
|
@@ -252,6 +253,11 @@ export async function resolveDocument(opts: {
|
|
|
252
253
|
|
|
253
254
|
seedNodes.add(nodeId);
|
|
254
255
|
|
|
256
|
+
const [_, anchor] = Object.entries(node).find(([key]) => key === '$anchor') || [];
|
|
257
|
+
if (anchor) {
|
|
258
|
+
anchorRefsMap.set(`#${anchor}`, node);
|
|
259
|
+
}
|
|
260
|
+
|
|
255
261
|
if (Array.isArray(node)) {
|
|
256
262
|
const itemsType = type.items;
|
|
257
263
|
// we continue resolving unknown types, but stop early on known scalars
|
|
@@ -313,6 +319,22 @@ export async function resolveDocument(opts: {
|
|
|
313
319
|
if (hasRef(refStack.prev, ref)) {
|
|
314
320
|
throw new Error('Self-referencing circular pointer');
|
|
315
321
|
}
|
|
322
|
+
|
|
323
|
+
if (isAnchor(ref.$ref)) {
|
|
324
|
+
// Wait for all anchors in the document to be collected firstly.
|
|
325
|
+
await nextTick();
|
|
326
|
+
const resolvedRef: ResolvedRef = {
|
|
327
|
+
resolved: true,
|
|
328
|
+
isRemote: false,
|
|
329
|
+
node: anchorRefsMap.get(ref.$ref),
|
|
330
|
+
document,
|
|
331
|
+
nodePointer: ref.$ref,
|
|
332
|
+
};
|
|
333
|
+
const refId = makeRefId(document.source.absoluteRef, ref.$ref);
|
|
334
|
+
resolvedRefMap.set(refId, resolvedRef);
|
|
335
|
+
return resolvedRef;
|
|
336
|
+
}
|
|
337
|
+
|
|
316
338
|
const { uri, pointer } = parseRef(ref.$ref);
|
|
317
339
|
const isRemote = uri !== null;
|
|
318
340
|
let targetDoc: Document;
|
|
@@ -336,7 +358,7 @@ export async function resolveDocument(opts: {
|
|
|
336
358
|
}
|
|
337
359
|
|
|
338
360
|
let resolvedRef: ResolvedRef = {
|
|
339
|
-
resolved: true
|
|
361
|
+
resolved: true,
|
|
340
362
|
document: targetDoc,
|
|
341
363
|
isRemote,
|
|
342
364
|
node: document.parsed,
|
|
@@ -138,6 +138,176 @@ describe('Oas3 spec', () => {
|
|
|
138
138
|
]
|
|
139
139
|
`);
|
|
140
140
|
});
|
|
141
|
+
|
|
142
|
+
it('should report on nullable without type', async () => {
|
|
143
|
+
const document = parseYamlToDocument(
|
|
144
|
+
outdent`
|
|
145
|
+
openapi: 3.0.0
|
|
146
|
+
components:
|
|
147
|
+
requestBodies:
|
|
148
|
+
TestRequestBody:
|
|
149
|
+
content:
|
|
150
|
+
application/json:
|
|
151
|
+
schema:
|
|
152
|
+
nullable: true
|
|
153
|
+
`,
|
|
154
|
+
'foobar.yaml'
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const results = await lintDocument({
|
|
158
|
+
externalRefResolver: new BaseResolver(),
|
|
159
|
+
document,
|
|
160
|
+
config: await makeConfig({ spec: 'error' }),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
164
|
+
Array [
|
|
165
|
+
Object {
|
|
166
|
+
"from": undefined,
|
|
167
|
+
"location": Array [
|
|
168
|
+
Object {
|
|
169
|
+
"pointer": "#/",
|
|
170
|
+
"reportOnKey": true,
|
|
171
|
+
"source": "foobar.yaml",
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
"message": "The field \`paths\` must be present on this level.",
|
|
175
|
+
"ruleId": "spec",
|
|
176
|
+
"severity": "error",
|
|
177
|
+
"suggest": Array [],
|
|
178
|
+
},
|
|
179
|
+
Object {
|
|
180
|
+
"from": undefined,
|
|
181
|
+
"location": Array [
|
|
182
|
+
Object {
|
|
183
|
+
"pointer": "#/",
|
|
184
|
+
"reportOnKey": true,
|
|
185
|
+
"source": "foobar.yaml",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
"message": "The field \`info\` must be present on this level.",
|
|
189
|
+
"ruleId": "spec",
|
|
190
|
+
"severity": "error",
|
|
191
|
+
"suggest": Array [],
|
|
192
|
+
},
|
|
193
|
+
Object {
|
|
194
|
+
"location": Array [
|
|
195
|
+
Object {
|
|
196
|
+
"pointer": "#/components/requestBodies/TestRequestBody/content/application~1json/schema/nullable",
|
|
197
|
+
"reportOnKey": false,
|
|
198
|
+
"source": "foobar.yaml",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
"message": "The \`type\` field must be defined when the \`nullable\` field is used.",
|
|
202
|
+
"ruleId": "spec",
|
|
203
|
+
"severity": "error",
|
|
204
|
+
"suggest": Array [],
|
|
205
|
+
},
|
|
206
|
+
]
|
|
207
|
+
`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should report on nullable with type defined in allOf', async () => {
|
|
211
|
+
const document = parseYamlToDocument(
|
|
212
|
+
outdent`
|
|
213
|
+
openapi: 3.0.0
|
|
214
|
+
components:
|
|
215
|
+
requestBodies:
|
|
216
|
+
TestRequestBody:
|
|
217
|
+
content:
|
|
218
|
+
application/json:
|
|
219
|
+
schema:
|
|
220
|
+
nullable: true
|
|
221
|
+
allOf:
|
|
222
|
+
- $ref: "#/components/requestBodies/TestSchema"
|
|
223
|
+
schemas:
|
|
224
|
+
TestSchema:
|
|
225
|
+
title: TestSchema
|
|
226
|
+
type: object
|
|
227
|
+
`,
|
|
228
|
+
'foobar.yaml'
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const results = await lintDocument({
|
|
232
|
+
externalRefResolver: new BaseResolver(),
|
|
233
|
+
document,
|
|
234
|
+
config: await makeConfig({ spec: 'error' }),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
238
|
+
Array [
|
|
239
|
+
Object {
|
|
240
|
+
"from": undefined,
|
|
241
|
+
"location": Array [
|
|
242
|
+
Object {
|
|
243
|
+
"pointer": "#/",
|
|
244
|
+
"reportOnKey": true,
|
|
245
|
+
"source": "foobar.yaml",
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
"message": "The field \`paths\` must be present on this level.",
|
|
249
|
+
"ruleId": "spec",
|
|
250
|
+
"severity": "error",
|
|
251
|
+
"suggest": Array [],
|
|
252
|
+
},
|
|
253
|
+
Object {
|
|
254
|
+
"from": undefined,
|
|
255
|
+
"location": Array [
|
|
256
|
+
Object {
|
|
257
|
+
"pointer": "#/",
|
|
258
|
+
"reportOnKey": true,
|
|
259
|
+
"source": "foobar.yaml",
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
"message": "The field \`info\` must be present on this level.",
|
|
263
|
+
"ruleId": "spec",
|
|
264
|
+
"severity": "error",
|
|
265
|
+
"suggest": Array [],
|
|
266
|
+
},
|
|
267
|
+
Object {
|
|
268
|
+
"location": Array [
|
|
269
|
+
Object {
|
|
270
|
+
"pointer": "#/components/requestBodies/TestRequestBody/content/application~1json/schema/nullable",
|
|
271
|
+
"reportOnKey": false,
|
|
272
|
+
"source": "foobar.yaml",
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
"message": "The \`type\` field must be defined when the \`nullable\` field is used.",
|
|
276
|
+
"ruleId": "spec",
|
|
277
|
+
"severity": "error",
|
|
278
|
+
"suggest": Array [],
|
|
279
|
+
},
|
|
280
|
+
Object {
|
|
281
|
+
"from": undefined,
|
|
282
|
+
"location": Array [
|
|
283
|
+
Object {
|
|
284
|
+
"pointer": "#/components/requestBodies/schemas",
|
|
285
|
+
"reportOnKey": true,
|
|
286
|
+
"source": "foobar.yaml",
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
"message": "The field \`content\` must be present on this level.",
|
|
290
|
+
"ruleId": "spec",
|
|
291
|
+
"severity": "error",
|
|
292
|
+
"suggest": Array [],
|
|
293
|
+
},
|
|
294
|
+
Object {
|
|
295
|
+
"from": undefined,
|
|
296
|
+
"location": Array [
|
|
297
|
+
Object {
|
|
298
|
+
"pointer": "#/components/requestBodies/schemas/TestSchema",
|
|
299
|
+
"reportOnKey": true,
|
|
300
|
+
"source": "foobar.yaml",
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
"message": "Property \`TestSchema\` is not expected here.",
|
|
304
|
+
"ruleId": "spec",
|
|
305
|
+
"severity": "error",
|
|
306
|
+
"suggest": Array [],
|
|
307
|
+
},
|
|
308
|
+
]
|
|
309
|
+
`);
|
|
310
|
+
});
|
|
141
311
|
});
|
|
142
312
|
|
|
143
313
|
describe('Oas3.1 spec', () => {
|
|
@@ -43,6 +43,24 @@ describe('oas3 assertions', () => {
|
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
describe('notPattern', () => {
|
|
47
|
+
it('value should not match regex pattern', () => {
|
|
48
|
+
expect(asserts.notPattern('test string', '/test me/', baseLocation)).toEqual([]);
|
|
49
|
+
expect(asserts.notPattern('test string', '/test/', baseLocation)).toEqual([
|
|
50
|
+
{ location: baseLocation, message: '"test string" should not match a regex /test/' },
|
|
51
|
+
]);
|
|
52
|
+
expect(
|
|
53
|
+
asserts.notPattern(['test string', 'test me'], '/test other/', baseLocation)
|
|
54
|
+
).toEqual([]);
|
|
55
|
+
expect(asserts.notPattern(['test string', 'test me'], '/test me/', baseLocation)).toEqual([
|
|
56
|
+
{
|
|
57
|
+
message: '"test me" should not match a regex /test me/',
|
|
58
|
+
location: baseLocation.key(),
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
46
64
|
describe('ref', () => {
|
|
47
65
|
it('value should have ref', () => {
|
|
48
66
|
expect(asserts.ref({ $ref: 'text' }, true, baseLocation, { $ref: 'text' })).toEqual([]);
|