@redocly/openapi-core 1.0.0-beta.111 → 1.0.0-beta.113
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/all.js +0 -1
- package/lib/config/config-resolvers.js +22 -18
- package/lib/config/config.d.ts +4 -10
- package/lib/config/config.js +1 -1
- package/lib/config/load.d.ts +1 -1
- package/lib/config/load.js +10 -10
- package/lib/config/minimal.js +0 -1
- package/lib/config/recommended.js +0 -1
- 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 +22 -5
- package/lib/rules/common/assertions/asserts.js +25 -0
- package/lib/rules/common/assertions/index.d.ts +27 -2
- package/lib/rules/common/assertions/index.js +6 -29
- package/lib/rules/common/assertions/utils.d.ts +7 -14
- package/lib/rules/common/assertions/utils.js +129 -97
- package/lib/rules/common/spec.js +6 -0
- package/lib/rules/oas2/index.d.ts +0 -1
- package/lib/rules/oas2/index.js +0 -2
- package/lib/rules/oas3/index.js +0 -2
- 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 +49 -27
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +13 -1
- package/lib/visitors.d.ts +2 -1
- package/lib/visitors.js +1 -0
- package/lib/walk.js +7 -1
- package/package.json +1 -1
- package/src/__tests__/bundle.test.ts +46 -0
- package/src/__tests__/lint.test.ts +24 -5
- package/src/benchmark/benches/rebilly.yaml +36 -28
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +1 -3
- package/src/config/__tests__/config-resolvers.test.ts +6 -7
- package/src/config/__tests__/fixtures/load-redocly.yaml +2 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +6 -5
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +0 -1
- package/src/config/__tests__/load.test.ts +4 -1
- package/src/config/all.ts +0 -1
- package/src/config/config-resolvers.ts +44 -31
- package/src/config/config.ts +6 -5
- package/src/config/load.ts +19 -9
- package/src/config/minimal.ts +0 -1
- package/src/config/recommended.ts +0 -1
- 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 +7 -3
- package/src/rules/common/assertions/__tests__/index.test.ts +41 -20
- package/src/rules/common/assertions/__tests__/utils.test.ts +43 -17
- package/src/rules/common/assertions/asserts.ts +60 -8
- package/src/rules/common/assertions/index.ts +36 -46
- package/src/rules/common/assertions/utils.ts +204 -127
- package/src/rules/common/spec.ts +7 -0
- package/src/rules/oas2/index.ts +0 -2
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +32 -0
- package/src/rules/oas3/index.ts +0 -2
- 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 +49 -29
- package/src/utils.ts +11 -0
- package/src/visitors.ts +7 -1
- package/src/walk.ts +8 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/info-description.d.ts +0 -2
- package/lib/rules/common/info-description.js +0 -12
- package/src/rules/common/__tests__/info-description.test.ts +0 -102
- package/src/rules/common/info-description.ts +0 -10
package/lib/utils.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.pickDefined = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
12
|
+
exports.nextTick = exports.pickDefined = exports.keysOf = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
const path_1 = require("path");
|
|
15
15
|
const minimatch = require("minimatch");
|
|
@@ -205,6 +205,12 @@ function identity(value) {
|
|
|
205
205
|
return value;
|
|
206
206
|
}
|
|
207
207
|
exports.identity = identity;
|
|
208
|
+
function keysOf(obj) {
|
|
209
|
+
if (!obj)
|
|
210
|
+
return [];
|
|
211
|
+
return Object.keys(obj);
|
|
212
|
+
}
|
|
213
|
+
exports.keysOf = keysOf;
|
|
208
214
|
function pickDefined(obj) {
|
|
209
215
|
if (!obj)
|
|
210
216
|
return undefined;
|
|
@@ -217,3 +223,9 @@ function pickDefined(obj) {
|
|
|
217
223
|
return res;
|
|
218
224
|
}
|
|
219
225
|
exports.pickDefined = pickDefined;
|
|
226
|
+
function nextTick() {
|
|
227
|
+
new Promise((resolve) => {
|
|
228
|
+
setTimeout(resolve);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
exports.nextTick = nextTick;
|
package/lib/visitors.d.ts
CHANGED
|
@@ -4,11 +4,12 @@ import type { NormalizedNodeType } from './types';
|
|
|
4
4
|
import type { Stack } from './utils';
|
|
5
5
|
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
6
6
|
import type { Location } from './ref-utils';
|
|
7
|
+
export declare type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
|
|
7
8
|
export declare type VisitFunction<T> = (node: T, ctx: UserContext & {
|
|
8
9
|
ignoreNextVisitorsOnNode: () => void;
|
|
9
10
|
}, parents?: any, context?: any) => void;
|
|
10
11
|
declare type VisitRefFunction = (node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) => void;
|
|
11
|
-
declare type SkipFunction<T> = (node: T, key: string | number) => boolean;
|
|
12
|
+
declare type SkipFunction<T> = (node: T, key: string | number, ctx: SkipFunctionContext) => boolean;
|
|
12
13
|
declare type VisitObject<T> = {
|
|
13
14
|
enter?: VisitFunction<T>;
|
|
14
15
|
leave?: VisitFunction<T>;
|
package/lib/visitors.js
CHANGED
package/lib/walk.js
CHANGED
|
@@ -112,7 +112,13 @@ function walkDocument(opts) {
|
|
|
112
112
|
location: resolvedLocation,
|
|
113
113
|
nextLevelTypeActivated: null,
|
|
114
114
|
withParentNode: (_g = (_f = context.parent) === null || _f === void 0 ? void 0 : _f.activatedOn) === null || _g === void 0 ? void 0 : _g.value.node,
|
|
115
|
-
skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) ||
|
|
115
|
+
skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) ||
|
|
116
|
+
(skip === null || skip === void 0 ? void 0 : skip(resolvedNode, key, {
|
|
117
|
+
location,
|
|
118
|
+
rawLocation,
|
|
119
|
+
resolve,
|
|
120
|
+
rawNode: node,
|
|
121
|
+
})))) !== null && _k !== void 0 ? _k : false,
|
|
116
122
|
};
|
|
117
123
|
context.activatedOn = utils_1.pushStack(context.activatedOn, activatedOn);
|
|
118
124
|
let ctx = context.parent;
|
package/package.json
CHANGED
|
@@ -187,4 +187,50 @@ describe('bundle', () => {
|
|
|
187
187
|
expect(problems).toHaveLength(0);
|
|
188
188
|
expect(parsedMeta).toMatchSnapshot();
|
|
189
189
|
});
|
|
190
|
+
|
|
191
|
+
it('should bundle refs using $anchors', async () => {
|
|
192
|
+
const testDocument = parseYamlToDocument(
|
|
193
|
+
outdent`
|
|
194
|
+
openapi: 3.1.0
|
|
195
|
+
components:
|
|
196
|
+
schemas:
|
|
197
|
+
User:
|
|
198
|
+
type: object
|
|
199
|
+
properties:
|
|
200
|
+
profile:
|
|
201
|
+
$ref: '#user-profile'
|
|
202
|
+
UserProfile:
|
|
203
|
+
$anchor: user-profile
|
|
204
|
+
type: string
|
|
205
|
+
`,
|
|
206
|
+
''
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const config = await makeConfig({});
|
|
210
|
+
|
|
211
|
+
const {
|
|
212
|
+
bundle: { parsed },
|
|
213
|
+
problems,
|
|
214
|
+
} = await bundleDocument({
|
|
215
|
+
document: testDocument,
|
|
216
|
+
config: config,
|
|
217
|
+
externalRefResolver: new BaseResolver(),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(problems).toHaveLength(0);
|
|
221
|
+
expect(parsed).toMatchInlineSnapshot(`
|
|
222
|
+
openapi: 3.1.0
|
|
223
|
+
components:
|
|
224
|
+
schemas:
|
|
225
|
+
User:
|
|
226
|
+
type: object
|
|
227
|
+
properties:
|
|
228
|
+
profile:
|
|
229
|
+
$ref: '#user-profile'
|
|
230
|
+
UserProfile:
|
|
231
|
+
$anchor: user-profile
|
|
232
|
+
type: string
|
|
233
|
+
|
|
234
|
+
`);
|
|
235
|
+
});
|
|
190
236
|
});
|
|
@@ -61,11 +61,13 @@ describe('lint', () => {
|
|
|
61
61
|
path-http-verbs-order: error
|
|
62
62
|
boolean-parameter-prefixes: off
|
|
63
63
|
assert/operation-summary-length:
|
|
64
|
-
subject:
|
|
65
|
-
|
|
64
|
+
subject:
|
|
65
|
+
type: Operation
|
|
66
|
+
property: summary
|
|
66
67
|
message: Operation summary should start with an active verb
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
assertions:
|
|
69
|
+
local/checkWordsCount:
|
|
70
|
+
min: 3
|
|
69
71
|
features.openapi:
|
|
70
72
|
showConsole: true
|
|
71
73
|
layout:
|
|
@@ -247,7 +249,24 @@ describe('lint', () => {
|
|
|
247
249
|
);
|
|
248
250
|
const results = await lintConfig({ document });
|
|
249
251
|
|
|
250
|
-
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
252
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
253
|
+
Array [
|
|
254
|
+
Object {
|
|
255
|
+
"from": undefined,
|
|
256
|
+
"location": Array [
|
|
257
|
+
Object {
|
|
258
|
+
"pointer": "#/referenceDocs",
|
|
259
|
+
"reportOnKey": true,
|
|
260
|
+
"source": "",
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
"message": "Property \`referenceDocs\` is not expected here.",
|
|
264
|
+
"ruleId": "configuration spec",
|
|
265
|
+
"severity": "error",
|
|
266
|
+
"suggest": Array [],
|
|
267
|
+
},
|
|
268
|
+
]
|
|
269
|
+
`);
|
|
251
270
|
});
|
|
252
271
|
|
|
253
272
|
it("'plugins' shouldn't be allowed in 'apis'", async () => {
|
|
@@ -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:
|
|
@@ -39,7 +39,6 @@ Object {
|
|
|
39
39
|
"assertions": "warn",
|
|
40
40
|
"boolean-parameter-prefixes": "error",
|
|
41
41
|
"info-contact": "off",
|
|
42
|
-
"info-description": "warn",
|
|
43
42
|
"info-license": "warn",
|
|
44
43
|
"info-license-url": "warn",
|
|
45
44
|
"local/operation-id-not-test": "error",
|
|
@@ -73,7 +72,7 @@ Object {
|
|
|
73
72
|
}
|
|
74
73
|
`;
|
|
75
74
|
|
|
76
|
-
exports[`resolveStyleguideConfig should resolve extends with local file config
|
|
75
|
+
exports[`resolveStyleguideConfig should resolve extends with local file config which contains path to nested config 1`] = `
|
|
77
76
|
Object {
|
|
78
77
|
"decorators": Object {},
|
|
79
78
|
"doNotResolveExamples": undefined,
|
|
@@ -129,7 +128,6 @@ Object {
|
|
|
129
128
|
],
|
|
130
129
|
"boolean-parameter-prefixes": "error",
|
|
131
130
|
"info-contact": "off",
|
|
132
|
-
"info-description": "warn",
|
|
133
131
|
"info-license": "warn",
|
|
134
132
|
"info-license-url": "warn",
|
|
135
133
|
"local/operation-id-not-test": "error",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { colorize } from '../../logger';
|
|
2
|
-
import { asserts } from '../../rules/common/assertions/asserts';
|
|
2
|
+
import { Asserts, asserts } from '../../rules/common/assertions/asserts';
|
|
3
3
|
import { resolveStyleguideConfig, resolveApis, resolveConfig } from '../config-resolvers';
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
@@ -100,7 +100,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
100
100
|
}).toThrow('Circular dependency in config file');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
it('should resolve extends with local file config
|
|
103
|
+
it('should resolve extends with local file config which contains path to nested config', async () => {
|
|
104
104
|
const styleguideConfig = {
|
|
105
105
|
extends: ['local-config-with-file.yaml'],
|
|
106
106
|
};
|
|
@@ -143,7 +143,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
143
143
|
|
|
144
144
|
expect(plugins).toBeDefined();
|
|
145
145
|
expect(plugins?.length).toBe(2);
|
|
146
|
-
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
146
|
+
expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
it('should throw error when custom assertion load not exist plugin', async () => {
|
|
@@ -163,7 +163,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
166
|
+
expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
it('should correctly merge assertions from nested config', async () => {
|
|
@@ -197,7 +197,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
197
197
|
]);
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
-
it('should resolve extends with url file config
|
|
200
|
+
it('should resolve extends with url file config which contains path to nested config', async () => {
|
|
201
201
|
const styleguideConfig = {
|
|
202
202
|
// This points to ./fixtures/resolve-remote-configs/remote-config.yaml
|
|
203
203
|
extends: [
|
|
@@ -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
|
});
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
lint:
|
|
2
1
|
rules:
|
|
3
2
|
no-invalid-media-type-examples: warn
|
|
4
3
|
operation-4xx-response: off
|
|
5
4
|
assert/tag-description:
|
|
6
|
-
subject:
|
|
7
|
-
|
|
5
|
+
subject:
|
|
6
|
+
type: Tag
|
|
7
|
+
property: description
|
|
8
8
|
message: Tag description must have at least 3 words.
|
|
9
9
|
severity: error
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
assertions:
|
|
11
|
+
test-plugin/checkWordsCount:
|
|
12
|
+
min: 3
|
|
12
13
|
plugins:
|
|
13
14
|
- plugin.js
|
|
14
15
|
extends:
|
|
@@ -49,7 +49,10 @@ describe('loadConfig', () => {
|
|
|
49
49
|
|
|
50
50
|
it('should call callback if such passed', async () => {
|
|
51
51
|
const mockFn = jest.fn();
|
|
52
|
-
await loadConfig({
|
|
52
|
+
await loadConfig({
|
|
53
|
+
configPath: path.join(__dirname, './fixtures/load-redocly.yaml'),
|
|
54
|
+
processRawConfig: mockFn,
|
|
55
|
+
});
|
|
53
56
|
expect(mockFn).toHaveBeenCalled();
|
|
54
57
|
});
|
|
55
58
|
});
|
package/src/config/all.ts
CHANGED
|
@@ -22,10 +22,16 @@ import type {
|
|
|
22
22
|
DeprecatedInRawConfig,
|
|
23
23
|
} from './types';
|
|
24
24
|
import { isBrowser } from '../env';
|
|
25
|
-
import { isNotString, isString, isDefined, parseYaml } from '../utils';
|
|
25
|
+
import { isNotString, isString, isDefined, parseYaml, keysOf } from '../utils';
|
|
26
26
|
import { Config } from './config';
|
|
27
27
|
import { colorize, logger } from '../logger';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
Asserts,
|
|
30
|
+
AssertionFn,
|
|
31
|
+
asserts,
|
|
32
|
+
buildAssertCustomFunction,
|
|
33
|
+
} from '../rules/common/assertions/asserts';
|
|
34
|
+
import type { Assertion, AssertionDefinition, RawAssertion } from '../rules/common/assertions';
|
|
29
35
|
|
|
30
36
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
31
37
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
|
@@ -35,25 +41,15 @@ export async function resolveConfig(rawConfig: RawConfig, configPath?: string):
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
const resolver = new BaseResolver(getResolveConfig(rawConfig.resolve));
|
|
38
|
-
const configExtends = rawConfig?.styleguide?.extends ?? ['recommended'];
|
|
39
|
-
const recommendedFallback = !rawConfig?.styleguide?.extends;
|
|
40
|
-
const styleguideConfig = {
|
|
41
|
-
...rawConfig?.styleguide,
|
|
42
|
-
extends: configExtends,
|
|
43
|
-
recommendedFallback,
|
|
44
|
-
};
|
|
45
44
|
|
|
46
45
|
const apis = await resolveApis({
|
|
47
|
-
rawConfig
|
|
48
|
-
...rawConfig,
|
|
49
|
-
styleguide: styleguideConfig,
|
|
50
|
-
},
|
|
46
|
+
rawConfig,
|
|
51
47
|
configPath,
|
|
52
48
|
resolver,
|
|
53
49
|
});
|
|
54
50
|
|
|
55
51
|
const styleguide = await resolveStyleguideConfig({
|
|
56
|
-
styleguideConfig,
|
|
52
|
+
styleguideConfig: rawConfig.styleguide,
|
|
57
53
|
configPath,
|
|
58
54
|
resolver,
|
|
59
55
|
});
|
|
@@ -409,26 +405,17 @@ function groupStyleguideAssertionRules({
|
|
|
409
405
|
const transformedRules: Record<string, RuleConfig> = {};
|
|
410
406
|
|
|
411
407
|
// Collect assertion rules
|
|
412
|
-
const assertions = [];
|
|
408
|
+
const assertions: Assertion[] = [];
|
|
413
409
|
for (const [ruleKey, rule] of Object.entries(rules)) {
|
|
414
410
|
if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
|
|
415
|
-
const assertion = rule;
|
|
411
|
+
const assertion = rule as RawAssertion;
|
|
412
|
+
|
|
416
413
|
if (plugins) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
throw Error(colorize.red(`Plugin ${colorize.blue(pluginId)} isn't found.`));
|
|
423
|
-
}
|
|
424
|
-
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
425
|
-
throw Error(
|
|
426
|
-
`Plugin ${colorize.red(
|
|
427
|
-
pluginId
|
|
428
|
-
)} doesn't export assertions function with name ${colorize.red(fn)}.`
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
asserts[field] = buildAssertCustomFunction(plugin.assertions[fn]);
|
|
414
|
+
registerCustomAssertions(plugins, assertion);
|
|
415
|
+
|
|
416
|
+
// We may have custom assertion inside where block
|
|
417
|
+
for (const context of assertion.where || []) {
|
|
418
|
+
registerCustomAssertions(plugins, context);
|
|
432
419
|
}
|
|
433
420
|
}
|
|
434
421
|
assertions.push({
|
|
@@ -446,3 +433,29 @@ function groupStyleguideAssertionRules({
|
|
|
446
433
|
|
|
447
434
|
return transformedRules;
|
|
448
435
|
}
|
|
436
|
+
|
|
437
|
+
function registerCustomAssertions(plugins: Plugin[], assertion: AssertionDefinition) {
|
|
438
|
+
for (const field of keysOf(assertion.assertions)) {
|
|
439
|
+
const [pluginId, fn] = field.split('/');
|
|
440
|
+
|
|
441
|
+
if (!pluginId || !fn) continue;
|
|
442
|
+
|
|
443
|
+
const plugin = plugins.find((plugin) => plugin.id === pluginId);
|
|
444
|
+
|
|
445
|
+
if (!plugin) {
|
|
446
|
+
throw Error(colorize.red(`Plugin ${colorize.blue(pluginId)} isn't found.`));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
450
|
+
throw Error(
|
|
451
|
+
`Plugin ${colorize.red(
|
|
452
|
+
pluginId
|
|
453
|
+
)} doesn't export assertions function with name ${colorize.red(fn)}.`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
(asserts as Asserts & { [name: string]: AssertionFn })[field] = buildAssertCustomFunction(
|
|
458
|
+
plugin.assertions[fn]
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
}
|
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';
|