@redocly/openapi-core 1.0.0-beta.93 → 1.0.0-beta.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/__tests__/bundle.test.ts +6 -6
- package/__tests__/fixtures/extension.js +1 -1
- package/__tests__/login.test.ts +2 -2
- package/__tests__/normalizeVisitors.test.ts +1 -1
- package/__tests__/ref-utils.test.ts +1 -1
- package/__tests__/utils.ts +30 -18
- package/__tests__/walk.test.ts +6 -6
- package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
- package/lib/benchmark/utils.d.ts +2 -1
- package/lib/benchmark/utils.js +10 -7
- package/lib/bundle.d.ts +2 -2
- package/lib/config/all.d.ts +2 -2
- package/lib/config/builtIn.d.ts +1 -1
- package/lib/config/config-resolvers.d.ts +16 -0
- package/lib/config/config-resolvers.js +213 -0
- package/lib/config/config.d.ts +14 -129
- package/lib/config/config.js +17 -234
- package/lib/config/index.d.ts +7 -0
- package/lib/config/index.js +19 -0
- package/lib/config/load.d.ts +2 -1
- package/lib/config/load.js +20 -13
- package/lib/config/minimal.d.ts +2 -2
- package/lib/config/recommended.d.ts +2 -2
- package/lib/config/types.d.ts +113 -0
- package/lib/config/types.js +2 -0
- package/lib/config/utils.d.ts +13 -0
- package/lib/config/utils.js +160 -0
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +28 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.js +5 -6
- package/lib/lint.d.ts +1 -1
- package/lib/lint.js +5 -7
- package/lib/redocly/index.d.ts +1 -1
- package/lib/redocly/index.js +1 -1
- package/lib/redocly/redocly-client-types.d.ts +1 -1
- package/lib/redocly/registry-api.d.ts +1 -1
- package/lib/resolve.d.ts +1 -1
- package/lib/resolve.js +1 -2
- package/lib/rules/common/assertions/index.js +1 -1
- package/lib/rules/common/assertions/utils.d.ts +1 -1
- package/lib/rules/common/assertions/utils.js +6 -2
- package/lib/types/index.js +2 -2
- package/lib/types/oas3_1.js +31 -5
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +18 -1
- package/package.json +7 -4
- package/src/__tests__/lint.test.ts +1 -1
- package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
- package/src/benchmark/benchmark.js +1 -1
- package/src/benchmark/utils.ts +13 -8
- package/src/bundle.ts +2 -1
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +140 -0
- package/src/config/__tests__/config-resolvers.test.ts +398 -0
- package/src/config/__tests__/config.test.ts +244 -0
- package/src/config/__tests__/fixtures/plugin.js +1 -1
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +7 -0
- package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +12 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
- package/src/config/__tests__/load.test.ts +8 -1
- package/src/config/all.ts +3 -2
- package/src/config/builtIn.ts +2 -1
- package/src/config/config-resolvers.ts +304 -0
- package/src/config/config.ts +40 -457
- package/src/config/index.ts +7 -0
- package/src/config/load.ts +37 -31
- package/src/config/minimal.ts +2 -2
- package/src/config/recommended.ts +2 -2
- package/src/config/types.ts +168 -0
- package/src/config/utils.ts +208 -0
- package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
- package/src/format/format.ts +36 -6
- package/src/index.ts +6 -2
- package/src/lint.ts +4 -5
- package/src/redocly/__tests__/redocly-client.test.ts +7 -0
- package/src/redocly/index.ts +4 -2
- package/src/redocly/redocly-client-types.ts +1 -1
- package/src/redocly/registry-api.ts +3 -1
- package/src/resolve.ts +2 -4
- package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
- package/src/rules/common/__tests__/info-description.test.ts +3 -3
- package/src/rules/common/__tests__/info-license.test.ts +2 -2
- package/src/rules/common/__tests__/license-url.test.ts +2 -2
- package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
- package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
- package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
- package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
- package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
- package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
- package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
- package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
- package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
- package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
- package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
- package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
- package/src/rules/common/__tests__/spec.test.ts +1 -1
- package/src/rules/common/__tests__/tag-description.test.ts +2 -2
- package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
- package/src/rules/common/assertions/index.ts +1 -1
- package/src/rules/common/assertions/utils.ts +5 -2
- package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
- package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
- package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
- package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
- package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
- package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
- package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
- package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
- package/src/rules/oas3/__tests__/spec/servers.test.ts +1 -1
- package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
- package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
- package/src/types/index.ts +2 -2
- package/src/types/oas3_1.ts +32 -7
- package/src/utils.ts +18 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { ProblemSeverity } from '../walk';
|
|
2
|
+
import type {
|
|
3
|
+
Oas3PreprocessorsSet,
|
|
4
|
+
OasMajorVersion,
|
|
5
|
+
Oas3DecoratorsSet,
|
|
6
|
+
Oas2RuleSet,
|
|
7
|
+
Oas2PreprocessorsSet,
|
|
8
|
+
Oas2DecoratorsSet,
|
|
9
|
+
Oas3RuleSet,
|
|
10
|
+
OasVersion,
|
|
11
|
+
} from '../oas-types';
|
|
12
|
+
import type { NodeType } from '../types';
|
|
13
|
+
|
|
14
|
+
export type RuleConfig =
|
|
15
|
+
| ProblemSeverity
|
|
16
|
+
| 'off'
|
|
17
|
+
| ({
|
|
18
|
+
severity?: ProblemSeverity;
|
|
19
|
+
} & Record<string, any>);
|
|
20
|
+
|
|
21
|
+
export type PreprocessorConfig =
|
|
22
|
+
| ProblemSeverity
|
|
23
|
+
| 'off'
|
|
24
|
+
| 'on'
|
|
25
|
+
| ({
|
|
26
|
+
severity?: ProblemSeverity;
|
|
27
|
+
} & Record<string, any>);
|
|
28
|
+
|
|
29
|
+
export type DecoratorConfig = PreprocessorConfig;
|
|
30
|
+
|
|
31
|
+
export type LintRawConfig = {
|
|
32
|
+
plugins?: (string | Plugin)[];
|
|
33
|
+
extends?: string[];
|
|
34
|
+
doNotResolveExamples?: boolean;
|
|
35
|
+
recommendedFallback?: boolean;
|
|
36
|
+
|
|
37
|
+
rules?: Record<string, RuleConfig>;
|
|
38
|
+
oas2Rules?: Record<string, RuleConfig>;
|
|
39
|
+
oas3_0Rules?: Record<string, RuleConfig>;
|
|
40
|
+
oas3_1Rules?: Record<string, RuleConfig>;
|
|
41
|
+
|
|
42
|
+
preprocessors?: Record<string, PreprocessorConfig>;
|
|
43
|
+
oas2Preprocessors?: Record<string, PreprocessorConfig>;
|
|
44
|
+
oas3_0Preprocessors?: Record<string, PreprocessorConfig>;
|
|
45
|
+
oas3_1Preprocessors?: Record<string, PreprocessorConfig>;
|
|
46
|
+
|
|
47
|
+
decorators?: Record<string, DecoratorConfig>;
|
|
48
|
+
oas2Decorators?: Record<string, DecoratorConfig>;
|
|
49
|
+
oas3_0Decorators?: Record<string, DecoratorConfig>;
|
|
50
|
+
oas3_1Decorators?: Record<string, DecoratorConfig>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type ResolvedLintConfig = PluginLintConfig & {
|
|
54
|
+
plugins?: Plugin[];
|
|
55
|
+
recommendedFallback?: boolean;
|
|
56
|
+
extends?: void | never;
|
|
57
|
+
extendPaths?: string[];
|
|
58
|
+
pluginPaths?: string[];
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type PreprocessorsConfig = {
|
|
62
|
+
oas3?: Oas3PreprocessorsSet;
|
|
63
|
+
oas2?: Oas2PreprocessorsSet;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type DecoratorsConfig = {
|
|
67
|
+
oas3?: Oas3DecoratorsSet;
|
|
68
|
+
oas2?: Oas2DecoratorsSet;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type TypesExtensionFn = (
|
|
72
|
+
types: Record<string, NodeType>,
|
|
73
|
+
oasVersion: OasVersion,
|
|
74
|
+
) => Record<string, NodeType>;
|
|
75
|
+
|
|
76
|
+
export type TypeExtensionsConfig = Partial<Record<OasMajorVersion, TypesExtensionFn>>;
|
|
77
|
+
|
|
78
|
+
export type CustomRulesConfig = {
|
|
79
|
+
oas3?: Oas3RuleSet;
|
|
80
|
+
oas2?: Oas2RuleSet;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type Plugin = {
|
|
84
|
+
id: string;
|
|
85
|
+
configs?: Record<string, PluginLintConfig>;
|
|
86
|
+
rules?: CustomRulesConfig;
|
|
87
|
+
preprocessors?: PreprocessorsConfig;
|
|
88
|
+
decorators?: DecoratorsConfig;
|
|
89
|
+
typeExtension?: TypeExtensionsConfig;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type PluginLintConfig = Omit<LintRawConfig, 'plugins' | 'extends'>;
|
|
93
|
+
|
|
94
|
+
export type ResolveHeader =
|
|
95
|
+
| {
|
|
96
|
+
name: string;
|
|
97
|
+
envVariable?: undefined;
|
|
98
|
+
value: string;
|
|
99
|
+
matches: string;
|
|
100
|
+
}
|
|
101
|
+
| {
|
|
102
|
+
name: string;
|
|
103
|
+
value?: undefined;
|
|
104
|
+
envVariable: string;
|
|
105
|
+
matches: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type RawResolveConfig = {
|
|
109
|
+
http?: Partial<HttpResolveConfig>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export type HttpResolveConfig = {
|
|
113
|
+
headers: ResolveHeader[];
|
|
114
|
+
customFetch?: Function;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type ResolveConfig = {
|
|
118
|
+
http: HttpResolveConfig;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type Region = 'us' | 'eu';
|
|
122
|
+
|
|
123
|
+
export type AccessTokens = { [region in Region]?: string };
|
|
124
|
+
|
|
125
|
+
export type DeprecatedRawConfig = {
|
|
126
|
+
apiDefinitions?: Record<string, string>;
|
|
127
|
+
lint?: LintRawConfig;
|
|
128
|
+
resolve?: RawResolveConfig;
|
|
129
|
+
region?: Region;
|
|
130
|
+
referenceDocs?: Record<string, any>;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type Api = {
|
|
134
|
+
root: string;
|
|
135
|
+
lint?: Omit<LintRawConfig, 'plugins'>;
|
|
136
|
+
'features.openapi'?: Record<string, any>;
|
|
137
|
+
'features.mockServer'?: Record<string, any>;
|
|
138
|
+
};
|
|
139
|
+
export type ResolvedApi = Omit<Api, 'lint'> & { lint: Omit<ResolvedLintConfig, 'plugins'>};
|
|
140
|
+
|
|
141
|
+
export type RawConfig = {
|
|
142
|
+
apis?: Record<string, Api>;
|
|
143
|
+
lint?: LintRawConfig;
|
|
144
|
+
resolve?: RawResolveConfig;
|
|
145
|
+
region?: Region;
|
|
146
|
+
'features.openapi'?: Record<string, any>;
|
|
147
|
+
'features.mockServer'?: Record<string, any>;
|
|
148
|
+
organization?: string;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export type ResolvedConfig = Omit<RawConfig, 'lint' | 'apis'> & {
|
|
152
|
+
lint: ResolvedLintConfig;
|
|
153
|
+
apis: Record<string,ResolvedApi>
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export type RulesFields =
|
|
157
|
+
| 'rules'
|
|
158
|
+
| 'oas2Rules'
|
|
159
|
+
| 'oas3_0Rules'
|
|
160
|
+
| 'oas3_1Rules'
|
|
161
|
+
| 'preprocessors'
|
|
162
|
+
| 'oas2Preprocessors'
|
|
163
|
+
| 'oas3_0Preprocessors'
|
|
164
|
+
| 'oas3_1Preprocessors'
|
|
165
|
+
| 'decorators'
|
|
166
|
+
| 'oas2Decorators'
|
|
167
|
+
| 'oas3_0Decorators'
|
|
168
|
+
| 'oas3_1Decorators';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { green, yellow } from 'colorette';
|
|
2
|
+
import { assignExisting } from '../utils';
|
|
3
|
+
import { Config } from './config';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
Api,
|
|
7
|
+
DeprecatedRawConfig,
|
|
8
|
+
Plugin,
|
|
9
|
+
RawConfig,
|
|
10
|
+
RawResolveConfig,
|
|
11
|
+
ResolveConfig,
|
|
12
|
+
ResolvedLintConfig,
|
|
13
|
+
RulesFields,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
export function parsePresetName(presetName: string): { pluginId: string; configName: string } {
|
|
17
|
+
if (presetName.indexOf('/') > -1) {
|
|
18
|
+
const [pluginId, configName] = presetName.split('/');
|
|
19
|
+
return { pluginId, configName };
|
|
20
|
+
} else {
|
|
21
|
+
return { pluginId: '', configName: presetName };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function transformApiDefinitionsToApis(
|
|
26
|
+
apiDefinitions: Record<string, string> = {},
|
|
27
|
+
): Record<string, Api> {
|
|
28
|
+
let apis: Record<string, Api> = {};
|
|
29
|
+
for (const [apiName, apiPath] of Object.entries(apiDefinitions)) {
|
|
30
|
+
apis[apiName] = { root: apiPath };
|
|
31
|
+
}
|
|
32
|
+
return apis;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function prefixRules<T extends Record<string, any>>(rules: T, prefix: string) {
|
|
36
|
+
if (!prefix) return rules;
|
|
37
|
+
|
|
38
|
+
const res: any = {};
|
|
39
|
+
for (const name of Object.keys(rules)) {
|
|
40
|
+
res[`${prefix}/${name}`] = rules[name];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function mergeExtends(rulesConfList: ResolvedLintConfig[]) {
|
|
47
|
+
const result: Omit<ResolvedLintConfig, RulesFields> &
|
|
48
|
+
Required<Pick<ResolvedLintConfig, RulesFields>> = {
|
|
49
|
+
rules: {},
|
|
50
|
+
oas2Rules: {},
|
|
51
|
+
oas3_0Rules: {},
|
|
52
|
+
oas3_1Rules: {},
|
|
53
|
+
|
|
54
|
+
preprocessors: {},
|
|
55
|
+
oas2Preprocessors: {},
|
|
56
|
+
oas3_0Preprocessors: {},
|
|
57
|
+
oas3_1Preprocessors: {},
|
|
58
|
+
|
|
59
|
+
decorators: {},
|
|
60
|
+
oas2Decorators: {},
|
|
61
|
+
oas3_0Decorators: {},
|
|
62
|
+
oas3_1Decorators: {},
|
|
63
|
+
|
|
64
|
+
plugins: [],
|
|
65
|
+
pluginPaths: [],
|
|
66
|
+
extendPaths: [],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
for (let rulesConf of rulesConfList) {
|
|
70
|
+
if (rulesConf.extends) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`\`extends\` is not supported in shared configs yet: ${JSON.stringify(
|
|
73
|
+
rulesConf,
|
|
74
|
+
null,
|
|
75
|
+
2,
|
|
76
|
+
)}.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Object.assign(result.rules, rulesConf.rules);
|
|
81
|
+
Object.assign(result.oas2Rules, rulesConf.oas2Rules);
|
|
82
|
+
assignExisting(result.oas2Rules, rulesConf.rules || {});
|
|
83
|
+
Object.assign(result.oas3_0Rules, rulesConf.oas3_0Rules);
|
|
84
|
+
assignExisting(result.oas3_0Rules, rulesConf.rules || {});
|
|
85
|
+
Object.assign(result.oas3_1Rules, rulesConf.oas3_1Rules);
|
|
86
|
+
assignExisting(result.oas3_1Rules, rulesConf.rules || {});
|
|
87
|
+
|
|
88
|
+
Object.assign(result.preprocessors, rulesConf.preprocessors);
|
|
89
|
+
Object.assign(result.oas2Preprocessors, rulesConf.oas2Preprocessors);
|
|
90
|
+
assignExisting(result.oas2Preprocessors, rulesConf.preprocessors || {});
|
|
91
|
+
Object.assign(result.oas3_0Preprocessors, rulesConf.oas3_0Preprocessors);
|
|
92
|
+
assignExisting(result.oas3_0Preprocessors, rulesConf.preprocessors || {});
|
|
93
|
+
Object.assign(result.oas3_1Preprocessors, rulesConf.oas3_1Preprocessors);
|
|
94
|
+
assignExisting(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
|
|
95
|
+
|
|
96
|
+
Object.assign(result.decorators, rulesConf.decorators);
|
|
97
|
+
Object.assign(result.oas2Decorators, rulesConf.oas2Decorators);
|
|
98
|
+
assignExisting(result.oas2Decorators, rulesConf.decorators || {});
|
|
99
|
+
Object.assign(result.oas3_0Decorators, rulesConf.oas3_0Decorators);
|
|
100
|
+
assignExisting(result.oas3_0Decorators, rulesConf.decorators || {});
|
|
101
|
+
Object.assign(result.oas3_1Decorators, rulesConf.oas3_1Decorators);
|
|
102
|
+
assignExisting(result.oas3_1Decorators, rulesConf.decorators || {});
|
|
103
|
+
|
|
104
|
+
result.plugins!.push(...(rulesConf.plugins || []));
|
|
105
|
+
result.pluginPaths!.push(...(rulesConf.pluginPaths || []));
|
|
106
|
+
result.extendPaths!.push(...new Set(rulesConf.extendPaths));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function getMergedConfig(config: Config, entrypointAlias?: string): Config {
|
|
113
|
+
const extendPaths = [
|
|
114
|
+
...Object.values(config.apis).map((api) => api?.lint?.extendPaths),
|
|
115
|
+
config.rawConfig?.lint?.extendPaths,
|
|
116
|
+
]
|
|
117
|
+
.flat()
|
|
118
|
+
.filter(Boolean) as string[];
|
|
119
|
+
|
|
120
|
+
const pluginPaths = [
|
|
121
|
+
...Object.values(config.apis).map((api) => api?.lint?.pluginPaths),
|
|
122
|
+
config.rawConfig?.lint?.pluginPaths,
|
|
123
|
+
]
|
|
124
|
+
.flat()
|
|
125
|
+
.filter(Boolean) as string[];
|
|
126
|
+
|
|
127
|
+
return entrypointAlias
|
|
128
|
+
? new Config(
|
|
129
|
+
{
|
|
130
|
+
...config.rawConfig,
|
|
131
|
+
lint: {
|
|
132
|
+
...(config.apis[entrypointAlias]
|
|
133
|
+
? config.apis[entrypointAlias].lint
|
|
134
|
+
: config.rawConfig.lint),
|
|
135
|
+
extendPaths,
|
|
136
|
+
pluginPaths,
|
|
137
|
+
},
|
|
138
|
+
'features.openapi': {
|
|
139
|
+
...config['features.openapi'],
|
|
140
|
+
...config.apis[entrypointAlias]?.['features.openapi'],
|
|
141
|
+
},
|
|
142
|
+
'features.mockServer': {
|
|
143
|
+
...config['features.mockServer'],
|
|
144
|
+
...config.apis[entrypointAlias]?.['features.mockServer'],
|
|
145
|
+
},
|
|
146
|
+
// TODO: merge everything else here
|
|
147
|
+
},
|
|
148
|
+
config.configFile,
|
|
149
|
+
)
|
|
150
|
+
: config;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function transformConfig(rawConfig: DeprecatedRawConfig | RawConfig): RawConfig {
|
|
154
|
+
if ((rawConfig as RawConfig).apis && (rawConfig as DeprecatedRawConfig).apiDefinitions) {
|
|
155
|
+
throw new Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");
|
|
156
|
+
}
|
|
157
|
+
if (
|
|
158
|
+
(rawConfig as RawConfig)['features.openapi'] &&
|
|
159
|
+
(rawConfig as DeprecatedRawConfig).referenceDocs
|
|
160
|
+
) {
|
|
161
|
+
throw new Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");
|
|
162
|
+
}
|
|
163
|
+
const { apiDefinitions, referenceDocs, ...rest } = rawConfig as DeprecatedRawConfig & RawConfig;
|
|
164
|
+
if (apiDefinitions) {
|
|
165
|
+
process.stderr.write(
|
|
166
|
+
`The ${yellow('apiDefinitions')} field is deprecated. Use ${green(
|
|
167
|
+
'apis',
|
|
168
|
+
)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (referenceDocs) {
|
|
172
|
+
process.stderr.write(
|
|
173
|
+
`The ${yellow('referenceDocs')} field is deprecated. Use ${green(
|
|
174
|
+
'features.openapi',
|
|
175
|
+
)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
'features.openapi': referenceDocs,
|
|
180
|
+
apis: transformApiDefinitionsToApis(apiDefinitions),
|
|
181
|
+
...rest,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function getResolveConfig(resolve?: RawResolveConfig): ResolveConfig {
|
|
186
|
+
return {
|
|
187
|
+
http: {
|
|
188
|
+
headers: resolve?.http?.headers ?? [],
|
|
189
|
+
customFetch: undefined,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
|
|
195
|
+
const seen = new Set();
|
|
196
|
+
const results = [];
|
|
197
|
+
for (const p of plugins) {
|
|
198
|
+
if (!seen.has(p.id)) {
|
|
199
|
+
results.push(p);
|
|
200
|
+
seen.add(p.id);
|
|
201
|
+
} else if (p.id) {
|
|
202
|
+
process.stderr.write(
|
|
203
|
+
`Duplicate plugin id "${yellow(p.id)}".\n`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return results;
|
|
208
|
+
}
|
|
@@ -25,7 +25,7 @@ describe('oas3 remove-x-internal', () => {
|
|
|
25
25
|
const { bundle: res } = await bundleDocument({
|
|
26
26
|
document: testDocument,
|
|
27
27
|
externalRefResolver: new BaseResolver(),
|
|
28
|
-
config: makeConfig({}, { 'remove-x-internal': { internalFlagProperty: 'removeit' } }),
|
|
28
|
+
config: await makeConfig({}, { 'remove-x-internal': { internalFlagProperty: 'removeit' } }),
|
|
29
29
|
});
|
|
30
30
|
expect(res.parsed).toMatchInlineSnapshot(`
|
|
31
31
|
openapi: 3.0.0
|
|
@@ -92,7 +92,7 @@ describe('oas3 remove-x-internal', () => {
|
|
|
92
92
|
const { bundle: res } = await bundleDocument({
|
|
93
93
|
document: testDoc,
|
|
94
94
|
externalRefResolver: new BaseResolver(),
|
|
95
|
-
config: makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
95
|
+
config: await makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
96
96
|
});
|
|
97
97
|
expect(res.parsed).toMatchInlineSnapshot(`
|
|
98
98
|
openapi: 3.1.0
|
|
@@ -165,7 +165,7 @@ describe('oas3 remove-x-internal', () => {
|
|
|
165
165
|
const { bundle: res } = await bundleDocument({
|
|
166
166
|
document: testDoc,
|
|
167
167
|
externalRefResolver: new BaseResolver(),
|
|
168
|
-
config: makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
168
|
+
config: await makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
expect(res.parsed).toMatchInlineSnapshot(`
|
|
@@ -239,7 +239,7 @@ describe('oas3 remove-x-internal', () => {
|
|
|
239
239
|
const { bundle: res } = await bundleDocument({
|
|
240
240
|
document: testDoc,
|
|
241
241
|
externalRefResolver: new BaseResolver(),
|
|
242
|
-
config: makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
242
|
+
config: await makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
243
243
|
});
|
|
244
244
|
expect(res.parsed).toMatchInlineSnapshot(`
|
|
245
245
|
openapi: 3.0.1
|
|
@@ -302,7 +302,7 @@ describe('oas2 remove-x-internal', () => {
|
|
|
302
302
|
const { bundle: res } = await bundleDocument({
|
|
303
303
|
document: testDoc,
|
|
304
304
|
externalRefResolver: new BaseResolver(),
|
|
305
|
-
config: makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
305
|
+
config: await makeConfig({}, { 'remove-x-internal': 'on' }),
|
|
306
306
|
});
|
|
307
307
|
expect(res.parsed).toMatchInlineSnapshot(`
|
|
308
308
|
swagger: '2.0'
|
package/src/format/format.ts
CHANGED
|
@@ -16,10 +16,10 @@ import { NormalizedProblem, ProblemSeverity, LineColLocationObject, LocationObje
|
|
|
16
16
|
import { getCodeframe, getLineColLocation } from './codeframes';
|
|
17
17
|
|
|
18
18
|
export type Totals = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
errors: number;
|
|
20
|
+
warnings: number;
|
|
21
|
+
ignored: number;
|
|
22
|
+
};
|
|
23
23
|
|
|
24
24
|
const ERROR_MESSAGE = {
|
|
25
25
|
INVALID_SEVERITY_LEVEL: 'Invalid severity level; accepted values: error or warn',
|
|
@@ -40,13 +40,18 @@ const SEVERITY_NAMES = {
|
|
|
40
40
|
error: 'Error',
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
const CODECLIMATE_SEVERITY_MAPPING = {
|
|
44
|
+
error: 'critical',
|
|
45
|
+
warn: 'minor',
|
|
46
|
+
};
|
|
47
|
+
|
|
43
48
|
const MAX_SUGGEST = 5;
|
|
44
49
|
|
|
45
50
|
function severityToNumber(severity: ProblemSeverity) {
|
|
46
51
|
return severity === 'error' ? 1 : 2;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
export type OutputFormat = 'codeframe' | 'stylish' | 'json' | 'checkstyle';
|
|
54
|
+
export type OutputFormat = 'codeframe' | 'stylish' | 'json' | 'checkstyle' | 'codeclimate';
|
|
50
55
|
|
|
51
56
|
export function getTotals(problems: (NormalizedProblem & { ignored?: boolean })[]): Totals {
|
|
52
57
|
let errors = 0;
|
|
@@ -76,7 +81,7 @@ export function formatProblems(
|
|
|
76
81
|
cwd?: string;
|
|
77
82
|
format?: OutputFormat;
|
|
78
83
|
color?: boolean;
|
|
79
|
-
totals: Totals
|
|
84
|
+
totals: Totals;
|
|
80
85
|
version: string;
|
|
81
86
|
},
|
|
82
87
|
) {
|
|
@@ -142,6 +147,9 @@ export function formatProblems(
|
|
|
142
147
|
process.stdout.write(`</checkstyle>\n`);
|
|
143
148
|
break;
|
|
144
149
|
}
|
|
150
|
+
case 'codeclimate':
|
|
151
|
+
outputForCodeClimate();
|
|
152
|
+
break;
|
|
145
153
|
}
|
|
146
154
|
|
|
147
155
|
if (totalProblems - ignoredProblems > maxProblems) {
|
|
@@ -152,6 +160,25 @@ export function formatProblems(
|
|
|
152
160
|
);
|
|
153
161
|
}
|
|
154
162
|
|
|
163
|
+
function outputForCodeClimate() {
|
|
164
|
+
const issues = problems.map((p) => {
|
|
165
|
+
const location = p.location[0]; // TODO: support multiple location
|
|
166
|
+
const lineCol = getLineColLocation(location);
|
|
167
|
+
return {
|
|
168
|
+
description: p.message,
|
|
169
|
+
location: {
|
|
170
|
+
path: path.relative(cwd, location.source.absoluteRef),
|
|
171
|
+
lines: {
|
|
172
|
+
begin: lineCol.start.line,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
severity: CODECLIMATE_SEVERITY_MAPPING[p.severity],
|
|
176
|
+
fingerprint: `${p.ruleId}${p.location.length > 0 ? '-' + p.location[0].pointer : ''}`,
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
process.stdout.write(JSON.stringify(issues, null, 2));
|
|
180
|
+
}
|
|
181
|
+
|
|
155
182
|
function outputJSON() {
|
|
156
183
|
const resultObject = {
|
|
157
184
|
totals,
|
|
@@ -214,6 +241,9 @@ export function formatProblems(
|
|
|
214
241
|
|
|
215
242
|
function formatStylish(problem: OnlyLineColProblem, locationPad: number, ruleIdPad: number) {
|
|
216
243
|
const color = COLORS[problem.severity];
|
|
244
|
+
if (!SEVERITY_NAMES[problem.severity]) {
|
|
245
|
+
return 'Error not found severity. Please check your config file. Allowed values: \`warn,error,off\`'
|
|
246
|
+
}
|
|
217
247
|
const severityName = color(SEVERITY_NAMES[problem.severity].toLowerCase().padEnd(7));
|
|
218
248
|
const { start } = problem.location[0];
|
|
219
249
|
return ` ${`${start.line}:${start.col}`.padEnd(
|
package/src/index.ts
CHANGED
|
@@ -29,9 +29,13 @@ export {
|
|
|
29
29
|
Region,
|
|
30
30
|
getMergedConfig,
|
|
31
31
|
transformConfig,
|
|
32
|
-
|
|
32
|
+
loadConfig,
|
|
33
|
+
getConfig,
|
|
34
|
+
findConfig,
|
|
35
|
+
CONFIG_FILE_NAMES,
|
|
36
|
+
} from './config';
|
|
37
|
+
|
|
33
38
|
|
|
34
|
-
export { loadConfig, getConfig, findConfig, CONFIG_FILE_NAMES } from './config/load';
|
|
35
39
|
export { RedoclyClient, isRedoclyRegistryURL } from './redocly';
|
|
36
40
|
|
|
37
41
|
export {
|
package/src/lint.ts
CHANGED
|
@@ -7,14 +7,13 @@ import { Oas3Types } from './types/oas3';
|
|
|
7
7
|
import { Oas2Types } from './types/oas2';
|
|
8
8
|
import { NodeType } from './types';
|
|
9
9
|
import { ProblemSeverity, WalkContext, walkDocument } from './walk';
|
|
10
|
-
import { LintConfig, Config } from './config
|
|
10
|
+
import { LintConfig, Config, initRules, defaultPlugin, resolvePlugins } from './config';
|
|
11
11
|
import { normalizeTypes } from './types';
|
|
12
|
-
import { initRules } from './config/rules';
|
|
13
12
|
import { releaseAjvInstance } from './rules/ajv';
|
|
14
13
|
import { detectOpenAPI, Oas3RuleSet, OasMajorVersion, OasVersion, openAPIMajor } from './oas-types';
|
|
15
14
|
import { ConfigTypes } from './types/redocly-yaml';
|
|
16
15
|
import { OasSpec } from './rules/common/spec';
|
|
17
|
-
|
|
16
|
+
|
|
18
17
|
|
|
19
18
|
export async function lint(opts: {
|
|
20
19
|
ref: string;
|
|
@@ -104,9 +103,9 @@ export async function lintConfig(opts: {
|
|
|
104
103
|
oasVersion: OasVersion.Version3_0,
|
|
105
104
|
visitorsData: {},
|
|
106
105
|
};
|
|
106
|
+
const plugins = resolvePlugins([defaultPlugin]);
|
|
107
107
|
const config = new LintConfig({
|
|
108
|
-
plugins
|
|
109
|
-
extends: [],
|
|
108
|
+
plugins,
|
|
110
109
|
rules: { spec: 'error' },
|
|
111
110
|
});
|
|
112
111
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { RedoclyClient } from '../index';
|
|
2
2
|
|
|
3
|
+
jest.mock('node-fetch', () => ({
|
|
4
|
+
default: jest.fn(() => ({
|
|
5
|
+
ok: true,
|
|
6
|
+
json: jest.fn().mockResolvedValue({}),
|
|
7
|
+
})),
|
|
8
|
+
}));
|
|
9
|
+
|
|
3
10
|
describe('RedoclyClient', () => {
|
|
4
11
|
const REDOCLY_DOMAIN_US = 'redocly.com';
|
|
5
12
|
const REDOCLY_DOMAIN_EU = 'eu.redocly.com';
|
package/src/redocly/index.ts
CHANGED
|
@@ -3,10 +3,12 @@ import { resolve } from 'path';
|
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
import { red, green, gray, yellow } from 'colorette';
|
|
5
5
|
import { RegistryApi } from './registry-api';
|
|
6
|
-
import {
|
|
6
|
+
import { DEFAULT_REGION, DOMAINS, AVAILABLE_REGIONS } from '../config/config';
|
|
7
7
|
import { RegionalToken, RegionalTokenWithValidity } from './redocly-client-types';
|
|
8
8
|
import { isNotEmptyObject } from '../utils';
|
|
9
9
|
|
|
10
|
+
import type { AccessTokens, Region } from '../config/types';
|
|
11
|
+
|
|
10
12
|
const TOKEN_FILENAME = '.redocly-config.json';
|
|
11
13
|
|
|
12
14
|
let REDOCLY_DOMAIN: string; // workaround for the isRedoclyRegistryURL, see more below
|
|
@@ -69,7 +71,7 @@ export class RedoclyClient {
|
|
|
69
71
|
process.stderr.write(
|
|
70
72
|
`${yellow(
|
|
71
73
|
'Warning:',
|
|
72
|
-
)} invalid Redocly API key. Use "npx @redocly/
|
|
74
|
+
)} invalid Redocly API key. Use "npx @redocly/cli login" to provide your API key\n`,
|
|
73
75
|
);
|
|
74
76
|
return undefined;
|
|
75
77
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fetch, { RequestInit, HeadersInit } from 'node-fetch';
|
|
2
2
|
import { RegistryApiTypes } from './registry-api-types';
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_REGION, DOMAINS } from '../config/config';
|
|
4
4
|
import { isNotEmptyObject } from '../utils';
|
|
5
5
|
const version = require('../../package.json').version;
|
|
6
6
|
|
|
7
|
+
import type { AccessTokens, Region } from '../config/types';
|
|
8
|
+
|
|
7
9
|
export class RegistryApi {
|
|
8
10
|
constructor(private accessTokens: AccessTokens, private region: Region) {}
|
|
9
11
|
|
package/src/resolve.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import * as url from 'url';
|
|
4
|
-
|
|
5
3
|
import { OasRef } from './typings/openapi';
|
|
6
4
|
import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl } from './ref-utils';
|
|
7
5
|
import type { YAMLNode, LoadOptions } from 'yaml-ast-parser';
|
|
8
6
|
import { NormalizedNodeType, isNamedType } from './types';
|
|
9
7
|
import { readFileFromUrl, parseYaml } from './utils';
|
|
10
|
-
import { ResolveConfig } from './config/
|
|
8
|
+
import { ResolveConfig } from './config/types';
|
|
11
9
|
|
|
12
10
|
export type CollectedRefs = Map<string /* absoluteFilePath */, Document>;
|
|
13
11
|
|
|
@@ -105,7 +103,7 @@ export class BaseResolver {
|
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
if (base && isAbsoluteUrl(base)) {
|
|
108
|
-
return
|
|
106
|
+
return new URL(ref, base).href;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
return path.resolve(base ? path.dirname(base) : process.cwd(), ref);
|
|
@@ -21,7 +21,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
|
|
|
21
21
|
const results = await lintDocument({
|
|
22
22
|
externalRefResolver: new BaseResolver(),
|
|
23
23
|
document,
|
|
24
|
-
config: makeConfig({
|
|
24
|
+
config: await makeConfig({
|
|
25
25
|
'no-unresolved-refs': 'error',
|
|
26
26
|
}),
|
|
27
27
|
});
|
|
@@ -61,7 +61,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
|
|
|
61
61
|
const results = await lintDocument({
|
|
62
62
|
externalRefResolver: new BaseResolver(),
|
|
63
63
|
document,
|
|
64
|
-
config: makeConfig({
|
|
64
|
+
config: await makeConfig({
|
|
65
65
|
'no-unresolved-refs': 'error',
|
|
66
66
|
}),
|
|
67
67
|
});
|
|
@@ -118,7 +118,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
|
|
|
118
118
|
const results = await lintDocument({
|
|
119
119
|
externalRefResolver: new BaseResolver(),
|
|
120
120
|
document,
|
|
121
|
-
config: makeConfig({
|
|
121
|
+
config: await makeConfig({
|
|
122
122
|
'no-unresolved-refs': 'error',
|
|
123
123
|
}),
|
|
124
124
|
});
|
|
@@ -142,7 +142,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
|
|
|
142
142
|
const results = await lintDocument({
|
|
143
143
|
externalRefResolver: new BaseResolver(),
|
|
144
144
|
document,
|
|
145
|
-
config: makeConfig({
|
|
145
|
+
config: await makeConfig({
|
|
146
146
|
'no-unresolved-refs': 'error',
|
|
147
147
|
}),
|
|
148
148
|
});
|