@redocly/openapi-core 1.5.0 → 1.7.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 +13 -0
- package/lib/bundle.d.ts +8 -6
- package/lib/bundle.js +48 -14
- package/lib/config/config-resolvers.d.ts +9 -1
- package/lib/config/config-resolvers.js +22 -1
- package/lib/config/load.d.ts +11 -3
- package/lib/config/load.js +12 -6
- package/lib/lint.d.ts +6 -3
- package/lib/lint.js +14 -3
- package/lib/rules/ajv.d.ts +2 -2
- package/lib/rules/ajv.js +2 -2
- package/lib/rules/utils.d.ts +2 -2
- package/lib/types/asyncapi.js +2 -8
- package/lib/types/portal-config-schema.d.ts +22 -2465
- package/lib/types/portal-config-schema.js +56 -38
- package/lib/types/redocly-yaml.js +4 -4
- package/lib/typings/openapi.d.ts +14 -11
- package/lib/visitors.d.ts +5 -5
- package/lib/visitors.js +4 -6
- package/lib/walk.d.ts +3 -3
- package/lib/walk.js +2 -2
- package/package.json +2 -2
- package/src/__tests__/lint.test.ts +2 -2
- package/src/bundle.ts +67 -26
- package/src/config/__tests__/fixtures/resolve-refs-in-config/config-with-refs.yaml +8 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/rules.yaml +2 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/seo.yaml +1 -0
- package/src/config/__tests__/load.test.ts +80 -1
- package/src/config/config-resolvers.ts +41 -11
- package/src/config/load.ts +36 -19
- package/src/lint.ts +32 -10
- package/src/rules/ajv.ts +6 -4
- package/src/rules/utils.ts +2 -2
- package/src/types/asyncapi.ts +2 -8
- package/src/types/portal-config-schema.ts +65 -42
- package/src/types/redocly-yaml.ts +2 -4
- package/src/typings/openapi.ts +15 -11
- package/src/visitors.ts +20 -22
- package/src/walk.ts +8 -8
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import { isAbsoluteUrl } from '../ref-utils';
|
|
3
3
|
import { pickDefined } from '../utils';
|
|
4
|
-
import { BaseResolver } from '../resolve';
|
|
4
|
+
import { resolveDocument, BaseResolver } from '../resolve';
|
|
5
5
|
import { defaultPlugin } from './builtIn';
|
|
6
6
|
import {
|
|
7
7
|
getResolveConfig,
|
|
@@ -11,6 +11,14 @@ import {
|
|
|
11
11
|
prefixRules,
|
|
12
12
|
transformConfig,
|
|
13
13
|
} from './utils';
|
|
14
|
+
import { isBrowser } from '../env';
|
|
15
|
+
import { isNotString, isString, isDefined, parseYaml, keysOf } from '../utils';
|
|
16
|
+
import { Config } from './config';
|
|
17
|
+
import { colorize, logger } from '../logger';
|
|
18
|
+
import { asserts, buildAssertCustomFunction } from '../rules/common/assertions/asserts';
|
|
19
|
+
import { normalizeTypes } from '../types';
|
|
20
|
+
import { ConfigTypes } from '../types/redocly-yaml';
|
|
21
|
+
|
|
14
22
|
import type {
|
|
15
23
|
StyleguideRawConfig,
|
|
16
24
|
ApiStyleguideRawConfig,
|
|
@@ -21,17 +29,39 @@ import type {
|
|
|
21
29
|
RuleConfig,
|
|
22
30
|
DeprecatedInRawConfig,
|
|
23
31
|
} from './types';
|
|
24
|
-
import { isBrowser } from '../env';
|
|
25
|
-
import { isNotString, isString, isDefined, parseYaml, keysOf } from '../utils';
|
|
26
|
-
import { Config } from './config';
|
|
27
|
-
import { colorize, logger } from '../logger';
|
|
28
|
-
import {
|
|
29
|
-
Asserts,
|
|
30
|
-
AssertionFn,
|
|
31
|
-
asserts,
|
|
32
|
-
buildAssertCustomFunction,
|
|
33
|
-
} from '../rules/common/assertions/asserts';
|
|
34
32
|
import type { Assertion, AssertionDefinition, RawAssertion } from '../rules/common/assertions';
|
|
33
|
+
import type { Asserts, AssertionFn } from '../rules/common/assertions/asserts';
|
|
34
|
+
import type { BundleOptions } from '../bundle';
|
|
35
|
+
import type { Document, ResolvedRefMap } from '../resolve';
|
|
36
|
+
|
|
37
|
+
export async function resolveConfigFileAndRefs({
|
|
38
|
+
configPath,
|
|
39
|
+
externalRefResolver = new BaseResolver(),
|
|
40
|
+
base = null,
|
|
41
|
+
}: Omit<BundleOptions, 'config'> & { configPath?: string }): Promise<{
|
|
42
|
+
document: Document;
|
|
43
|
+
resolvedRefMap: ResolvedRefMap;
|
|
44
|
+
}> {
|
|
45
|
+
if (!configPath) {
|
|
46
|
+
throw new Error('Reference to a config is required.\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const document = await externalRefResolver.resolveDocument(base, configPath, true);
|
|
50
|
+
|
|
51
|
+
if (document instanceof Error) {
|
|
52
|
+
throw document;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const types = normalizeTypes(ConfigTypes);
|
|
56
|
+
|
|
57
|
+
const resolvedRefMap = await resolveDocument({
|
|
58
|
+
rootDocument: document,
|
|
59
|
+
rootType: types.ConfigRoot,
|
|
60
|
+
externalRefResolver,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return { document, resolvedRefMap };
|
|
64
|
+
}
|
|
35
65
|
|
|
36
66
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
37
67
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
package/src/config/load.ts
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { RedoclyClient } from '../redocly';
|
|
4
|
-
import { isEmptyObject,
|
|
4
|
+
import { isEmptyObject, doesYamlFileExist } from '../utils';
|
|
5
5
|
import { parseYaml } from '../js-yaml';
|
|
6
6
|
import { Config, DOMAINS } from './config';
|
|
7
7
|
import { ConfigValidationError, transformConfig } from './utils';
|
|
8
|
-
import { resolveConfig } from './config-resolvers';
|
|
8
|
+
import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers';
|
|
9
|
+
import { bundleConfig } from '../bundle';
|
|
9
10
|
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
RawUniversalConfig,
|
|
15
|
-
Region,
|
|
16
|
-
} from './types';
|
|
17
|
-
import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
|
11
|
+
import type { Document } from '../resolve';
|
|
12
|
+
import type { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
|
13
|
+
import type { RawConfig, RawUniversalConfig, Region } from './types';
|
|
14
|
+
import type { BaseResolver, ResolvedRefMap } from '../resolve';
|
|
18
15
|
|
|
19
16
|
async function addConfigMetadata({
|
|
20
17
|
rawConfig,
|
|
@@ -73,17 +70,30 @@ async function addConfigMetadata({
|
|
|
73
70
|
);
|
|
74
71
|
}
|
|
75
72
|
|
|
73
|
+
export type RawConfigProcessor = (
|
|
74
|
+
rawConfig: Document,
|
|
75
|
+
resolvedRefMap: ResolvedRefMap
|
|
76
|
+
) => void | Promise<void>;
|
|
77
|
+
|
|
76
78
|
export async function loadConfig(
|
|
77
79
|
options: {
|
|
78
80
|
configPath?: string;
|
|
79
81
|
customExtends?: string[];
|
|
80
|
-
processRawConfig?:
|
|
82
|
+
processRawConfig?: RawConfigProcessor;
|
|
83
|
+
externalRefResolver?: BaseResolver;
|
|
81
84
|
files?: string[];
|
|
82
85
|
region?: Region;
|
|
83
86
|
} = {}
|
|
84
87
|
): Promise<Config> {
|
|
85
|
-
const {
|
|
86
|
-
|
|
88
|
+
const {
|
|
89
|
+
configPath = findConfig(),
|
|
90
|
+
customExtends,
|
|
91
|
+
processRawConfig,
|
|
92
|
+
files,
|
|
93
|
+
region,
|
|
94
|
+
externalRefResolver,
|
|
95
|
+
} = options;
|
|
96
|
+
const rawConfig = await getConfig({ configPath, processRawConfig, externalRefResolver });
|
|
87
97
|
|
|
88
98
|
const redoclyClient = new RedoclyClient();
|
|
89
99
|
const tokens = await redoclyClient.getTokens();
|
|
@@ -116,17 +126,24 @@ export function findConfig(dir?: string): string | undefined {
|
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
export async function getConfig(
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
options: {
|
|
130
|
+
configPath?: string;
|
|
131
|
+
processRawConfig?: RawConfigProcessor;
|
|
132
|
+
externalRefResolver?: BaseResolver;
|
|
133
|
+
} = {}
|
|
121
134
|
): Promise<RawConfig> {
|
|
135
|
+
const { configPath = findConfig(), processRawConfig, externalRefResolver } = options;
|
|
122
136
|
if (!configPath || !doesYamlFileExist(configPath)) return {};
|
|
123
137
|
try {
|
|
124
|
-
const
|
|
125
|
-
|
|
138
|
+
const { document, resolvedRefMap } = await resolveConfigFileAndRefs({
|
|
139
|
+
configPath,
|
|
140
|
+
externalRefResolver,
|
|
141
|
+
});
|
|
126
142
|
if (typeof processRawConfig === 'function') {
|
|
127
|
-
await processRawConfig(
|
|
143
|
+
await processRawConfig(document, resolvedRefMap);
|
|
128
144
|
}
|
|
129
|
-
|
|
145
|
+
const bundledConfig = await bundleConfig(document, resolvedRefMap);
|
|
146
|
+
return transformConfig(bundledConfig);
|
|
130
147
|
} catch (e) {
|
|
131
148
|
if (e instanceof ConfigValidationError) {
|
|
132
149
|
throw e;
|
package/src/lint.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { BaseResolver, resolveDocument,
|
|
1
|
+
import { BaseResolver, resolveDocument, makeDocumentFromString } from './resolve';
|
|
2
2
|
import { normalizeVisitors } from './visitors';
|
|
3
|
-
import {
|
|
4
|
-
import { ProblemSeverity, WalkContext, walkDocument } from './walk';
|
|
3
|
+
import { walkDocument } from './walk';
|
|
5
4
|
import { StyleguideConfig, Config, initRules, defaultPlugin, resolvePlugins } from './config';
|
|
6
5
|
import { normalizeTypes } from './types';
|
|
7
6
|
import { releaseAjvInstance } from './rules/ajv';
|
|
8
7
|
import { SpecVersion, getMajorSpecVersion, detectSpec, getTypes } from './oas-types';
|
|
9
8
|
import { ConfigTypes } from './types/redocly-yaml';
|
|
10
9
|
import { Spec } from './rules/common/spec';
|
|
10
|
+
import { NoUnresolvedRefs } from './rules/no-unresolved-refs';
|
|
11
|
+
|
|
12
|
+
import type { Document, ResolvedRefMap } from './resolve';
|
|
13
|
+
import type { ProblemSeverity, WalkContext } from './walk';
|
|
14
|
+
import type { NodeType } from './types';
|
|
15
|
+
import type { NestedVisitObject, Oas3Visitor, RuleInstanceConfig } from './visitors';
|
|
11
16
|
|
|
12
17
|
export async function lint(opts: {
|
|
13
18
|
ref: string;
|
|
@@ -102,8 +107,13 @@ export async function lintDocument(opts: {
|
|
|
102
107
|
return ctx.problems.map((problem) => config.addProblemToIgnore(problem));
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
export async function lintConfig(opts: {
|
|
106
|
-
|
|
110
|
+
export async function lintConfig(opts: {
|
|
111
|
+
document: Document;
|
|
112
|
+
resolvedRefMap?: ResolvedRefMap;
|
|
113
|
+
severity?: ProblemSeverity;
|
|
114
|
+
externalRefResolver?: BaseResolver;
|
|
115
|
+
}) {
|
|
116
|
+
const { document, severity, externalRefResolver = new BaseResolver() } = opts;
|
|
107
117
|
|
|
108
118
|
const ctx: WalkContext = {
|
|
109
119
|
problems: [],
|
|
@@ -117,21 +127,33 @@ export async function lintConfig(opts: { document: Document; severity?: ProblemS
|
|
|
117
127
|
});
|
|
118
128
|
|
|
119
129
|
const types = normalizeTypes(ConfigTypes, config);
|
|
120
|
-
const rules
|
|
130
|
+
const rules: (RuleInstanceConfig & {
|
|
131
|
+
visitor: NestedVisitObject<unknown, Oas3Visitor | Oas3Visitor[]>;
|
|
132
|
+
})[] = [
|
|
121
133
|
{
|
|
122
134
|
severity: severity || 'error',
|
|
123
135
|
ruleId: 'configuration spec',
|
|
124
136
|
visitor: Spec({ severity: 'error' }),
|
|
125
137
|
},
|
|
138
|
+
{
|
|
139
|
+
severity: severity || 'error',
|
|
140
|
+
ruleId: 'configuration no-unresolved-refs',
|
|
141
|
+
visitor: NoUnresolvedRefs({ severity: 'error' }),
|
|
142
|
+
},
|
|
126
143
|
];
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
144
|
+
const normalizedVisitors = normalizeVisitors(rules, types);
|
|
145
|
+
const resolvedRefMap =
|
|
146
|
+
opts.resolvedRefMap ||
|
|
147
|
+
(await resolveDocument({
|
|
148
|
+
rootDocument: document,
|
|
149
|
+
rootType: types.ConfigRoot,
|
|
150
|
+
externalRefResolver,
|
|
151
|
+
}));
|
|
130
152
|
walkDocument({
|
|
131
153
|
document,
|
|
132
154
|
rootType: types.ConfigRoot,
|
|
133
155
|
normalizedVisitors,
|
|
134
|
-
resolvedRefMap
|
|
156
|
+
resolvedRefMap,
|
|
135
157
|
ctx,
|
|
136
158
|
});
|
|
137
159
|
|
package/src/rules/ajv.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import Ajv
|
|
1
|
+
import Ajv from '@redocly/ajv/dist/2020';
|
|
2
2
|
import { Location, escapePointer } from '../ref-utils';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import type { ValidateFunction, ErrorObject } from '@redocly/ajv/dist/2020';
|
|
5
|
+
import type { ResolveFn } from '../walk';
|
|
4
6
|
|
|
5
7
|
let ajvInstance: Ajv | null = null;
|
|
6
8
|
|
|
@@ -21,10 +23,10 @@ function getAjv(resolve: ResolveFn, allowAdditionalProperties: boolean) {
|
|
|
21
23
|
allowUnionTypes: true,
|
|
22
24
|
validateFormats: false, // TODO: fix it
|
|
23
25
|
defaultUnevaluatedProperties: allowAdditionalProperties,
|
|
24
|
-
loadSchemaSync(base: string, $ref: string) {
|
|
26
|
+
loadSchemaSync(base: string, $ref: string, $id: string) {
|
|
25
27
|
const resolvedRef = resolve({ $ref }, base.split('#')[0]);
|
|
26
28
|
if (!resolvedRef || !resolvedRef.location) return false;
|
|
27
|
-
return { $id: resolvedRef.location.
|
|
29
|
+
return { $id: resolvedRef.location.source.absoluteRef + '#' + $id, ...resolvedRef.node };
|
|
28
30
|
},
|
|
29
31
|
logger: false,
|
|
30
32
|
});
|
package/src/rules/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import levenshtein = require('js-levenshtein');
|
|
|
2
2
|
import { UserContext } from '../walk';
|
|
3
3
|
import { Location } from '../ref-utils';
|
|
4
4
|
import { validateJsonSchema } from './ajv';
|
|
5
|
-
import { Oas3Schema, Referenced } from '../typings/openapi';
|
|
5
|
+
import { Oas3Schema, Oas3_1Schema, Referenced } from '../typings/openapi';
|
|
6
6
|
import { showErrorForDeprecatedField, showWarningForDeprecatedField } from '../utils';
|
|
7
7
|
|
|
8
8
|
export function oasTypeOf(value: unknown) {
|
|
@@ -91,7 +91,7 @@ export function getSuggest(given: string, variants: string[]): string[] {
|
|
|
91
91
|
|
|
92
92
|
export function validateExample(
|
|
93
93
|
example: any,
|
|
94
|
-
schema: Referenced<Oas3Schema>,
|
|
94
|
+
schema: Referenced<Oas3Schema | Oas3_1Schema>,
|
|
95
95
|
dataLoc: Location,
|
|
96
96
|
{ resolve, location, report }: UserContext,
|
|
97
97
|
allowAdditionalProperties: boolean
|
package/src/types/asyncapi.ts
CHANGED
|
@@ -274,10 +274,7 @@ const OperationBindings: NodeType = {
|
|
|
274
274
|
|
|
275
275
|
const OperationTrait: NodeType = {
|
|
276
276
|
properties: {
|
|
277
|
-
tags:
|
|
278
|
-
type: 'array',
|
|
279
|
-
items: { type: 'string' },
|
|
280
|
-
},
|
|
277
|
+
tags: 'TagList',
|
|
281
278
|
summary: { type: 'string' },
|
|
282
279
|
description: { type: 'string' },
|
|
283
280
|
externalDocs: 'ExternalDocs',
|
|
@@ -311,10 +308,7 @@ const MessageTrait: NodeType = {
|
|
|
311
308
|
|
|
312
309
|
const Operation: NodeType = {
|
|
313
310
|
properties: {
|
|
314
|
-
tags:
|
|
315
|
-
type: 'array',
|
|
316
|
-
items: { type: 'string' },
|
|
317
|
-
},
|
|
311
|
+
tags: 'TagList',
|
|
318
312
|
summary: { type: 'string' },
|
|
319
313
|
description: { type: 'string' },
|
|
320
314
|
externalDocs: 'ExternalDocs',
|
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
DEFAULT_TEAM_CLAIM_NAME,
|
|
5
5
|
} from '../config';
|
|
6
6
|
|
|
7
|
+
import type { NodeType } from '.';
|
|
8
|
+
|
|
7
9
|
const oidcIssuerMetadataSchema = {
|
|
8
10
|
type: 'object',
|
|
9
11
|
properties: {
|
|
@@ -87,8 +89,9 @@ const authProviderConfigSchema = {
|
|
|
87
89
|
|
|
88
90
|
export const ssoConfigSchema = {
|
|
89
91
|
type: 'object',
|
|
92
|
+
properties: {},
|
|
90
93
|
additionalProperties: authProviderConfigSchema,
|
|
91
|
-
} as
|
|
94
|
+
} as NodeType;
|
|
92
95
|
|
|
93
96
|
const redirectConfigSchema = {
|
|
94
97
|
type: 'object',
|
|
@@ -97,8 +100,14 @@ const redirectConfigSchema = {
|
|
|
97
100
|
type: { type: 'number', default: 301 },
|
|
98
101
|
},
|
|
99
102
|
required: ['to'],
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
} as NodeType;
|
|
104
|
+
|
|
105
|
+
const redirectsConfigSchema = {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {},
|
|
108
|
+
additionalProperties: 'redirectConfigSchema',
|
|
109
|
+
default: {},
|
|
110
|
+
} as NodeType;
|
|
102
111
|
|
|
103
112
|
export const apiConfigSchema = {
|
|
104
113
|
type: 'object',
|
|
@@ -147,18 +156,21 @@ const seoConfigSchema = {
|
|
|
147
156
|
},
|
|
148
157
|
},
|
|
149
158
|
},
|
|
150
|
-
additionalProperties: false,
|
|
151
159
|
} as const;
|
|
152
160
|
|
|
153
|
-
const rbacScopeItemsSchema = {
|
|
161
|
+
const rbacScopeItemsSchema = {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {},
|
|
164
|
+
additionalProperties: { type: 'string' },
|
|
165
|
+
} as NodeType;
|
|
154
166
|
|
|
155
167
|
const rbacConfigSchema = {
|
|
156
168
|
type: 'object',
|
|
157
169
|
properties: {
|
|
158
|
-
defaults: rbacScopeItemsSchema,
|
|
170
|
+
defaults: 'rbacScopeItemsSchema',
|
|
159
171
|
},
|
|
160
172
|
additionalProperties: rbacScopeItemsSchema,
|
|
161
|
-
} as
|
|
173
|
+
} as NodeType;
|
|
162
174
|
|
|
163
175
|
const graviteeAdapterConfigSchema = {
|
|
164
176
|
type: 'object',
|
|
@@ -234,14 +246,38 @@ const devOnboardingAdapterConfigSchema = {
|
|
|
234
246
|
const devOnboardingConfigSchema = {
|
|
235
247
|
type: 'object',
|
|
236
248
|
required: ['adapters'],
|
|
237
|
-
additionalProperties: false,
|
|
238
249
|
properties: {
|
|
239
250
|
adapters: {
|
|
240
251
|
type: 'array',
|
|
241
252
|
items: devOnboardingAdapterConfigSchema,
|
|
242
253
|
},
|
|
243
254
|
},
|
|
244
|
-
} as
|
|
255
|
+
} as NodeType;
|
|
256
|
+
|
|
257
|
+
const i18ConfigSchema = {
|
|
258
|
+
type: 'object',
|
|
259
|
+
properties: {
|
|
260
|
+
defaultLocale: {
|
|
261
|
+
type: 'string',
|
|
262
|
+
},
|
|
263
|
+
locales: {
|
|
264
|
+
type: 'array',
|
|
265
|
+
items: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
code: {
|
|
269
|
+
type: 'string',
|
|
270
|
+
},
|
|
271
|
+
name: {
|
|
272
|
+
type: 'string',
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
required: ['code'],
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
required: ['defaultLocale', 'locales'],
|
|
280
|
+
} as NodeType;
|
|
245
281
|
|
|
246
282
|
const responseHeaderSchema = {
|
|
247
283
|
type: 'object',
|
|
@@ -253,14 +289,25 @@ const responseHeaderSchema = {
|
|
|
253
289
|
required: ['name', 'value'],
|
|
254
290
|
} as const;
|
|
255
291
|
|
|
292
|
+
export const PortalConfigNodeTypes: Record<string, NodeType> = {
|
|
293
|
+
seoConfigSchema,
|
|
294
|
+
rbacConfigSchema,
|
|
295
|
+
rbacScopeItemsSchema,
|
|
296
|
+
ssoConfigSchema,
|
|
297
|
+
devOnboardingConfigSchema,
|
|
298
|
+
i18ConfigSchema,
|
|
299
|
+
redirectsConfigSchema,
|
|
300
|
+
redirectConfigSchema,
|
|
301
|
+
// TODO: Extract other types that need to be linted in the config
|
|
302
|
+
};
|
|
303
|
+
|
|
256
304
|
export const redoclyConfigSchema = {
|
|
257
305
|
type: 'object',
|
|
258
306
|
properties: {
|
|
259
307
|
licenseKey: { type: 'string' },
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
rbac: rbacConfigSchema,
|
|
308
|
+
redirects: 'redirectsConfigSchema',
|
|
309
|
+
seo: 'seoConfigSchema',
|
|
310
|
+
rbac: 'rbacConfigSchema',
|
|
264
311
|
responseHeaders: {
|
|
265
312
|
type: 'object',
|
|
266
313
|
additionalProperties: {
|
|
@@ -282,38 +329,13 @@ export const redoclyConfigSchema = {
|
|
|
282
329
|
type: 'object',
|
|
283
330
|
additionalProperties: apiConfigSchema,
|
|
284
331
|
},
|
|
285
|
-
sso: ssoConfigSchema,
|
|
286
|
-
developerOnboarding: devOnboardingConfigSchema,
|
|
287
|
-
i18n:
|
|
288
|
-
type: 'object',
|
|
289
|
-
properties: {
|
|
290
|
-
defaultLocale: {
|
|
291
|
-
type: 'string',
|
|
292
|
-
},
|
|
293
|
-
locales: {
|
|
294
|
-
type: 'array',
|
|
295
|
-
items: {
|
|
296
|
-
type: 'object',
|
|
297
|
-
properties: {
|
|
298
|
-
code: {
|
|
299
|
-
type: 'string',
|
|
300
|
-
},
|
|
301
|
-
name: {
|
|
302
|
-
type: 'string',
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
required: ['code'],
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
additionalProperties: false,
|
|
310
|
-
required: ['defaultLocale', 'locales'],
|
|
311
|
-
},
|
|
332
|
+
sso: 'ssoConfigSchema',
|
|
333
|
+
developerOnboarding: 'devOnboardingConfigSchema',
|
|
334
|
+
i18n: 'i18ConfigSchema',
|
|
312
335
|
metadata: metadataConfigSchema,
|
|
313
336
|
},
|
|
314
337
|
default: {},
|
|
315
|
-
|
|
316
|
-
} as const;
|
|
338
|
+
} as NodeType;
|
|
317
339
|
|
|
318
340
|
export const environmentSchema = {
|
|
319
341
|
oneOf: [
|
|
@@ -335,6 +357,7 @@ export const rootRedoclyConfigSchema = {
|
|
|
335
357
|
...redoclyConfigSchema.properties,
|
|
336
358
|
env: {
|
|
337
359
|
type: 'object',
|
|
360
|
+
properties: {},
|
|
338
361
|
additionalProperties: environmentSchema,
|
|
339
362
|
},
|
|
340
363
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { rootRedoclyConfigSchema, apiConfigSchema } from './portal-config-schema';
|
|
2
2
|
import { themeConfigSchema } from './theme-config';
|
|
3
3
|
import { NodeType, listOf } from '.';
|
|
4
|
-
import { Oas3_1Types } from './oas3_1';
|
|
5
4
|
import { omitObjectProps, pickObjectProps, isCustomRuleId } from '../utils';
|
|
5
|
+
import { PortalConfigNodeTypes } from './portal-config-schema';
|
|
6
6
|
|
|
7
7
|
const builtInCommonRules = [
|
|
8
8
|
'spec',
|
|
@@ -236,8 +236,6 @@ const ConfigApisProperties: NodeType = {
|
|
|
236
236
|
type: 'string',
|
|
237
237
|
},
|
|
238
238
|
},
|
|
239
|
-
lint: 'ConfigStyleguide', // deprecated
|
|
240
|
-
styleguide: 'ConfigStyleguide', // deprecated
|
|
241
239
|
...ConfigStyleguide.properties,
|
|
242
240
|
'features.openapi': 'ConfigReferenceDocs', // deprecated
|
|
243
241
|
'features.mockServer': 'ConfigMockServer', // deprecated
|
|
@@ -997,7 +995,6 @@ const ConfigMockServer: NodeType = {
|
|
|
997
995
|
};
|
|
998
996
|
|
|
999
997
|
export const ConfigTypes: Record<string, NodeType> = {
|
|
1000
|
-
...Oas3_1Types,
|
|
1001
998
|
Assert,
|
|
1002
999
|
ConfigRoot,
|
|
1003
1000
|
ConfigApis,
|
|
@@ -1062,4 +1059,5 @@ export const ConfigTypes: Record<string, NodeType> = {
|
|
|
1062
1059
|
Typography,
|
|
1063
1060
|
AssertionDefinitionAssertions,
|
|
1064
1061
|
AssertionDefinitionSubject,
|
|
1062
|
+
...PortalConfigNodeTypes,
|
|
1065
1063
|
};
|
package/src/typings/openapi.ts
CHANGED
|
@@ -111,14 +111,14 @@ export interface Oas3Xml {
|
|
|
111
111
|
wrapped?: string;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
// common fields for OpenAPI Schema v3.x
|
|
115
|
+
interface Oas3XSchemaBase<T> {
|
|
115
116
|
$ref?: string;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
additionalProperties?: boolean | Oas3Schema;
|
|
117
|
+
properties?: { [name: string]: T };
|
|
118
|
+
additionalProperties?: boolean | T;
|
|
119
119
|
description?: string;
|
|
120
120
|
default?: any;
|
|
121
|
-
items?:
|
|
121
|
+
items?: T;
|
|
122
122
|
required?: string[];
|
|
123
123
|
readOnly?: boolean;
|
|
124
124
|
writeOnly?: boolean;
|
|
@@ -127,10 +127,10 @@ export interface Oas3Schema {
|
|
|
127
127
|
externalDocs?: Oas3ExternalDocs;
|
|
128
128
|
discriminator?: Oas3Discriminator;
|
|
129
129
|
nullable?: boolean;
|
|
130
|
-
oneOf?:
|
|
131
|
-
anyOf?:
|
|
132
|
-
allOf?:
|
|
133
|
-
not?:
|
|
130
|
+
oneOf?: T[];
|
|
131
|
+
anyOf?: T[];
|
|
132
|
+
allOf?: T[];
|
|
133
|
+
not?: T;
|
|
134
134
|
|
|
135
135
|
title?: string;
|
|
136
136
|
multipleOf?: number;
|
|
@@ -153,11 +153,15 @@ export interface Oas3Schema {
|
|
|
153
153
|
'x-tags'?: string[];
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
export
|
|
156
|
+
export interface Oas3Schema extends Oas3XSchemaBase<Oas3Schema> {
|
|
157
|
+
type?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface Oas3_1Schema extends Oas3XSchemaBase<Oas3_1Schema> {
|
|
157
161
|
type?: string | string[];
|
|
158
162
|
examples?: any[];
|
|
159
163
|
prefixItems?: Oas3_1Schema[];
|
|
160
|
-
}
|
|
164
|
+
}
|
|
161
165
|
|
|
162
166
|
export interface Oas3_1Definition extends Oas3Definition {
|
|
163
167
|
webhooks?: Oas3_1Webhooks;
|
package/src/visitors.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { SpecExtension } from './types';
|
|
2
|
+
|
|
3
|
+
import type { NormalizedNodeType } from './types';
|
|
4
|
+
import type { Stack } from './utils';
|
|
5
|
+
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
6
|
+
import type { Location } from './ref-utils';
|
|
1
7
|
import type {
|
|
2
8
|
Oas3Definition,
|
|
3
9
|
Oas3ExternalDocs,
|
|
@@ -26,7 +32,6 @@ import type {
|
|
|
26
32
|
Oas3Discriminator,
|
|
27
33
|
Oas3Callback,
|
|
28
34
|
} from './typings/openapi';
|
|
29
|
-
|
|
30
35
|
import type {
|
|
31
36
|
Oas2Definition,
|
|
32
37
|
Oas2Tag,
|
|
@@ -44,12 +49,7 @@ import type {
|
|
|
44
49
|
Oas2Parameter,
|
|
45
50
|
Oas2SecurityScheme,
|
|
46
51
|
} from './typings/swagger';
|
|
47
|
-
|
|
48
|
-
import { NormalizedNodeType, SpecExtension } from './types';
|
|
49
|
-
import type { Stack } from './utils';
|
|
50
|
-
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
51
|
-
import type { Location } from './ref-utils';
|
|
52
|
-
import { Async2Definition } from './typings/asyncapi';
|
|
52
|
+
import type { Async2Definition } from './typings/asyncapi';
|
|
53
53
|
|
|
54
54
|
export type SkipFunctionContext = Pick<
|
|
55
55
|
UserContext,
|
|
@@ -297,7 +297,7 @@ export type RuleInstanceConfig = {
|
|
|
297
297
|
};
|
|
298
298
|
|
|
299
299
|
export function normalizeVisitors<T extends BaseVisitor>(
|
|
300
|
-
visitorsConfig: (RuleInstanceConfig & { visitor: NestedVisitObject<
|
|
300
|
+
visitorsConfig: (RuleInstanceConfig & { visitor: NestedVisitObject<unknown, T> })[],
|
|
301
301
|
types: Record<keyof T, NormalizedNodeType>
|
|
302
302
|
): NormalizedOasVisitors<T> {
|
|
303
303
|
const normalizedVisitors: NormalizedOasVisitors<T> = {} as any;
|
|
@@ -377,12 +377,10 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
377
377
|
|
|
378
378
|
function addWeakFromStack(ruleConf: RuleInstanceConfig, stack: NormalizedNodeType[]) {
|
|
379
379
|
for (const interType of stack.slice(1)) {
|
|
380
|
-
(normalizedVisitors as any)[interType.name] =
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
leave: [],
|
|
385
|
-
} as any);
|
|
380
|
+
(normalizedVisitors as any)[interType.name] = normalizedVisitors[interType.name] || {
|
|
381
|
+
enter: [],
|
|
382
|
+
leave: [],
|
|
383
|
+
};
|
|
386
384
|
normalizedVisitors[interType.name].enter.push({
|
|
387
385
|
...ruleConf,
|
|
388
386
|
visit: () => undefined,
|
|
@@ -398,7 +396,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
398
396
|
}
|
|
399
397
|
|
|
400
398
|
function findLegacyVisitorNode<T>(
|
|
401
|
-
visitor: NestedVisitObject<
|
|
399
|
+
visitor: NestedVisitObject<unknown, T>,
|
|
402
400
|
typeName: keyof T | Array<keyof T>
|
|
403
401
|
) {
|
|
404
402
|
if (Array.isArray(typeName)) {
|
|
@@ -411,7 +409,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
411
409
|
|
|
412
410
|
function normalizeVisitorLevel(
|
|
413
411
|
ruleConf: RuleInstanceConfig,
|
|
414
|
-
visitor: NestedVisitObject<
|
|
412
|
+
visitor: NestedVisitObject<unknown, T>,
|
|
415
413
|
parentContext: VisitorLevelContext | null,
|
|
416
414
|
depth = 0
|
|
417
415
|
) {
|
|
@@ -434,14 +432,14 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
434
432
|
findLegacyVisitorNode(
|
|
435
433
|
visitor,
|
|
436
434
|
legacyTypesMap[typeName as keyof typeof legacyTypesMap] as keyof T
|
|
437
|
-
)) as
|
|
435
|
+
)) as NestedVisitObject<unknown, T>;
|
|
438
436
|
const normalizedTypeVisitor = normalizedVisitors[typeName];
|
|
439
437
|
|
|
440
438
|
if (!typeVisitor) continue;
|
|
441
439
|
|
|
442
|
-
let visitorEnter: VisitFunction<
|
|
443
|
-
let visitorLeave: VisitFunction<
|
|
444
|
-
let visitorSkip: SkipFunction<
|
|
440
|
+
let visitorEnter: VisitFunction<unknown> | undefined;
|
|
441
|
+
let visitorLeave: VisitFunction<unknown> | undefined;
|
|
442
|
+
let visitorSkip: SkipFunction<unknown> | undefined;
|
|
445
443
|
|
|
446
444
|
const isObjectVisitor = typeof typeVisitor === 'object';
|
|
447
445
|
|
|
@@ -450,7 +448,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
450
448
|
}
|
|
451
449
|
|
|
452
450
|
if (typeof typeVisitor === 'function') {
|
|
453
|
-
visitorEnter = typeVisitor
|
|
451
|
+
visitorEnter = typeVisitor;
|
|
454
452
|
} else if (isObjectVisitor) {
|
|
455
453
|
visitorEnter = typeVisitor.enter;
|
|
456
454
|
visitorLeave = typeVisitor.leave;
|
|
@@ -465,7 +463,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
|
|
|
465
463
|
};
|
|
466
464
|
|
|
467
465
|
if (typeof typeVisitor === 'object') {
|
|
468
|
-
normalizeVisitorLevel(ruleConf, typeVisitor
|
|
466
|
+
normalizeVisitorLevel(ruleConf, typeVisitor, context, depth + 1);
|
|
469
467
|
}
|
|
470
468
|
|
|
471
469
|
if (parentContext) {
|