@redocly/openapi-core 1.0.0-beta.119 → 1.0.0-beta.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config/config.d.ts +2 -3
- package/lib/config/config.js +1 -2
- package/lib/config/types.d.ts +13 -6
- package/lib/config/utils.d.ts +1 -0
- package/lib/config/utils.js +21 -12
- package/lib/decorators/common/info-override.d.ts +2 -0
- package/lib/decorators/common/info-override.js +28 -0
- package/lib/decorators/oas2/index.d.ts +1 -0
- package/lib/decorators/oas2/index.js +2 -0
- package/lib/decorators/oas3/index.d.ts +1 -0
- package/lib/decorators/oas3/index.js +2 -0
- package/lib/resolve.js +3 -1
- package/lib/rules/common/operation-2xx-response.js +15 -9
- package/lib/rules/common/operation-4xx-response.js +15 -9
- package/lib/rules/utils.d.ts +1 -0
- package/lib/rules/utils.js +14 -2
- package/lib/types/index.js +1 -1
- package/lib/types/redocly-yaml.js +9 -2
- package/lib/utils.d.ts +2 -2
- package/lib/utils.js +9 -4
- package/package.json +1 -1
- package/src/__tests__/lint.test.ts +19 -73
- package/src/config/__tests__/config.test.ts +8 -10
- package/src/config/__tests__/utils.test.ts +67 -6
- package/src/config/config.ts +3 -4
- package/src/config/types.ts +15 -6
- package/src/config/utils.ts +32 -18
- package/src/decorators/common/info-override.ts +15 -0
- package/src/decorators/oas2/index.ts +2 -0
- package/src/decorators/oas3/index.ts +2 -0
- package/src/resolve.ts +5 -1
- package/src/rules/__tests__/fixtures/code-sample.php +9 -0
- package/src/rules/__tests__/no-unresolved-refs.test.ts +89 -0
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +64 -0
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +64 -0
- package/src/rules/common/operation-2xx-response.ts +17 -9
- package/src/rules/common/operation-4xx-response.ts +16 -9
- package/src/rules/utils.ts +26 -1
- package/src/types/index.ts +1 -1
- package/src/types/redocly-yaml.ts +14 -4
- package/src/utils.ts +18 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -44,8 +44,10 @@ const testConfig: Config = {
|
|
|
44
44
|
preprocessors: { oas2: {}, oas3_0: {}, oas3_1: {} },
|
|
45
45
|
decorators: { oas2: {}, oas3_0: {}, oas3_1: {} },
|
|
46
46
|
} as unknown as StyleguideConfig,
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
theme: {
|
|
48
|
+
openapi: {},
|
|
49
|
+
mockServer: {},
|
|
50
|
+
},
|
|
49
51
|
resolve: { http: { headers: [] } },
|
|
50
52
|
organization: 'redocly-test',
|
|
51
53
|
files: [],
|
|
@@ -66,8 +68,6 @@ describe('getMergedConfig', () => {
|
|
|
66
68
|
},
|
|
67
69
|
},
|
|
68
70
|
"configFile": "redocly.yaml",
|
|
69
|
-
"features.mockServer": Object {},
|
|
70
|
-
"features.openapi": Object {},
|
|
71
71
|
"files": Array [],
|
|
72
72
|
"organization": "redocly-test",
|
|
73
73
|
"rawConfig": Object {
|
|
@@ -81,8 +81,6 @@ describe('getMergedConfig', () => {
|
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
|
-
"features.mockServer": Object {},
|
|
85
|
-
"features.openapi": Object {},
|
|
86
84
|
"files": Array [],
|
|
87
85
|
"organization": "redocly-test",
|
|
88
86
|
"styleguide": Object {
|
|
@@ -92,6 +90,7 @@ describe('getMergedConfig', () => {
|
|
|
92
90
|
"operation-summary": "warn",
|
|
93
91
|
},
|
|
94
92
|
},
|
|
93
|
+
"theme": Object {},
|
|
95
94
|
},
|
|
96
95
|
"region": undefined,
|
|
97
96
|
"resolve": Object {
|
|
@@ -139,6 +138,7 @@ describe('getMergedConfig', () => {
|
|
|
139
138
|
},
|
|
140
139
|
},
|
|
141
140
|
},
|
|
141
|
+
"theme": Object {},
|
|
142
142
|
}
|
|
143
143
|
`);
|
|
144
144
|
});
|
|
@@ -164,8 +164,6 @@ describe('getMergedConfig', () => {
|
|
|
164
164
|
},
|
|
165
165
|
},
|
|
166
166
|
"configFile": "redocly.yaml",
|
|
167
|
-
"features.mockServer": Object {},
|
|
168
|
-
"features.openapi": Object {},
|
|
169
167
|
"files": Array [],
|
|
170
168
|
"organization": "redocly-test",
|
|
171
169
|
"rawConfig": Object {
|
|
@@ -179,8 +177,6 @@ describe('getMergedConfig', () => {
|
|
|
179
177
|
},
|
|
180
178
|
},
|
|
181
179
|
},
|
|
182
|
-
"features.mockServer": Object {},
|
|
183
|
-
"features.openapi": Object {},
|
|
184
180
|
"files": Array [],
|
|
185
181
|
"organization": "redocly-test",
|
|
186
182
|
"styleguide": Object {
|
|
@@ -192,6 +188,7 @@ describe('getMergedConfig', () => {
|
|
|
192
188
|
"operation-summary": "error",
|
|
193
189
|
},
|
|
194
190
|
},
|
|
191
|
+
"theme": Object {},
|
|
195
192
|
},
|
|
196
193
|
"region": undefined,
|
|
197
194
|
"resolve": Object {
|
|
@@ -244,6 +241,7 @@ describe('getMergedConfig', () => {
|
|
|
244
241
|
},
|
|
245
242
|
},
|
|
246
243
|
},
|
|
244
|
+
"theme": Object {},
|
|
247
245
|
}
|
|
248
246
|
`);
|
|
249
247
|
});
|
|
@@ -41,8 +41,10 @@ const rawTestConfig: RawConfig = {
|
|
|
41
41
|
resolve: {
|
|
42
42
|
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
|
|
43
43
|
},
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
theme: {
|
|
45
|
+
openapi: {
|
|
46
|
+
disableSidebar: true,
|
|
47
|
+
},
|
|
46
48
|
},
|
|
47
49
|
};
|
|
48
50
|
|
|
@@ -63,8 +65,10 @@ const flatTestConfig: FlatRawConfig = {
|
|
|
63
65
|
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
|
|
64
66
|
doNotResolveExamples: true,
|
|
65
67
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
theme: {
|
|
69
|
+
openapi: {
|
|
70
|
+
disableSidebar: true,
|
|
71
|
+
},
|
|
68
72
|
},
|
|
69
73
|
};
|
|
70
74
|
|
|
@@ -85,12 +89,15 @@ describe('transformConfig', () => {
|
|
|
85
89
|
},
|
|
86
90
|
},
|
|
87
91
|
},
|
|
88
|
-
"features.openapi": undefined,
|
|
89
92
|
"styleguide": Object {
|
|
90
93
|
"rules": Object {
|
|
91
94
|
"operation-4xx-response": "warn",
|
|
92
95
|
},
|
|
93
96
|
},
|
|
97
|
+
"theme": Object {
|
|
98
|
+
"mockServer": Object {},
|
|
99
|
+
"openapi": Object {},
|
|
100
|
+
},
|
|
94
101
|
}
|
|
95
102
|
`);
|
|
96
103
|
});
|
|
@@ -110,12 +117,15 @@ describe('transformConfig', () => {
|
|
|
110
117
|
"root": "file.yaml",
|
|
111
118
|
},
|
|
112
119
|
},
|
|
113
|
-
"features.openapi": undefined,
|
|
114
120
|
"styleguide": Object {
|
|
115
121
|
"rules": Object {
|
|
116
122
|
"operation-4xx-response": "warn",
|
|
117
123
|
},
|
|
118
124
|
},
|
|
125
|
+
"theme": Object {
|
|
126
|
+
"mockServer": Object {},
|
|
127
|
+
"openapi": Object {},
|
|
128
|
+
},
|
|
119
129
|
}
|
|
120
130
|
`);
|
|
121
131
|
});
|
|
@@ -140,4 +150,55 @@ describe('transformConfig', () => {
|
|
|
140
150
|
`Do not use 'lint', 'styleguide' and flat syntax together. \nSee more about the configuration in the docs: https://redocly.com/docs/cli/configuration/ \n`
|
|
141
151
|
);
|
|
142
152
|
});
|
|
153
|
+
it('should throw an error if both `features.openapi` and `theme` syntaxes used together', () => {
|
|
154
|
+
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
|
|
155
|
+
apis: {
|
|
156
|
+
'test@v1': {
|
|
157
|
+
root: 'root.yaml',
|
|
158
|
+
styleguide: {
|
|
159
|
+
extends: ['recommended'],
|
|
160
|
+
rules: { 'operation-2xx-response': 'error' },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
'features.openapi': {
|
|
165
|
+
disableSidebar: true,
|
|
166
|
+
},
|
|
167
|
+
theme: {
|
|
168
|
+
openapi: {
|
|
169
|
+
disableSidebar: true,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
expect(() => utils.transformConfig(testRawConfig)).toThrowError(
|
|
174
|
+
`Do not use 'features.openapi' field. Use 'theme.openapi' instead. `
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
it('should transform referenceDocs config into theme.openapi', () => {
|
|
178
|
+
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
|
|
179
|
+
referenceDocs: {
|
|
180
|
+
disableSidebar: true,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
expect(utils.transformConfig(testRawConfig)).toEqual({
|
|
184
|
+
apis: undefined,
|
|
185
|
+
styleguide: undefined,
|
|
186
|
+
theme: { mockServer: {}, openapi: { disableSidebar: true } },
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
it('should transform "features.openapi" config into theme.openapi', () => {
|
|
190
|
+
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
|
|
191
|
+
'features.openapi': {
|
|
192
|
+
disableSidebar: true,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
expect(utils.transformConfig(testRawConfig)).toEqual({
|
|
196
|
+
apis: undefined,
|
|
197
|
+
'features.openapi': {
|
|
198
|
+
disableSidebar: true,
|
|
199
|
+
},
|
|
200
|
+
styleguide: undefined,
|
|
201
|
+
theme: { mockServer: {}, openapi: { disableSidebar: true } },
|
|
202
|
+
});
|
|
203
|
+
});
|
|
143
204
|
});
|
package/src/config/config.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
ResolvedStyleguideConfig,
|
|
19
19
|
RuleConfig,
|
|
20
20
|
RuleSettings,
|
|
21
|
+
ThemeRawConfig,
|
|
21
22
|
} from './types';
|
|
22
23
|
import { getResolveConfig } from './utils';
|
|
23
24
|
|
|
@@ -304,15 +305,13 @@ export class Config {
|
|
|
304
305
|
resolve: ResolveConfig;
|
|
305
306
|
licenseKey?: string;
|
|
306
307
|
region?: Region;
|
|
307
|
-
|
|
308
|
-
'features.mockServer'?: Record<string, any>;
|
|
308
|
+
theme: ThemeRawConfig;
|
|
309
309
|
organization?: string;
|
|
310
310
|
files: string[];
|
|
311
311
|
constructor(public rawConfig: ResolvedConfig, public configFile?: string) {
|
|
312
312
|
this.apis = rawConfig.apis || {};
|
|
313
313
|
this.styleguide = new StyleguideConfig(rawConfig.styleguide || {}, configFile);
|
|
314
|
-
this
|
|
315
|
-
this['features.mockServer'] = rawConfig['features.mockServer'] || {};
|
|
314
|
+
this.theme = rawConfig.theme || {};
|
|
316
315
|
this.resolve = getResolveConfig(rawConfig?.resolve);
|
|
317
316
|
this.region = rawConfig.region;
|
|
318
317
|
this.organization = rawConfig.organization;
|
package/src/config/types.ts
CHANGED
|
@@ -145,16 +145,16 @@ export type DeprecatedInRawConfig = {
|
|
|
145
145
|
styleguide?: StyleguideRawConfig;
|
|
146
146
|
referenceDocs?: Record<string, any>;
|
|
147
147
|
apis?: Record<string, Api & DeprecatedInApi>;
|
|
148
|
-
};
|
|
148
|
+
} & DeprecatedFeaturesConfig;
|
|
149
149
|
|
|
150
150
|
export type Api = {
|
|
151
151
|
root: string;
|
|
152
152
|
styleguide?: ApiStyleguideRawConfig;
|
|
153
|
-
} &
|
|
153
|
+
} & ThemeConfig;
|
|
154
154
|
|
|
155
155
|
export type DeprecatedInApi = {
|
|
156
156
|
lint?: ApiStyleguideRawConfig;
|
|
157
|
-
};
|
|
157
|
+
} & DeprecatedFeaturesConfig;
|
|
158
158
|
|
|
159
159
|
export type ResolvedApi = Omit<Api, 'styleguide'> & {
|
|
160
160
|
styleguide: ResolvedStyleguideConfig;
|
|
@@ -168,7 +168,7 @@ export type RawConfig = {
|
|
|
168
168
|
region?: Region;
|
|
169
169
|
organization?: string;
|
|
170
170
|
files?: string[];
|
|
171
|
-
} &
|
|
171
|
+
} & ThemeConfig;
|
|
172
172
|
|
|
173
173
|
export type FlatApi = Omit<Api, 'styleguide'> &
|
|
174
174
|
Omit<ApiStyleguideRawConfig, 'doNotResolveExamples'>;
|
|
@@ -177,18 +177,27 @@ export type FlatRawConfig = Omit<RawConfig, 'styleguide' | 'resolve' | 'apis'> &
|
|
|
177
177
|
Omit<StyleguideRawConfig, 'doNotResolveExamples'> & {
|
|
178
178
|
resolve?: RawResolveConfig;
|
|
179
179
|
apis?: Record<string, FlatApi>;
|
|
180
|
-
};
|
|
180
|
+
} & ThemeRawConfig;
|
|
181
181
|
|
|
182
182
|
export type ResolvedConfig = Omit<RawConfig, 'apis' | 'styleguide'> & {
|
|
183
183
|
apis: Record<string, ResolvedApi>;
|
|
184
184
|
styleguide: ResolvedStyleguideConfig;
|
|
185
185
|
};
|
|
186
186
|
|
|
187
|
-
type
|
|
187
|
+
type DeprecatedFeaturesConfig = {
|
|
188
188
|
'features.openapi'?: Record<string, any>;
|
|
189
189
|
'features.mockServer'?: Record<string, any>;
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
+
export type ThemeConfig = {
|
|
193
|
+
theme?: ThemeRawConfig;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type ThemeRawConfig = {
|
|
197
|
+
openapi?: Record<string, any>;
|
|
198
|
+
mockServer?: Record<string, any>;
|
|
199
|
+
};
|
|
200
|
+
|
|
192
201
|
export type RulesFields =
|
|
193
202
|
| 'rules'
|
|
194
203
|
| 'oas2Rules'
|
package/src/config/utils.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ResolvedStyleguideConfig,
|
|
20
20
|
RulesFields,
|
|
21
21
|
StyleguideRawConfig,
|
|
22
|
+
ThemeConfig,
|
|
22
23
|
} from './types';
|
|
23
24
|
import { logger, colorize } from '../logger';
|
|
24
25
|
|
|
@@ -225,13 +226,9 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
|
|
|
225
226
|
extendPaths,
|
|
226
227
|
pluginPaths,
|
|
227
228
|
},
|
|
228
|
-
|
|
229
|
-
...config
|
|
230
|
-
...config.apis[apiName]?.
|
|
231
|
-
},
|
|
232
|
-
'features.mockServer': {
|
|
233
|
-
...config['features.mockServer'],
|
|
234
|
-
...config.apis[apiName]?.['features.mockServer'],
|
|
229
|
+
theme: {
|
|
230
|
+
...config.rawConfig.theme,
|
|
231
|
+
...config.apis[apiName]?.theme,
|
|
235
232
|
},
|
|
236
233
|
files: [...config.files, ...(config.apis?.[apiName]?.files ?? [])],
|
|
237
234
|
// TODO: merge everything else here
|
|
@@ -241,10 +238,11 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
|
|
|
241
238
|
: config;
|
|
242
239
|
}
|
|
243
240
|
|
|
244
|
-
function checkForDeprecatedFields(
|
|
241
|
+
export function checkForDeprecatedFields(
|
|
245
242
|
deprecatedField: keyof (DeprecatedInRawConfig & RawConfig),
|
|
246
243
|
updatedField: keyof FlatRawConfig | undefined,
|
|
247
|
-
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig
|
|
244
|
+
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig,
|
|
245
|
+
updatedObject: keyof FlatRawConfig | undefined
|
|
248
246
|
): void {
|
|
249
247
|
const isDeprecatedFieldInApis =
|
|
250
248
|
rawConfig.apis &&
|
|
@@ -261,8 +259,12 @@ function checkForDeprecatedFields(
|
|
|
261
259
|
showErrorForDeprecatedField(deprecatedField, updatedField);
|
|
262
260
|
}
|
|
263
261
|
|
|
262
|
+
if (rawConfig[deprecatedField] && updatedObject && rawConfig[updatedObject]) {
|
|
263
|
+
showErrorForDeprecatedField(deprecatedField, updatedField, updatedObject);
|
|
264
|
+
}
|
|
265
|
+
|
|
264
266
|
if (rawConfig[deprecatedField] || isDeprecatedFieldInApis) {
|
|
265
|
-
showWarningForDeprecatedField(deprecatedField, updatedField);
|
|
267
|
+
showWarningForDeprecatedField(deprecatedField, updatedField, updatedObject);
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
|
|
@@ -271,16 +273,18 @@ export function transformConfig(
|
|
|
271
273
|
): RawConfig {
|
|
272
274
|
const migratedFields: [
|
|
273
275
|
keyof (DeprecatedInRawConfig & RawConfig),
|
|
274
|
-
keyof FlatRawConfig | undefined
|
|
276
|
+
keyof FlatRawConfig | undefined,
|
|
277
|
+
keyof ThemeConfig | undefined
|
|
275
278
|
][] = [
|
|
276
|
-
['apiDefinitions', 'apis'],
|
|
277
|
-
['referenceDocs', '
|
|
278
|
-
['lint', undefined],
|
|
279
|
-
['styleguide', undefined],
|
|
279
|
+
['apiDefinitions', 'apis', undefined],
|
|
280
|
+
['referenceDocs', 'openapi', 'theme'],
|
|
281
|
+
['lint', undefined, undefined],
|
|
282
|
+
['styleguide', undefined, undefined],
|
|
283
|
+
['features.openapi', 'openapi', 'theme'],
|
|
280
284
|
];
|
|
281
285
|
|
|
282
|
-
for (const [deprecatedField, updatedField] of migratedFields) {
|
|
283
|
-
checkForDeprecatedFields(deprecatedField, updatedField, rawConfig);
|
|
286
|
+
for (const [deprecatedField, updatedField, updatedObject] of migratedFields) {
|
|
287
|
+
checkForDeprecatedFields(deprecatedField, updatedField, rawConfig, updatedObject);
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
const { apis, apiDefinitions, referenceDocs, lint, ...rest } = rawConfig;
|
|
@@ -288,7 +292,17 @@ export function transformConfig(
|
|
|
288
292
|
const { styleguideConfig, rawConfigRest } = extractFlatConfig(rest);
|
|
289
293
|
|
|
290
294
|
return {
|
|
291
|
-
|
|
295
|
+
theme: {
|
|
296
|
+
openapi: {
|
|
297
|
+
...referenceDocs,
|
|
298
|
+
...rawConfig['features.openapi'],
|
|
299
|
+
...rawConfig.theme?.openapi,
|
|
300
|
+
},
|
|
301
|
+
mockServer: {
|
|
302
|
+
...rawConfig['features.mockServer'],
|
|
303
|
+
...rawConfig.theme?.mockServer,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
292
306
|
apis: transformApis(apis) || transformApiDefinitionsToApis(apiDefinitions),
|
|
293
307
|
styleguide: styleguideConfig || lint,
|
|
294
308
|
...rawConfigRest,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Oas3Decorator, Oas2Decorator } from '../../visitors';
|
|
2
|
+
|
|
3
|
+
export const InfoOverride: Oas3Decorator | Oas2Decorator = (newInfo) => {
|
|
4
|
+
return {
|
|
5
|
+
Info: {
|
|
6
|
+
leave(info) {
|
|
7
|
+
if (typeof newInfo !== 'object' || Array.isArray(newInfo) || newInfo === null) {
|
|
8
|
+
throw new Error(`"info-override" decorator should be called with an object`);
|
|
9
|
+
}
|
|
10
|
+
const { severity: _, ...rest } = newInfo;
|
|
11
|
+
Object.assign(info, rest);
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -3,6 +3,7 @@ import { RegistryDependencies } from '../common/registry-dependencies';
|
|
|
3
3
|
import { OperationDescriptionOverride } from '../common/operation-description-override';
|
|
4
4
|
import { TagDescriptionOverride } from '../common/tag-description-override';
|
|
5
5
|
import { InfoDescriptionOverride } from '../common/info-description-override';
|
|
6
|
+
import { InfoOverride } from '../common/info-override';
|
|
6
7
|
import { RemoveXInternal } from '../common/remove-x-internal';
|
|
7
8
|
import { FilterIn } from '../common/filters/filter-in';
|
|
8
9
|
import { FilterOut } from '../common/filters/filter-out';
|
|
@@ -12,6 +13,7 @@ export const decorators = {
|
|
|
12
13
|
'operation-description-override': OperationDescriptionOverride as Oas2Decorator,
|
|
13
14
|
'tag-description-override': TagDescriptionOverride as Oas2Decorator,
|
|
14
15
|
'info-description-override': InfoDescriptionOverride as Oas2Decorator,
|
|
16
|
+
'info-override': InfoOverride as Oas2Decorator,
|
|
15
17
|
'remove-x-internal': RemoveXInternal as Oas2Decorator,
|
|
16
18
|
'filter-in': FilterIn as Oas2Decorator,
|
|
17
19
|
'filter-out': FilterOut as Oas2Decorator,
|
|
@@ -3,6 +3,7 @@ import { RegistryDependencies } from '../common/registry-dependencies';
|
|
|
3
3
|
import { OperationDescriptionOverride } from '../common/operation-description-override';
|
|
4
4
|
import { TagDescriptionOverride } from '../common/tag-description-override';
|
|
5
5
|
import { InfoDescriptionOverride } from '../common/info-description-override';
|
|
6
|
+
import { InfoOverride } from '../common/info-override';
|
|
6
7
|
import { RemoveXInternal } from '../common/remove-x-internal';
|
|
7
8
|
import { FilterIn } from '../common/filters/filter-in';
|
|
8
9
|
import { FilterOut } from '../common/filters/filter-out';
|
|
@@ -13,6 +14,7 @@ export const decorators = {
|
|
|
13
14
|
'operation-description-override': OperationDescriptionOverride as Oas3Decorator,
|
|
14
15
|
'tag-description-override': TagDescriptionOverride as Oas3Decorator,
|
|
15
16
|
'info-description-override': InfoDescriptionOverride as Oas3Decorator,
|
|
17
|
+
'info-override': InfoOverride as Oas3Decorator,
|
|
16
18
|
'remove-x-internal': RemoveXInternal as Oas3Decorator,
|
|
17
19
|
'filter-in': FilterIn as Oas3Decorator,
|
|
18
20
|
'filter-out': FilterOut as Oas3Decorator,
|
package/src/resolve.ts
CHANGED
|
@@ -276,7 +276,11 @@ export async function resolveDocument(opts: {
|
|
|
276
276
|
if (propType === undefined) propType = type.additionalProperties;
|
|
277
277
|
if (typeof propType === 'function') propType = propType(propValue, propName);
|
|
278
278
|
if (propType === undefined) propType = unknownType;
|
|
279
|
-
if (
|
|
279
|
+
if (
|
|
280
|
+
type.extensionsPrefix &&
|
|
281
|
+
propName.startsWith(type.extensionsPrefix) &&
|
|
282
|
+
propType === unknownType
|
|
283
|
+
) {
|
|
280
284
|
propType = SpecExtension;
|
|
281
285
|
}
|
|
282
286
|
|
|
@@ -165,4 +165,93 @@ describe('oas3 boolean-parameter-prefixes', () => {
|
|
|
165
165
|
]
|
|
166
166
|
`);
|
|
167
167
|
});
|
|
168
|
+
|
|
169
|
+
it('should not report on refs inside specification extensions', async () => {
|
|
170
|
+
const document = parseYamlToDocument(
|
|
171
|
+
outdent`
|
|
172
|
+
openapi: 3.0.0
|
|
173
|
+
components:
|
|
174
|
+
requestBodies:
|
|
175
|
+
a:
|
|
176
|
+
content:
|
|
177
|
+
application/json:
|
|
178
|
+
schema:
|
|
179
|
+
type: object
|
|
180
|
+
x-webhooks:
|
|
181
|
+
test:
|
|
182
|
+
put:
|
|
183
|
+
requestBody:
|
|
184
|
+
$ref: '#/components/requestBodies/a'
|
|
185
|
+
`,
|
|
186
|
+
path.join(__dirname, 'foobar.yaml')
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const results = await lintDocument({
|
|
190
|
+
externalRefResolver: new BaseResolver(),
|
|
191
|
+
document,
|
|
192
|
+
config: await makeConfig({
|
|
193
|
+
'no-unresolved-refs': 'error',
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
expect(replaceSourceWithRef(results, __dirname)).toMatchInlineSnapshot(`Array []`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should not report on nested refs inside specification extensions', async () => {
|
|
201
|
+
const document = parseYamlToDocument(
|
|
202
|
+
outdent`
|
|
203
|
+
openapi: 3.0.0
|
|
204
|
+
x-test:
|
|
205
|
+
prop:
|
|
206
|
+
$ref: 'fixtures/ref.yaml'
|
|
207
|
+
paths:
|
|
208
|
+
'/test':
|
|
209
|
+
get:
|
|
210
|
+
x-codeSamples:
|
|
211
|
+
- lang: PHP
|
|
212
|
+
source:
|
|
213
|
+
$ref: 'fixtures/code-sample.php'
|
|
214
|
+
`,
|
|
215
|
+
path.join(__dirname, 'foobar.yaml')
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const results = await lintDocument({
|
|
219
|
+
externalRefResolver: new BaseResolver(),
|
|
220
|
+
document,
|
|
221
|
+
config: await makeConfig({
|
|
222
|
+
'no-unresolved-refs': 'error',
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(replaceSourceWithRef(results, __dirname)).toMatchInlineSnapshot(`Array []`);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should not report on nested refs inside specification extensions for 3.1', async () => {
|
|
230
|
+
const document = parseYamlToDocument(
|
|
231
|
+
outdent`
|
|
232
|
+
openapi: 3.1.0
|
|
233
|
+
x-test:
|
|
234
|
+
prop:
|
|
235
|
+
$ref: 'fixtures/ref.yaml'
|
|
236
|
+
paths:
|
|
237
|
+
'/test':
|
|
238
|
+
get:
|
|
239
|
+
x-codeSamples:
|
|
240
|
+
- lang: PHP
|
|
241
|
+
source:
|
|
242
|
+
$ref: 'fixtures/code-sample.php'
|
|
243
|
+
`,
|
|
244
|
+
path.join(__dirname, 'foobar.yaml')
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const results = await lintDocument({
|
|
248
|
+
externalRefResolver: new BaseResolver(),
|
|
249
|
+
document,
|
|
250
|
+
config: await makeConfig({
|
|
251
|
+
'no-unresolved-refs': 'error',
|
|
252
|
+
}),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(replaceSourceWithRef(results, __dirname)).toMatchInlineSnapshot(`Array []`);
|
|
256
|
+
});
|
|
168
257
|
});
|
|
@@ -125,4 +125,68 @@ describe('Oas3 operation-2xx-response', () => {
|
|
|
125
125
|
]
|
|
126
126
|
`);
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
it('should report missing 2xx response in webhooks when enabled', async () => {
|
|
130
|
+
const document = parseYamlToDocument(
|
|
131
|
+
outdent`
|
|
132
|
+
openapi: 3.1.0
|
|
133
|
+
webhooks:
|
|
134
|
+
'/test/':
|
|
135
|
+
put:
|
|
136
|
+
responses:
|
|
137
|
+
400:
|
|
138
|
+
description: success response
|
|
139
|
+
`,
|
|
140
|
+
'foobar.yaml'
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const results = await lintDocument({
|
|
144
|
+
externalRefResolver: new BaseResolver(),
|
|
145
|
+
document,
|
|
146
|
+
config: await makeConfig({
|
|
147
|
+
'operation-2xx-response': { severity: 'error', validateWebhooks: true },
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
152
|
+
Array [
|
|
153
|
+
Object {
|
|
154
|
+
"location": Array [
|
|
155
|
+
Object {
|
|
156
|
+
"pointer": "#/webhooks/~1test~1/put/responses",
|
|
157
|
+
"reportOnKey": true,
|
|
158
|
+
"source": "foobar.yaml",
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
"message": "Operation must have at least one \`2XX\` response.",
|
|
162
|
+
"ruleId": "operation-2xx-response",
|
|
163
|
+
"severity": "error",
|
|
164
|
+
"suggest": Array [],
|
|
165
|
+
},
|
|
166
|
+
]
|
|
167
|
+
`);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should not report missing 2xx response in webhooks when not enabled', async () => {
|
|
171
|
+
const document = parseYamlToDocument(
|
|
172
|
+
outdent`
|
|
173
|
+
openapi: 3.0.0
|
|
174
|
+
x-webhooks:
|
|
175
|
+
'/test/':
|
|
176
|
+
put:
|
|
177
|
+
responses:
|
|
178
|
+
400:
|
|
179
|
+
description: success response
|
|
180
|
+
`,
|
|
181
|
+
'foobar.yaml'
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const results = await lintDocument({
|
|
185
|
+
externalRefResolver: new BaseResolver(),
|
|
186
|
+
document,
|
|
187
|
+
config: await makeConfig({ 'operation-2xx-response': 'error' }),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
191
|
+
});
|
|
128
192
|
});
|
|
@@ -164,4 +164,68 @@ describe('Oas3 operation-4xx-response', () => {
|
|
|
164
164
|
]
|
|
165
165
|
`);
|
|
166
166
|
});
|
|
167
|
+
|
|
168
|
+
it('should report missing 4xx response in webhooks when enabled', async () => {
|
|
169
|
+
const document = parseYamlToDocument(
|
|
170
|
+
outdent`
|
|
171
|
+
openapi: 3.1.0
|
|
172
|
+
webhooks:
|
|
173
|
+
'/test/':
|
|
174
|
+
put:
|
|
175
|
+
responses:
|
|
176
|
+
200:
|
|
177
|
+
description: success response
|
|
178
|
+
`,
|
|
179
|
+
'foobar.yaml'
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const results = await lintDocument({
|
|
183
|
+
externalRefResolver: new BaseResolver(),
|
|
184
|
+
document,
|
|
185
|
+
config: await makeConfig({
|
|
186
|
+
'operation-4xx-response': { severity: 'error', validateWebhooks: true },
|
|
187
|
+
}),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
191
|
+
Array [
|
|
192
|
+
Object {
|
|
193
|
+
"location": Array [
|
|
194
|
+
Object {
|
|
195
|
+
"pointer": "#/webhooks/~1test~1/put/responses",
|
|
196
|
+
"reportOnKey": true,
|
|
197
|
+
"source": "foobar.yaml",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
"message": "Operation must have at least one \`4XX\` response.",
|
|
201
|
+
"ruleId": "operation-4xx-response",
|
|
202
|
+
"severity": "error",
|
|
203
|
+
"suggest": Array [],
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
`);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should not report missing 4xx response in webhooks when not enabled', async () => {
|
|
210
|
+
const document = parseYamlToDocument(
|
|
211
|
+
outdent`
|
|
212
|
+
openapi: 3.0.0
|
|
213
|
+
x-webhooks:
|
|
214
|
+
'/test/':
|
|
215
|
+
put:
|
|
216
|
+
responses:
|
|
217
|
+
200:
|
|
218
|
+
description: success response
|
|
219
|
+
`,
|
|
220
|
+
'foobar.yaml'
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const results = await lintDocument({
|
|
224
|
+
externalRefResolver: new BaseResolver(),
|
|
225
|
+
document,
|
|
226
|
+
config: await makeConfig({ 'operation-4xx-response': 'error' }),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
230
|
+
});
|
|
167
231
|
});
|