@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.
Files changed (42) hide show
  1. package/lib/config/config.d.ts +2 -3
  2. package/lib/config/config.js +1 -2
  3. package/lib/config/types.d.ts +13 -6
  4. package/lib/config/utils.d.ts +1 -0
  5. package/lib/config/utils.js +21 -12
  6. package/lib/decorators/common/info-override.d.ts +2 -0
  7. package/lib/decorators/common/info-override.js +28 -0
  8. package/lib/decorators/oas2/index.d.ts +1 -0
  9. package/lib/decorators/oas2/index.js +2 -0
  10. package/lib/decorators/oas3/index.d.ts +1 -0
  11. package/lib/decorators/oas3/index.js +2 -0
  12. package/lib/resolve.js +3 -1
  13. package/lib/rules/common/operation-2xx-response.js +15 -9
  14. package/lib/rules/common/operation-4xx-response.js +15 -9
  15. package/lib/rules/utils.d.ts +1 -0
  16. package/lib/rules/utils.js +14 -2
  17. package/lib/types/index.js +1 -1
  18. package/lib/types/redocly-yaml.js +9 -2
  19. package/lib/utils.d.ts +2 -2
  20. package/lib/utils.js +9 -4
  21. package/package.json +1 -1
  22. package/src/__tests__/lint.test.ts +19 -73
  23. package/src/config/__tests__/config.test.ts +8 -10
  24. package/src/config/__tests__/utils.test.ts +67 -6
  25. package/src/config/config.ts +3 -4
  26. package/src/config/types.ts +15 -6
  27. package/src/config/utils.ts +32 -18
  28. package/src/decorators/common/info-override.ts +15 -0
  29. package/src/decorators/oas2/index.ts +2 -0
  30. package/src/decorators/oas3/index.ts +2 -0
  31. package/src/resolve.ts +5 -1
  32. package/src/rules/__tests__/fixtures/code-sample.php +9 -0
  33. package/src/rules/__tests__/no-unresolved-refs.test.ts +89 -0
  34. package/src/rules/common/__tests__/operation-2xx-response.test.ts +64 -0
  35. package/src/rules/common/__tests__/operation-4xx-response.test.ts +64 -0
  36. package/src/rules/common/operation-2xx-response.ts +17 -9
  37. package/src/rules/common/operation-4xx-response.ts +16 -9
  38. package/src/rules/utils.ts +26 -1
  39. package/src/types/index.ts +1 -1
  40. package/src/types/redocly-yaml.ts +14 -4
  41. package/src/utils.ts +18 -4
  42. 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
- 'features.openapi': {},
48
- 'features.mockServer': {},
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
- 'features.openapi': {
45
- disableSidebar: true,
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
- 'features.openapi': {
67
- disableSidebar: true,
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
  });
@@ -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
- 'features.openapi': Record<string, any>;
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['features.openapi'] = rawConfig['features.openapi'] || {};
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;
@@ -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
- } & FeaturesConfig;
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
- } & FeaturesConfig;
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 FeaturesConfig = {
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'
@@ -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
- 'features.openapi': {
229
- ...config['features.openapi'],
230
- ...config.apis[apiName]?.['features.openapi'],
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', 'features.openapi'],
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
- 'features.openapi': referenceDocs,
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 (type.extensionsPrefix && propName.startsWith(type.extensionsPrefix)) {
279
+ if (
280
+ type.extensionsPrefix &&
281
+ propName.startsWith(type.extensionsPrefix) &&
282
+ propType === unknownType
283
+ ) {
280
284
  propType = SpecExtension;
281
285
  }
282
286
 
@@ -0,0 +1,9 @@
1
+ $form = new \PetStore\Entities\Pet();
2
+ $form->setPetType("Dog");
3
+ $form->setName("Rex");
4
+ // set other fields
5
+ try {
6
+ $pet = $client->pets()->create($form);
7
+ } catch (UnprocessableEntityException $e) {
8
+ var_dump($e->getErrors());
9
+ }
@@ -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
  });