@redocly/openapi-core 1.8.2 → 1.9.1
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/config/all.js +1 -0
- package/lib/config/minimal.js +1 -0
- package/lib/config/recommended-strict.js +1 -0
- package/lib/config/recommended.js +1 -0
- package/lib/lint.d.ts +1 -0
- package/lib/lint.js +1 -1
- package/lib/oas-types.d.ts +1 -1
- package/lib/ref-utils.js +4 -4
- package/lib/resolve.js +9 -1
- package/lib/rules/common/no-required-schema-properties-undefined.d.ts +2 -0
- package/lib/rules/common/no-required-schema-properties-undefined.js +37 -0
- package/lib/rules/oas2/index.js +2 -0
- package/lib/rules/oas3/index.js +2 -0
- package/lib/types/index.d.ts +7 -7
- package/lib/types/json-schema-adapter.d.ts +3 -0
- package/lib/types/json-schema-adapter.js +173 -0
- package/lib/types/oas2.d.ts +3 -2
- package/lib/types/oas3.d.ts +3 -2
- package/lib/types/oas3_1.d.ts +3 -2
- package/lib/types/portal-config-schema.d.ts +5261 -52
- package/lib/types/portal-config-schema.js +71 -55
- package/lib/types/redocly-yaml.d.ts +14 -2
- package/lib/types/redocly-yaml.js +102 -39
- package/lib/types/theme-config.d.ts +819 -36
- package/lib/types/theme-config.js +67 -29
- package/lib/utils.d.ts +2 -2
- package/lib/visitors.js +1 -1
- package/lib/walk.js +7 -1
- package/package.json +1 -1
- package/src/__tests__/lint.test.ts +1218 -36
- package/src/__tests__/ref-utils.test.ts +22 -0
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +2 -0
- package/src/config/__tests__/load.test.ts +13 -13
- package/src/config/all.ts +1 -0
- package/src/config/minimal.ts +1 -0
- package/src/config/recommended-strict.ts +1 -0
- package/src/config/recommended.ts +1 -0
- package/src/decorators/oas2/remove-unused-components.ts +3 -2
- package/src/decorators/oas3/remove-unused-components.ts +3 -2
- package/src/lint.ts +2 -1
- package/src/ref-utils.ts +4 -4
- package/src/resolve.ts +13 -1
- package/src/rules/common/__tests__/no-required-schema-properties-undefined.test.ts +550 -0
- package/src/rules/common/no-required-schema-properties-undefined.ts +53 -0
- package/src/rules/oas2/index.ts +2 -0
- package/src/rules/oas3/index.ts +2 -0
- package/src/types/index.ts +7 -12
- package/src/types/json-schema-adapter.ts +217 -0
- package/src/types/oas2.ts +5 -2
- package/src/types/oas3.ts +6 -2
- package/src/types/oas3_1.ts +5 -2
- package/src/types/portal-config-schema.ts +111 -61
- package/src/types/redocly-yaml.ts +119 -43
- package/src/types/theme-config.ts +125 -27
- package/src/utils.ts +2 -2
- package/src/visitors.ts +1 -1
- package/src/walk.ts +7 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -96,6 +96,28 @@ describe('ref-utils', () => {
|
|
|
96
96
|
expect(result).toMatchInlineSnapshot(`[]`);
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('should parse a ref correctly', () => {
|
|
100
|
+
expect(parseRef('./info.yaml#/description')).toEqual({
|
|
101
|
+
uri: './info.yaml',
|
|
102
|
+
pointer: ['description'],
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should parse a ref which contain a hash in the middle', () => {
|
|
107
|
+
// Here `info#description.md` is a file name
|
|
108
|
+
expect(parseRef('./info#description.md')).toEqual({
|
|
109
|
+
uri: './info#description.md',
|
|
110
|
+
pointer: [],
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should parse a ref which ends with a hash', () => {
|
|
115
|
+
expect(parseRef('./info.yaml#')).toEqual({
|
|
116
|
+
uri: './info.yaml',
|
|
117
|
+
pointer: [],
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
99
121
|
describe('refBaseName', () => {
|
|
100
122
|
it('returns base name for file reference', () => {
|
|
101
123
|
expect(refBaseName('../testcase/Pet.yaml')).toStrictEqual('Pet');
|
|
@@ -74,6 +74,7 @@ exports[`resolveConfig should ignore minimal from the root and read local file 1
|
|
|
74
74
|
"no-invalid-parameter-examples": "off",
|
|
75
75
|
"no-invalid-schema-examples": "off",
|
|
76
76
|
"no-path-trailing-slash": "error",
|
|
77
|
+
"no-required-schema-properties-undefined": "off",
|
|
77
78
|
"no-unresolved-refs": "error",
|
|
78
79
|
"operation-2xx-response": "warn",
|
|
79
80
|
"operation-4xx-response": "error",
|
|
@@ -198,6 +199,7 @@ exports[`resolveStyleguideConfig should resolve extends with local file config w
|
|
|
198
199
|
"no-invalid-parameter-examples": "off",
|
|
199
200
|
"no-invalid-schema-examples": "off",
|
|
200
201
|
"no-path-trailing-slash": "error",
|
|
202
|
+
"no-required-schema-properties-undefined": "off",
|
|
201
203
|
"no-unresolved-refs": "error",
|
|
202
204
|
"operation-2xx-response": "error",
|
|
203
205
|
"operation-4xx-response": "off",
|
|
@@ -137,6 +137,19 @@ describe('getConfig', () => {
|
|
|
137
137
|
"severity": "warn",
|
|
138
138
|
"suggest": [],
|
|
139
139
|
},
|
|
140
|
+
{
|
|
141
|
+
"location": [
|
|
142
|
+
{
|
|
143
|
+
"pointer": "#/theme",
|
|
144
|
+
"reportOnKey": false,
|
|
145
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
|
|
149
|
+
"ruleId": "configuration no-unresolved-refs",
|
|
150
|
+
"severity": "warn",
|
|
151
|
+
"suggest": [],
|
|
152
|
+
},
|
|
140
153
|
{
|
|
141
154
|
"from": {
|
|
142
155
|
"pointer": "#/rules",
|
|
@@ -154,19 +167,6 @@ describe('getConfig', () => {
|
|
|
154
167
|
"severity": "warn",
|
|
155
168
|
"suggest": [],
|
|
156
169
|
},
|
|
157
|
-
{
|
|
158
|
-
"location": [
|
|
159
|
-
{
|
|
160
|
-
"pointer": "#/theme",
|
|
161
|
-
"reportOnKey": false,
|
|
162
|
-
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
|
|
166
|
-
"ruleId": "configuration no-unresolved-refs",
|
|
167
|
-
"severity": "warn",
|
|
168
|
-
"suggest": [],
|
|
169
|
-
},
|
|
170
170
|
]
|
|
171
171
|
`);
|
|
172
172
|
});
|
package/src/config/all.ts
CHANGED
|
@@ -43,6 +43,7 @@ const all: PluginStyleguideConfig<'built-in'> = {
|
|
|
43
43
|
'path-params-defined': 'error',
|
|
44
44
|
'required-string-property-missing-min-length': 'error',
|
|
45
45
|
'response-contains-header': 'error',
|
|
46
|
+
'no-required-schema-properties-undefined': 'error',
|
|
46
47
|
},
|
|
47
48
|
oas2Rules: {
|
|
48
49
|
'boolean-parameter-prefixes': 'error',
|
package/src/config/minimal.ts
CHANGED
|
@@ -40,6 +40,7 @@ const minimal: PluginStyleguideConfig<'built-in'> = {
|
|
|
40
40
|
'response-contains-header': 'off',
|
|
41
41
|
'path-segment-plural': 'off',
|
|
42
42
|
'scalar-property-missing-example': 'off',
|
|
43
|
+
'no-required-schema-properties-undefined': 'off',
|
|
43
44
|
},
|
|
44
45
|
oas2Rules: {
|
|
45
46
|
'boolean-parameter-prefixes': 'off',
|
|
@@ -40,6 +40,7 @@ const recommendedStrict: PluginStyleguideConfig<'built-in'> = {
|
|
|
40
40
|
'required-string-property-missing-min-length': 'off',
|
|
41
41
|
'response-contains-header': 'off',
|
|
42
42
|
'scalar-property-missing-example': 'off',
|
|
43
|
+
'no-required-schema-properties-undefined': 'off',
|
|
43
44
|
},
|
|
44
45
|
oas2Rules: {
|
|
45
46
|
'boolean-parameter-prefixes': 'off',
|
|
@@ -40,6 +40,7 @@ const recommended: PluginStyleguideConfig<'built-in'> = {
|
|
|
40
40
|
'required-string-property-missing-min-length': 'off',
|
|
41
41
|
'response-contains-header': 'off',
|
|
42
42
|
'scalar-property-missing-example': 'off',
|
|
43
|
+
'no-required-schema-properties-undefined': 'off',
|
|
43
44
|
},
|
|
44
45
|
oas2Rules: {
|
|
45
46
|
'boolean-parameter-prefixes': 'off',
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Oas2Decorator } from '../../visitors';
|
|
2
1
|
import { Location } from '../../ref-utils';
|
|
3
|
-
import type { Oas2Components } from '../../typings/swagger';
|
|
4
2
|
import { isEmptyObject } from '../../utils';
|
|
5
3
|
|
|
4
|
+
import type { Oas2Decorator } from '../../visitors';
|
|
5
|
+
import type { Oas2Components } from '../../typings/swagger';
|
|
6
|
+
|
|
6
7
|
export const RemoveUnusedComponents: Oas2Decorator = () => {
|
|
7
8
|
const components = new Map<
|
|
8
9
|
string,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Oas3Decorator } from '../../visitors';
|
|
2
1
|
import { Location } from '../../ref-utils';
|
|
3
|
-
import type { Oas3Components } from '../../typings/openapi';
|
|
4
2
|
import { isEmptyObject } from '../../utils';
|
|
5
3
|
|
|
4
|
+
import type { Oas3Decorator } from '../../visitors';
|
|
5
|
+
import type { Oas3Components } from '../../typings/openapi';
|
|
6
|
+
|
|
6
7
|
export const RemoveUnusedComponents: Oas3Decorator = () => {
|
|
7
8
|
const components = new Map<
|
|
8
9
|
string,
|
package/src/lint.ts
CHANGED
|
@@ -112,6 +112,7 @@ export async function lintConfig(opts: {
|
|
|
112
112
|
resolvedRefMap?: ResolvedRefMap;
|
|
113
113
|
severity?: ProblemSeverity;
|
|
114
114
|
externalRefResolver?: BaseResolver;
|
|
115
|
+
externalConfigTypes?: Record<string, NodeType>;
|
|
115
116
|
}) {
|
|
116
117
|
const { document, severity, externalRefResolver = new BaseResolver() } = opts;
|
|
117
118
|
|
|
@@ -126,7 +127,7 @@ export async function lintConfig(opts: {
|
|
|
126
127
|
rules: { spec: 'error' },
|
|
127
128
|
});
|
|
128
129
|
|
|
129
|
-
const types = normalizeTypes(ConfigTypes, config);
|
|
130
|
+
const types = normalizeTypes(opts.externalConfigTypes || ConfigTypes, config);
|
|
130
131
|
const rules: (RuleInstanceConfig & {
|
|
131
132
|
visitor: NestedVisitObject<unknown, Oas3Visitor | Oas3Visitor[]>;
|
|
132
133
|
})[] = [
|
package/src/ref-utils.ts
CHANGED
|
@@ -43,15 +43,15 @@ export function escapePointer<T extends string | number>(fragment: T): T {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function parseRef(ref: string): { uri: string | null; pointer: string[] } {
|
|
46
|
-
const [uri, pointer] = ref.split('#/');
|
|
46
|
+
const [uri, pointer = ''] = ref.split('#/');
|
|
47
47
|
return {
|
|
48
|
-
uri: uri || null,
|
|
49
|
-
pointer: pointer
|
|
48
|
+
uri: (uri.endsWith('#') ? uri.slice(0, -1) : uri) || null,
|
|
49
|
+
pointer: parsePointer(pointer),
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function parsePointer(pointer: string) {
|
|
54
|
-
return pointer.
|
|
54
|
+
return pointer.split('/').map(unescapePointer).filter(isTruthy);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function pointerBaseName(pointer: string) {
|
package/src/resolve.ts
CHANGED
|
@@ -269,8 +269,20 @@ export async function resolveDocument(opts: {
|
|
|
269
269
|
if (itemsType === undefined && type !== unknownType && type !== SpecExtension) {
|
|
270
270
|
return;
|
|
271
271
|
}
|
|
272
|
+
const isTypeAFunction = typeof itemsType === 'function';
|
|
272
273
|
for (let i = 0; i < node.length; i++) {
|
|
273
|
-
|
|
274
|
+
const itemType = isTypeAFunction
|
|
275
|
+
? itemsType(node[i], joinPointer(nodeAbsoluteRef, i))
|
|
276
|
+
: itemsType;
|
|
277
|
+
// we continue resolving unknown types, but stop early on known scalars
|
|
278
|
+
if (itemType === undefined && type !== unknownType && type !== SpecExtension) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
walk(
|
|
282
|
+
node[i],
|
|
283
|
+
isNamedType(itemType) ? itemType : unknownType,
|
|
284
|
+
joinPointer(nodeAbsoluteRef, i)
|
|
285
|
+
);
|
|
274
286
|
}
|
|
275
287
|
return;
|
|
276
288
|
}
|