@redocly/openapi-core 1.15.0 → 1.17.0
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/CHANGELOG.md +16 -0
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +2 -2
- package/lib/config/config.d.ts +0 -6
- package/lib/config/config.js +1 -19
- package/lib/config/load.d.ts +12 -3
- package/lib/config/load.js +34 -14
- package/lib/config/utils.d.ts +1 -0
- package/lib/config/utils.js +5 -1
- package/lib/decorators/common/registry-dependencies.js +2 -2
- package/lib/index.d.ts +3 -2
- package/lib/index.js +17 -2
- package/lib/lint.d.ts +1 -0
- package/lib/lint.js +3 -7
- package/lib/oas-types.d.ts +1 -1
- package/lib/redocly/domains.d.ts +14 -0
- package/lib/redocly/domains.js +41 -0
- package/lib/redocly/index.d.ts +1 -2
- package/lib/redocly/index.js +14 -24
- package/lib/redocly/registry-api.d.ts +2 -2
- package/lib/redocly/registry-api.js +9 -9
- package/lib/types/oas3_1.js +1 -0
- package/lib/types/redocly-yaml.d.ts +3 -1
- package/lib/types/redocly-yaml.js +34 -36
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +7 -1
- package/package.json +4 -2
- package/src/__tests__/lint.test.ts +22 -12
- package/src/bundle.ts +1 -1
- package/src/config/__tests__/load.test.ts +86 -61
- package/src/config/config.ts +1 -23
- package/src/config/load.ts +51 -24
- package/src/config/utils.ts +4 -0
- package/src/decorators/common/registry-dependencies.ts +1 -1
- package/src/index.ts +4 -1
- package/src/lint.ts +10 -9
- package/src/redocly/__tests__/domains.test.ts +52 -0
- package/src/redocly/__tests__/redocly-client.test.ts +5 -3
- package/src/redocly/domains.ts +48 -0
- package/src/redocly/index.ts +14 -24
- package/src/redocly/registry-api.ts +25 -31
- package/src/types/oas3_1.ts +1 -0
- package/src/types/redocly-yaml.ts +32 -33
- package/src/utils.ts +7 -1
- package/tsconfig.tsbuildinfo +1 -1
package/lib/types/oas3_1.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NodeType } from '.';
|
|
2
2
|
import type { JSONSchema } from 'json-schema-to-ts';
|
|
3
|
+
import { Config } from '../config';
|
|
3
4
|
declare const builtInCommonRules: readonly ["spec", "info-contact", "operation-operationId", "tag-description", "tags-alphabetical"];
|
|
4
5
|
export type BuiltInCommonRuleId = typeof builtInCommonRules[number];
|
|
5
6
|
declare const builtInCommonOASRules: readonly ["info-license-url", "info-license", "no-ambiguous-paths", "no-enum-type-mismatch", "no-http-verbs-in-paths", "no-identical-paths", "no-invalid-parameter-examples", "no-invalid-schema-examples", "no-path-trailing-slash", "operation-2xx-response", "operation-4xx-response", "operation-description", "operation-operationId-unique", "operation-operationId-url-safe", "operation-parameters-unique", "operation-singular-tag", "operation-summary", "operation-tag-defined", "parameter-description", "path-declaration-must-exist", "path-excludes-patterns", "path-http-verbs-order", "path-not-include-query", "path-params-defined", "path-parameters-defined", "path-segment-plural", "paths-kebab-case", "required-string-property-missing-min-length", "response-contains-header", "scalar-property-missing-example", "security-defined", "spec-strict-refs", "no-unresolved-refs", "no-required-schema-properties-undefined"];
|
|
@@ -16,9 +17,10 @@ declare const oas3NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDo
|
|
|
16
17
|
export type Oas3NodeType = typeof oas3NodeTypesList[number];
|
|
17
18
|
declare const oas3_1NodeTypesList: readonly ["Root", "Schema", "SchemaProperties", "Info", "License", "Components", "NamedPathItems", "SecurityScheme", "Operation"];
|
|
18
19
|
export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number];
|
|
19
|
-
export declare
|
|
20
|
+
export declare function createConfigTypes(extraSchemas: JSONSchema, config?: Config): {
|
|
20
21
|
ConfigRoot: NodeType;
|
|
21
22
|
ConfigApisProperties: NodeType;
|
|
23
|
+
AssertionDefinitionSubject: NodeType;
|
|
22
24
|
};
|
|
23
25
|
export declare const ConfigTypes: Record<string, NodeType>;
|
|
24
26
|
export {};
|
|
@@ -5,6 +5,7 @@ const config_1 = require("@redocly/config");
|
|
|
5
5
|
const _1 = require(".");
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const json_schema_adapter_1 = require("./json-schema-adapter");
|
|
8
|
+
const oas_types_1 = require("../oas-types");
|
|
8
9
|
const builtInCommonRules = [
|
|
9
10
|
'spec',
|
|
10
11
|
'info-contact',
|
|
@@ -194,7 +195,6 @@ const oas3_1NodeTypesList = [
|
|
|
194
195
|
'SecurityScheme',
|
|
195
196
|
'Operation',
|
|
196
197
|
];
|
|
197
|
-
const asyncNodeTypesList = ['Message'];
|
|
198
198
|
const ConfigStyleguide = {
|
|
199
199
|
properties: {
|
|
200
200
|
extends: {
|
|
@@ -296,37 +296,30 @@ const Schema = {
|
|
|
296
296
|
properties: {},
|
|
297
297
|
additionalProperties: {},
|
|
298
298
|
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
...new Set([
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
return { type: 'string' };
|
|
322
|
-
}
|
|
299
|
+
function createAssertionDefinitionSubject(nodeNames) {
|
|
300
|
+
return {
|
|
301
|
+
properties: {
|
|
302
|
+
type: {
|
|
303
|
+
enum: [...new Set(['any', ...nodeNames, 'SpecExtension'])],
|
|
304
|
+
},
|
|
305
|
+
property: (value) => {
|
|
306
|
+
if (Array.isArray(value)) {
|
|
307
|
+
return { type: 'array', items: { type: 'string' } };
|
|
308
|
+
}
|
|
309
|
+
else if (value === null) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
return { type: 'string' };
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
filterInParentKeys: { type: 'array', items: { type: 'string' } },
|
|
317
|
+
filterOutParentKeys: { type: 'array', items: { type: 'string' } },
|
|
318
|
+
matchParentKeys: { type: 'string' },
|
|
323
319
|
},
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
},
|
|
328
|
-
required: ['type'],
|
|
329
|
-
};
|
|
320
|
+
required: ['type'],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
330
323
|
const AssertionDefinitionAssertions = {
|
|
331
324
|
properties: {
|
|
332
325
|
enum: { type: 'array', items: { type: 'string' } },
|
|
@@ -897,11 +890,17 @@ const ConfigMockServer = {
|
|
|
897
890
|
errorIfForcedExampleNotFound: { type: 'boolean' },
|
|
898
891
|
},
|
|
899
892
|
};
|
|
900
|
-
|
|
893
|
+
function createConfigTypes(extraSchemas, config) {
|
|
894
|
+
const nodeNames = Object.values(oas_types_1.SpecVersion).flatMap((version) => {
|
|
895
|
+
const types = (config === null || config === void 0 ? void 0 : config.styleguide)
|
|
896
|
+
? config.styleguide.extendTypes((0, oas_types_1.getTypes)(version), version)
|
|
897
|
+
: (0, oas_types_1.getTypes)(version);
|
|
898
|
+
return Object.keys(types);
|
|
899
|
+
});
|
|
901
900
|
// Create types based on external schemas
|
|
902
901
|
const nodeTypes = (0, json_schema_adapter_1.getNodeTypesFromJSONSchema)('rootRedoclyConfigSchema', extraSchemas);
|
|
903
|
-
return Object.assign(Object.assign(Object.assign({}, CoreConfigTypes), { ConfigRoot: createConfigRoot(nodeTypes), ConfigApisProperties: createConfigApisProperties(nodeTypes) }), nodeTypes);
|
|
904
|
-
}
|
|
902
|
+
return Object.assign(Object.assign(Object.assign({}, CoreConfigTypes), { ConfigRoot: createConfigRoot(nodeTypes), ConfigApisProperties: createConfigApisProperties(nodeTypes), AssertionDefinitionSubject: createAssertionDefinitionSubject(nodeNames) }), nodeTypes);
|
|
903
|
+
}
|
|
905
904
|
exports.createConfigTypes = createConfigTypes;
|
|
906
905
|
const CoreConfigTypes = {
|
|
907
906
|
Assert,
|
|
@@ -964,6 +963,5 @@ const CoreConfigTypes = {
|
|
|
964
963
|
Heading,
|
|
965
964
|
Typography,
|
|
966
965
|
AssertionDefinitionAssertions,
|
|
967
|
-
AssertionDefinitionSubject,
|
|
968
966
|
};
|
|
969
|
-
exports.ConfigTypes =
|
|
967
|
+
exports.ConfigTypes = createConfigTypes(config_1.rootRedoclyConfigSchema);
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UserContext } from './walk';
|
|
2
2
|
import { HttpResolveConfig } from './config';
|
|
3
|
+
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
3
4
|
export { parseYaml, stringifyYaml } from './js-yaml';
|
|
4
5
|
export type StackFrame<T> = {
|
|
5
6
|
prev: StackFrame<T> | null;
|
|
@@ -51,3 +52,4 @@ export declare function keysOf<T>(obj: T): (keyof T)[];
|
|
|
51
52
|
export declare function pickDefined<T extends Record<string, unknown>>(obj?: T): Record<string, unknown> | undefined;
|
|
52
53
|
export declare function nextTick(): void;
|
|
53
54
|
export declare function pause(ms: number): Promise<void>;
|
|
55
|
+
export declare function getProxyAgent(): HttpsProxyAgent<string> | undefined;
|
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.pause = 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.yamlAndJsonSyncReader = 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.getProxyAgent = exports.pause = 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.yamlAndJsonSyncReader = 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");
|
|
@@ -18,6 +18,7 @@ const pluralize = require("pluralize");
|
|
|
18
18
|
const js_yaml_1 = require("./js-yaml");
|
|
19
19
|
const env_1 = require("./env");
|
|
20
20
|
const logger_1 = require("./logger");
|
|
21
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
21
22
|
var js_yaml_2 = require("./js-yaml");
|
|
22
23
|
Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
|
|
23
24
|
Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: function () { return js_yaml_2.stringifyYaml; } });
|
|
@@ -246,3 +247,8 @@ exports.pause = pause;
|
|
|
246
247
|
function getUpdatedFieldName(updatedField, updatedObject) {
|
|
247
248
|
return `${typeof updatedObject !== 'undefined' ? `${updatedObject}.` : ''}${updatedField}`;
|
|
248
249
|
}
|
|
250
|
+
function getProxyAgent() {
|
|
251
|
+
const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
|
252
|
+
return proxy ? new https_proxy_agent_1.HttpsProxyAgent(proxy) : undefined;
|
|
253
|
+
}
|
|
254
|
+
exports.getProxyAgent = getProxyAgent;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"path": "path-browserify",
|
|
19
19
|
"os": false,
|
|
20
20
|
"node-fetch": false,
|
|
21
|
-
"colorette": false
|
|
21
|
+
"colorette": false,
|
|
22
|
+
"https-proxy-agent": false
|
|
22
23
|
},
|
|
23
24
|
"homepage": "https://github.com/Redocly/redocly-cli",
|
|
24
25
|
"keywords": [
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"@redocly/ajv": "^8.11.0",
|
|
38
39
|
"@redocly/config": "^0.6.0",
|
|
39
40
|
"colorette": "^1.2.0",
|
|
41
|
+
"https-proxy-agent": "^7.0.4",
|
|
40
42
|
"js-levenshtein": "^1.1.6",
|
|
41
43
|
"js-yaml": "^4.1.0",
|
|
42
44
|
"lodash.isequal": "^4.5.0",
|
|
@@ -3,7 +3,7 @@ import { outdent } from 'outdent';
|
|
|
3
3
|
|
|
4
4
|
import { lintFromString, lintConfig, lintDocument, lint } from '../lint';
|
|
5
5
|
import { BaseResolver } from '../resolve';
|
|
6
|
-
import { loadConfig } from '../config/load';
|
|
6
|
+
import { createConfig, loadConfig } from '../config/load';
|
|
7
7
|
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
|
|
8
8
|
import { detectSpec } from '../oas-types';
|
|
9
9
|
import { rootRedoclyConfigSchema } from '@redocly/config';
|
|
@@ -293,7 +293,7 @@ describe('lint', () => {
|
|
|
293
293
|
- url: http://redocly-example.com
|
|
294
294
|
paths: {}
|
|
295
295
|
`,
|
|
296
|
-
config: await loadConfig(),
|
|
296
|
+
config: await loadConfig({ configPath: path.join(__dirname, 'fixtures/redocly.yaml') }),
|
|
297
297
|
});
|
|
298
298
|
|
|
299
299
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
@@ -374,7 +374,8 @@ describe('lint', () => {
|
|
|
374
374
|
`,
|
|
375
375
|
''
|
|
376
376
|
);
|
|
377
|
-
const
|
|
377
|
+
const config = await createConfig({});
|
|
378
|
+
const results = await lintConfig({ document, config });
|
|
378
379
|
|
|
379
380
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
380
381
|
[
|
|
@@ -435,7 +436,8 @@ describe('lint', () => {
|
|
|
435
436
|
`,
|
|
436
437
|
''
|
|
437
438
|
);
|
|
438
|
-
const
|
|
439
|
+
const config = await createConfig({});
|
|
440
|
+
const results = await lintConfig({ document, config });
|
|
439
441
|
|
|
440
442
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
441
443
|
[
|
|
@@ -475,7 +477,8 @@ describe('lint', () => {
|
|
|
475
477
|
`,
|
|
476
478
|
''
|
|
477
479
|
);
|
|
478
|
-
const
|
|
480
|
+
const config = await createConfig({});
|
|
481
|
+
const results = await lintConfig({ document, config });
|
|
479
482
|
|
|
480
483
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
481
484
|
[
|
|
@@ -510,7 +513,8 @@ describe('lint', () => {
|
|
|
510
513
|
`,
|
|
511
514
|
''
|
|
512
515
|
);
|
|
513
|
-
const
|
|
516
|
+
const config = await createConfig({});
|
|
517
|
+
const results = await lintConfig({ document, config });
|
|
514
518
|
|
|
515
519
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
516
520
|
[
|
|
@@ -534,7 +538,8 @@ describe('lint', () => {
|
|
|
534
538
|
|
|
535
539
|
it('lintConfig should detect wrong fields in the default configuration after merging with the portal config schema', async () => {
|
|
536
540
|
const document = testPortalConfig;
|
|
537
|
-
const
|
|
541
|
+
const config = await createConfig({});
|
|
542
|
+
const results = await lintConfig({ document, config });
|
|
538
543
|
|
|
539
544
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
540
545
|
[
|
|
@@ -1165,13 +1170,18 @@ describe('lint', () => {
|
|
|
1165
1170
|
|
|
1166
1171
|
it('lintConfig should alternate its behavior when supplied externalConfigTypes', async () => {
|
|
1167
1172
|
const document = testPortalConfig;
|
|
1173
|
+
const config = await createConfig({});
|
|
1168
1174
|
const results = await lintConfig({
|
|
1169
1175
|
document,
|
|
1170
|
-
externalConfigTypes: createConfigTypes(
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1176
|
+
externalConfigTypes: createConfigTypes(
|
|
1177
|
+
{
|
|
1178
|
+
type: 'object',
|
|
1179
|
+
properties: { theme: rootRedoclyConfigSchema.properties.theme },
|
|
1180
|
+
additionalProperties: false,
|
|
1181
|
+
},
|
|
1182
|
+
config
|
|
1183
|
+
),
|
|
1184
|
+
config,
|
|
1175
1185
|
});
|
|
1176
1186
|
|
|
1177
1187
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
package/src/bundle.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
|
|
|
14
14
|
import { initRules } from './config/rules';
|
|
15
15
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
16
16
|
import { isPlainObject, isTruthy } from './utils';
|
|
17
|
-
import { isRedoclyRegistryURL } from './redocly';
|
|
17
|
+
import { isRedoclyRegistryURL } from './redocly/domains';
|
|
18
18
|
import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components';
|
|
19
19
|
import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components';
|
|
20
20
|
import { ConfigTypes } from './types/redocly-yaml';
|
|
@@ -58,6 +58,88 @@ describe('loadConfig', () => {
|
|
|
58
58
|
expect(mockFn).toHaveBeenCalled();
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it('should resolve config and call processRawConfig', async () => {
|
|
62
|
+
let problems: NormalizedProblem[];
|
|
63
|
+
let doc: any;
|
|
64
|
+
|
|
65
|
+
await loadConfig({
|
|
66
|
+
configPath: path.join(__dirname, './fixtures/resolve-refs-in-config/config-with-refs.yaml'),
|
|
67
|
+
processRawConfig: async ({ document, parsed, resolvedRefMap, config }) => {
|
|
68
|
+
doc = parsed;
|
|
69
|
+
problems = await lintConfig({
|
|
70
|
+
document,
|
|
71
|
+
severity: 'warn',
|
|
72
|
+
resolvedRefMap,
|
|
73
|
+
config,
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(replaceSourceWithRef(problems!, __dirname)).toMatchInlineSnapshot(`
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
"from": {
|
|
82
|
+
"pointer": "#/seo",
|
|
83
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
84
|
+
},
|
|
85
|
+
"location": [
|
|
86
|
+
{
|
|
87
|
+
"pointer": "#/title",
|
|
88
|
+
"reportOnKey": false,
|
|
89
|
+
"source": "fixtures/resolve-refs-in-config/seo.yaml",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
"message": "Expected type \`string\` but got \`integer\`.",
|
|
93
|
+
"ruleId": "configuration spec",
|
|
94
|
+
"severity": "warn",
|
|
95
|
+
"suggest": [],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"from": {
|
|
99
|
+
"pointer": "#/rules",
|
|
100
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
101
|
+
},
|
|
102
|
+
"location": [
|
|
103
|
+
{
|
|
104
|
+
"pointer": "#/non-existing-rule",
|
|
105
|
+
"reportOnKey": true,
|
|
106
|
+
"source": "fixtures/resolve-refs-in-config/rules.yaml",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
"message": "Property \`non-existing-rule\` is not expected here.",
|
|
110
|
+
"ruleId": "configuration spec",
|
|
111
|
+
"severity": "warn",
|
|
112
|
+
"suggest": [],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"location": [
|
|
116
|
+
{
|
|
117
|
+
"pointer": "#/theme",
|
|
118
|
+
"reportOnKey": false,
|
|
119
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
|
|
123
|
+
"ruleId": "configuration no-unresolved-refs",
|
|
124
|
+
"severity": "warn",
|
|
125
|
+
"suggest": [],
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
`);
|
|
129
|
+
expect(doc).toMatchInlineSnapshot(`
|
|
130
|
+
{
|
|
131
|
+
"rules": {
|
|
132
|
+
"info-license": "error",
|
|
133
|
+
"non-existing-rule": "warn",
|
|
134
|
+
},
|
|
135
|
+
"seo": {
|
|
136
|
+
"title": 1,
|
|
137
|
+
},
|
|
138
|
+
"theme": undefined,
|
|
139
|
+
}
|
|
140
|
+
`);
|
|
141
|
+
});
|
|
142
|
+
|
|
61
143
|
it('should call externalRefResolver if such passed', async () => {
|
|
62
144
|
const externalRefResolver = new BaseResolver();
|
|
63
145
|
const resolverSpy = jest.spyOn(externalRefResolver, 'resolveDocument');
|
|
@@ -104,22 +186,16 @@ describe('findConfig', () => {
|
|
|
104
186
|
describe('getConfig', () => {
|
|
105
187
|
jest.spyOn(fs, 'hasOwnProperty').mockImplementation(() => false);
|
|
106
188
|
it('should return empty object if there is no configPath and config file is not found', () => {
|
|
107
|
-
expect(getConfig()).toEqual(Promise.resolve({}));
|
|
189
|
+
expect(getConfig()).toEqual(Promise.resolve({ rawConfig: {} }));
|
|
108
190
|
});
|
|
109
191
|
|
|
110
192
|
it('should resolve refs in config', async () => {
|
|
111
193
|
let problems: NormalizedProblem[];
|
|
112
|
-
|
|
194
|
+
|
|
195
|
+
const { rawConfig } = await getConfig({
|
|
113
196
|
configPath: path.join(__dirname, './fixtures/resolve-refs-in-config/config-with-refs.yaml'),
|
|
114
|
-
processRawConfig: async (config, resolvedRefMap) => {
|
|
115
|
-
problems = await lintConfig({
|
|
116
|
-
document: config,
|
|
117
|
-
severity: 'warn',
|
|
118
|
-
resolvedRefMap,
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
197
|
});
|
|
122
|
-
expect(
|
|
198
|
+
expect(rawConfig).toEqual({
|
|
123
199
|
seo: {
|
|
124
200
|
title: 1,
|
|
125
201
|
},
|
|
@@ -130,57 +206,6 @@ describe('getConfig', () => {
|
|
|
130
206
|
},
|
|
131
207
|
},
|
|
132
208
|
});
|
|
133
|
-
expect(replaceSourceWithRef(problems!, __dirname)).toMatchInlineSnapshot(`
|
|
134
|
-
[
|
|
135
|
-
{
|
|
136
|
-
"from": {
|
|
137
|
-
"pointer": "#/seo",
|
|
138
|
-
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
139
|
-
},
|
|
140
|
-
"location": [
|
|
141
|
-
{
|
|
142
|
-
"pointer": "#/title",
|
|
143
|
-
"reportOnKey": false,
|
|
144
|
-
"source": "fixtures/resolve-refs-in-config/seo.yaml",
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
"message": "Expected type \`string\` but got \`integer\`.",
|
|
148
|
-
"ruleId": "configuration spec",
|
|
149
|
-
"severity": "warn",
|
|
150
|
-
"suggest": [],
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"from": {
|
|
154
|
-
"pointer": "#/rules",
|
|
155
|
-
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
156
|
-
},
|
|
157
|
-
"location": [
|
|
158
|
-
{
|
|
159
|
-
"pointer": "#/non-existing-rule",
|
|
160
|
-
"reportOnKey": true,
|
|
161
|
-
"source": "fixtures/resolve-refs-in-config/rules.yaml",
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
"message": "Property \`non-existing-rule\` is not expected here.",
|
|
165
|
-
"ruleId": "configuration spec",
|
|
166
|
-
"severity": "warn",
|
|
167
|
-
"suggest": [],
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
"location": [
|
|
171
|
-
{
|
|
172
|
-
"pointer": "#/theme",
|
|
173
|
-
"reportOnKey": false,
|
|
174
|
-
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
|
|
178
|
-
"ruleId": "configuration no-unresolved-refs",
|
|
179
|
-
"severity": "warn",
|
|
180
|
-
"suggest": [],
|
|
181
|
-
},
|
|
182
|
-
]
|
|
183
|
-
`);
|
|
184
209
|
});
|
|
185
210
|
});
|
|
186
211
|
|
package/src/config/config.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Oas3RuleSet,
|
|
11
11
|
Async2RuleSet,
|
|
12
12
|
} from '../oas-types';
|
|
13
|
-
import { isBrowser
|
|
13
|
+
import { isBrowser } from '../env';
|
|
14
14
|
|
|
15
15
|
import type { NodeType } from '../types';
|
|
16
16
|
import type {
|
|
@@ -35,25 +35,6 @@ const IGNORE_BANNER =
|
|
|
35
35
|
`# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
|
|
36
36
|
`# See https://redoc.ly/docs/cli/ for more information.\n`;
|
|
37
37
|
|
|
38
|
-
export const DEFAULT_REGION = 'us';
|
|
39
|
-
|
|
40
|
-
function getDomains() {
|
|
41
|
-
const domains: { [region in Region]: string } = {
|
|
42
|
-
us: 'redocly.com',
|
|
43
|
-
eu: 'eu.redocly.com',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// FIXME: temporary fix for our lab environments
|
|
47
|
-
const domain = env.REDOCLY_DOMAIN;
|
|
48
|
-
if (domain?.endsWith('.redocly.host')) {
|
|
49
|
-
domains[domain.split('.')[0] as Region] = domain;
|
|
50
|
-
}
|
|
51
|
-
if (domain === 'redoc.online') {
|
|
52
|
-
domains[domain as Region] = domain;
|
|
53
|
-
}
|
|
54
|
-
return domains;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
38
|
function getIgnoreFilePath(configFile?: string): string | undefined {
|
|
58
39
|
if (configFile) {
|
|
59
40
|
return doesYamlFileExist(configFile)
|
|
@@ -64,9 +45,6 @@ function getIgnoreFilePath(configFile?: string): string | undefined {
|
|
|
64
45
|
}
|
|
65
46
|
}
|
|
66
47
|
|
|
67
|
-
export const DOMAINS = getDomains();
|
|
68
|
-
export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
|
|
69
|
-
|
|
70
48
|
export class StyleguideConfig {
|
|
71
49
|
plugins: Plugin[];
|
|
72
50
|
ignore: Record<string, Record<string, Set<string>>> = {};
|
package/src/config/load.ts
CHANGED
|
@@ -3,8 +3,8 @@ import * as path from 'path';
|
|
|
3
3
|
import { RedoclyClient } from '../redocly';
|
|
4
4
|
import { isEmptyObject } from '../utils';
|
|
5
5
|
import { parseYaml } from '../js-yaml';
|
|
6
|
-
import { Config
|
|
7
|
-
import { ConfigValidationError, transformConfig } from './utils';
|
|
6
|
+
import { Config } from './config';
|
|
7
|
+
import { ConfigValidationError, transformConfig, deepCloneMapWithJSON } from './utils';
|
|
8
8
|
import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers';
|
|
9
9
|
import { bundleConfig } from '../bundle';
|
|
10
10
|
import { BaseResolver } from '../resolve';
|
|
@@ -14,6 +14,7 @@ import type { Document } from '../resolve';
|
|
|
14
14
|
import type { RegionalToken, RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
|
15
15
|
import type { RawConfig, RawUniversalConfig, Region } from './types';
|
|
16
16
|
import type { ResolvedRefMap } from '../resolve';
|
|
17
|
+
import { DOMAINS } from '../redocly/domains';
|
|
17
18
|
|
|
18
19
|
async function addConfigMetadata({
|
|
19
20
|
rawConfig,
|
|
@@ -79,10 +80,12 @@ async function addConfigMetadata({
|
|
|
79
80
|
});
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
export type RawConfigProcessor = (
|
|
83
|
-
|
|
84
|
-
resolvedRefMap: ResolvedRefMap
|
|
85
|
-
|
|
83
|
+
export type RawConfigProcessor = (params: {
|
|
84
|
+
document: Document;
|
|
85
|
+
resolvedRefMap: ResolvedRefMap;
|
|
86
|
+
config: Config;
|
|
87
|
+
parsed: Document['parsed'];
|
|
88
|
+
}) => void | Promise<void>;
|
|
86
89
|
|
|
87
90
|
export async function loadConfig(
|
|
88
91
|
options: {
|
|
@@ -102,12 +105,16 @@ export async function loadConfig(
|
|
|
102
105
|
region,
|
|
103
106
|
externalRefResolver,
|
|
104
107
|
} = options;
|
|
105
|
-
|
|
108
|
+
|
|
109
|
+
const { rawConfig, document, parsed, resolvedRefMap } = await getConfig({
|
|
110
|
+
configPath,
|
|
111
|
+
externalRefResolver,
|
|
112
|
+
});
|
|
106
113
|
|
|
107
114
|
const redoclyClient = isBrowser ? undefined : new RedoclyClient();
|
|
108
115
|
const tokens = redoclyClient && redoclyClient.hasTokens() ? redoclyClient.getAllTokens() : [];
|
|
109
116
|
|
|
110
|
-
|
|
117
|
+
const config = await addConfigMetadata({
|
|
111
118
|
rawConfig,
|
|
112
119
|
customExtends,
|
|
113
120
|
configPath,
|
|
@@ -116,6 +123,24 @@ export async function loadConfig(
|
|
|
116
123
|
region,
|
|
117
124
|
externalRefResolver,
|
|
118
125
|
});
|
|
126
|
+
|
|
127
|
+
if (document && parsed && resolvedRefMap && typeof processRawConfig === 'function') {
|
|
128
|
+
try {
|
|
129
|
+
await processRawConfig({
|
|
130
|
+
document,
|
|
131
|
+
resolvedRefMap,
|
|
132
|
+
config,
|
|
133
|
+
parsed,
|
|
134
|
+
});
|
|
135
|
+
} catch (e) {
|
|
136
|
+
if (e instanceof ConfigValidationError) {
|
|
137
|
+
throw e;
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return config;
|
|
119
144
|
}
|
|
120
145
|
|
|
121
146
|
export const CONFIG_FILE_NAMES = ['redocly.yaml', 'redocly.yml', '.redocly.yaml', '.redocly.yml'];
|
|
@@ -138,31 +163,33 @@ export function findConfig(dir?: string): string | undefined {
|
|
|
138
163
|
export async function getConfig(
|
|
139
164
|
options: {
|
|
140
165
|
configPath?: string;
|
|
141
|
-
processRawConfig?: RawConfigProcessor;
|
|
142
166
|
externalRefResolver?: BaseResolver;
|
|
143
167
|
} = {}
|
|
144
|
-
): Promise<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
): Promise<{
|
|
169
|
+
rawConfig: RawConfig;
|
|
170
|
+
document?: Document;
|
|
171
|
+
parsed?: Document['parsed'];
|
|
172
|
+
resolvedRefMap?: ResolvedRefMap;
|
|
173
|
+
}> {
|
|
174
|
+
const { configPath = findConfig(), externalRefResolver = new BaseResolver() } = options;
|
|
175
|
+
if (!configPath) return { rawConfig: {} };
|
|
151
176
|
|
|
152
177
|
try {
|
|
153
178
|
const { document, resolvedRefMap } = await resolveConfigFileAndRefs({
|
|
154
179
|
configPath,
|
|
155
180
|
externalRefResolver,
|
|
156
181
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return
|
|
182
|
+
|
|
183
|
+
const bundledRefMap = deepCloneMapWithJSON(resolvedRefMap);
|
|
184
|
+
const parsed = await bundleConfig(JSON.parse(JSON.stringify(document)), bundledRefMap);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
rawConfig: transformConfig(parsed),
|
|
188
|
+
document,
|
|
189
|
+
parsed,
|
|
190
|
+
resolvedRefMap,
|
|
191
|
+
};
|
|
162
192
|
} catch (e) {
|
|
163
|
-
if (e instanceof ConfigValidationError) {
|
|
164
|
-
throw e;
|
|
165
|
-
}
|
|
166
193
|
throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
|
|
167
194
|
}
|
|
168
195
|
}
|
package/src/config/utils.ts
CHANGED
|
@@ -364,3 +364,7 @@ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
export class ConfigValidationError extends Error {}
|
|
367
|
+
|
|
368
|
+
export function deepCloneMapWithJSON<K, V>(originalMap: Map<K, V>): Map<K, V> {
|
|
369
|
+
return new Map(JSON.parse(JSON.stringify([...originalMap])));
|
|
370
|
+
}
|