oas 17.8.0 → 17.8.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.
@@ -1,6 +1,7 @@
1
1
  import type { RequestBodyExamples } from './operation/get-requestbody-examples';
2
2
  import type { CallbackExamples } from './operation/get-callback-examples';
3
3
  import type { ResponseExamples } from './operation/get-response-examples';
4
+ import type { SchemaWrapper } from './operation/get-parameters-as-json-schema';
4
5
  import * as RMOAS from './rmoas.types';
5
6
  declare type SecurityType = 'Basic' | 'Bearer' | 'Query' | 'Header' | 'Cookie' | 'OAuth2' | 'http' | 'apiKey';
6
7
  export default class Operation {
@@ -43,6 +44,10 @@ export default class Operation {
43
44
  request: string[];
44
45
  response: string[];
45
46
  };
47
+ /**
48
+ * All parameters and request bodies converted into JSON Schema.
49
+ */
50
+ parameterJsonSchema: SchemaWrapper[];
46
51
  constructor(api: RMOAS.OASDocument, path: string, method: RMOAS.HttpMethods, operation: RMOAS.OperationObject);
47
52
  getSummary(): string;
48
53
  getDescription(): string;
@@ -84,11 +89,15 @@ export default class Operation {
84
89
  */
85
90
  hasOperationId(): boolean;
86
91
  /**
87
- * Get an `operationId` for this operation. If one is not present (it's not required by the spec!) a hash of the path
88
- * and method will be returned instead.
92
+ * Get an `operationId` for this operation. If one is not present (it's not required by the spec!)
93
+ * a hash of the path and method will be returned instead.
89
94
  *
95
+ * @param opts
96
+ * @param opts.camelCase Generate a JS method-friendly operation ID when one isn't present.
90
97
  */
91
- getOperationId(): string;
98
+ getOperationId(opts?: {
99
+ camelCase: boolean;
100
+ }): string;
92
101
  /**
93
102
  * Return an array of all tags, and their metadata, that exist on this operation.
94
103
  *
@@ -109,13 +118,18 @@ export default class Operation {
109
118
  *
110
119
  */
111
120
  getParameters(): RMOAS.ParameterObject[];
121
+ /**
122
+ * Determine if this operation has any required parameters.
123
+ *
124
+ */
125
+ hasRequiredParameters(): boolean;
112
126
  /**
113
127
  * Convert the operation into an array of JSON Schema schemas for each available type of parameter available on the
114
128
  * operation.
115
129
  *
116
130
  * @param globalDefaults Contains an object of user defined schema defaults.
117
131
  */
118
- getParametersAsJsonSchema(globalDefaults?: Record<string, unknown>): import("./operation/get-parameters-as-json-schema").SchemaWrapper[];
132
+ getParametersAsJsonSchema(globalDefaults?: Record<string, unknown>): SchemaWrapper[];
119
133
  /**
120
134
  * Get a single response for this status code, formatted as JSON schema.
121
135
  *
@@ -144,6 +158,11 @@ export default class Operation {
144
158
  * @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#mediaTypeObject}
145
159
  */
146
160
  getRequestBodyMediaTypes(): string[];
161
+ /**
162
+ * Determine if this operation has a required request body.
163
+ *
164
+ */
165
+ hasRequiredRequestBody(): boolean;
147
166
  /**
148
167
  * Retrieve a specific request body content schema off this operation.
149
168
  *
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## <small>17.8.1 (2022-03-04)</small>
2
+
3
+ * fix: typo in the `--pattern` option ([42db80a](https://github.com/readmeio/oas/commit/42db80a))
4
+ * feat: adding new accessors for determining if an operation has required params (#615) ([c081d92](https://github.com/readmeio/oas/commit/c081d92)), closes [#615](https://github.com/readmeio/oas/issues/615)
5
+ * feat: optionally generate friendlier operationIds (#602) ([6826164](https://github.com/readmeio/oas/commit/6826164)), closes [#602](https://github.com/readmeio/oas/issues/602)
6
+
7
+
8
+
1
9
  ## 17.8.0 (2022-03-02)
2
10
 
3
11
  * chore(deps-dev): bump @commitlint/cli from 16.1.0 to 16.2.1 (#612) ([e7364dd](https://github.com/readmeio/oas/commit/e7364dd)), closes [#612](https://github.com/readmeio/oas/issues/612)
package/dist/operation.js CHANGED
@@ -286,20 +286,33 @@ var Operation = /** @class */ (function () {
286
286
  return 'operationId' in this.schema;
287
287
  };
288
288
  /**
289
- * Get an `operationId` for this operation. If one is not present (it's not required by the spec!) a hash of the path
290
- * and method will be returned instead.
289
+ * Get an `operationId` for this operation. If one is not present (it's not required by the spec!)
290
+ * a hash of the path and method will be returned instead.
291
291
  *
292
+ * @param opts
293
+ * @param opts.camelCase Generate a JS method-friendly operation ID when one isn't present.
292
294
  */
293
- Operation.prototype.getOperationId = function () {
295
+ Operation.prototype.getOperationId = function (opts) {
294
296
  if ('operationId' in this.schema) {
295
297
  return this.schema.operationId;
296
298
  }
297
- var url = this.path
299
+ var method = this.method.toLowerCase();
300
+ var operationId = this.path
298
301
  .replace(/[^a-zA-Z0-9]/g, '-') // Remove weird characters
299
302
  .replace(/^-|-$/g, '') // Don't start or end with -
300
303
  .replace(/--+/g, '-') // Remove double --'s
301
304
  .toLowerCase();
302
- return "".concat(this.method.toLowerCase(), "_").concat(url);
305
+ if (opts === null || opts === void 0 ? void 0 : opts.camelCase) {
306
+ operationId = operationId.replace(/[^a-zA-Z0-9]+(.)/g, function (_, chr) { return chr.toUpperCase(); });
307
+ // If the generated operationId already starts with the method (eg. `getPets`) we don't want
308
+ // to double it up into `getGetPets`.
309
+ if (operationId.startsWith(method)) {
310
+ return operationId;
311
+ }
312
+ operationId = operationId.charAt(0).toUpperCase() + operationId.slice(1);
313
+ return "".concat(method).concat(operationId);
314
+ }
315
+ return "".concat(method, "_").concat(operationId);
303
316
  };
304
317
  /**
305
318
  * Return an array of all tags, and their metadata, that exist on this operation.
@@ -358,6 +371,13 @@ var Operation = /** @class */ (function () {
358
371
  }
359
372
  return parameters;
360
373
  };
374
+ /**
375
+ * Determine if this operation has any required parameters.
376
+ *
377
+ */
378
+ Operation.prototype.hasRequiredParameters = function () {
379
+ return this.getParameters().some(function (param) { return 'required' in param && param.required; });
380
+ };
361
381
  /**
362
382
  * Convert the operation into an array of JSON Schema schemas for each available type of parameter available on the
363
383
  * operation.
@@ -365,7 +385,11 @@ var Operation = /** @class */ (function () {
365
385
  * @param globalDefaults Contains an object of user defined schema defaults.
366
386
  */
367
387
  Operation.prototype.getParametersAsJsonSchema = function (globalDefaults) {
368
- return (0, get_parameters_as_json_schema_1["default"])(this, this.api, globalDefaults);
388
+ if (this.parameterJsonSchema) {
389
+ return this.parameterJsonSchema;
390
+ }
391
+ this.parameterJsonSchema = (0, get_parameters_as_json_schema_1["default"])(this, this.api, globalDefaults);
392
+ return this.parameterJsonSchema;
369
393
  };
370
394
  /**
371
395
  * Get a single response for this status code, formatted as JSON schema.
@@ -407,6 +431,33 @@ var Operation = /** @class */ (function () {
407
431
  }
408
432
  return Object.keys(requestBody.content);
409
433
  };
434
+ /**
435
+ * Determine if this operation has a required request body.
436
+ *
437
+ */
438
+ Operation.prototype.hasRequiredRequestBody = function () {
439
+ if (!this.hasRequestBody()) {
440
+ return false;
441
+ }
442
+ var requestBody = this.schema.requestBody;
443
+ if (RMOAS.isRef(requestBody)) {
444
+ return false;
445
+ }
446
+ if (requestBody.required) {
447
+ return true;
448
+ }
449
+ // The OpenAPI spec isn't clear on the differentiation between schema `required` and
450
+ // `requestBody.required` because you can have required top-level schema properties but a
451
+ // non-required requestBody that negates each other.
452
+ //
453
+ // To kind of work ourselves around this and present a better QOL for this accessor, if at this
454
+ // final point where we don't have a required request body, but the underlying Media Type Object
455
+ // schema says that it has required properties then we should ultimately recognize that this
456
+ // request body is required -- even as the request body description says otherwise.
457
+ return !!this.getParametersAsJsonSchema()
458
+ .filter(function (js) { return ['body', 'formData'].includes(js.type); })
459
+ .find(function (js) { return js.schema && Array.isArray(js.schema.required) && js.schema.required.length; });
460
+ };
410
461
  /**
411
462
  * Retrieve a specific request body content schema off this operation.
412
463
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oas",
3
- "version": "17.8.0",
3
+ "version": "17.8.1",
4
4
  "description": "Working with OpenAPI definitions is hard. This makes it easier.",
5
5
  "license": "MIT",
6
6
  "author": "ReadMe <support@readme.io> (https://readme.com)",
@@ -53,7 +53,7 @@ exports.findSwagger = async function (info, cb) {
53
53
  format: '.json',
54
54
  scope: info.opts.scope,
55
55
  base,
56
- pattern: info.ops.pattern || null,
56
+ pattern: info.opts.pattern || null,
57
57
  }).catch(err => {
58
58
  console.error(err);
59
59
  process.exit(1);
package/src/operation.ts CHANGED
@@ -2,6 +2,7 @@ import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
2
  import type { RequestBodyExamples } from './operation/get-requestbody-examples';
3
3
  import type { CallbackExamples } from './operation/get-callback-examples';
4
4
  import type { ResponseExamples } from './operation/get-response-examples';
5
+ import type { SchemaWrapper } from './operation/get-parameters-as-json-schema';
5
6
 
6
7
  import * as RMOAS from './rmoas.types';
7
8
  import dedupeCommonParameters from './lib/dedupe-common-parameters';
@@ -65,6 +66,11 @@ export default class Operation {
65
66
  response: string[];
66
67
  };
67
68
 
69
+ /**
70
+ * All parameters and request bodies converted into JSON Schema.
71
+ */
72
+ parameterJsonSchema: SchemaWrapper[];
73
+
68
74
  constructor(api: RMOAS.OASDocument, path: string, method: RMOAS.HttpMethods, operation: RMOAS.OperationObject) {
69
75
  this.schema = operation;
70
76
  this.api = api;
@@ -323,22 +329,38 @@ export default class Operation {
323
329
  }
324
330
 
325
331
  /**
326
- * Get an `operationId` for this operation. If one is not present (it's not required by the spec!) a hash of the path
327
- * and method will be returned instead.
332
+ * Get an `operationId` for this operation. If one is not present (it's not required by the spec!)
333
+ * a hash of the path and method will be returned instead.
328
334
  *
335
+ * @param opts
336
+ * @param opts.camelCase Generate a JS method-friendly operation ID when one isn't present.
329
337
  */
330
- getOperationId(): string {
338
+ getOperationId(opts?: { camelCase: boolean }): string {
331
339
  if ('operationId' in this.schema) {
332
340
  return this.schema.operationId;
333
341
  }
334
342
 
335
- const url = this.path
343
+ const method = this.method.toLowerCase();
344
+ let operationId = this.path
336
345
  .replace(/[^a-zA-Z0-9]/g, '-') // Remove weird characters
337
346
  .replace(/^-|-$/g, '') // Don't start or end with -
338
347
  .replace(/--+/g, '-') // Remove double --'s
339
348
  .toLowerCase();
340
349
 
341
- return `${this.method.toLowerCase()}_${url}`;
350
+ if (opts?.camelCase) {
351
+ operationId = operationId.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
352
+
353
+ // If the generated operationId already starts with the method (eg. `getPets`) we don't want
354
+ // to double it up into `getGetPets`.
355
+ if (operationId.startsWith(method)) {
356
+ return operationId;
357
+ }
358
+
359
+ operationId = operationId.charAt(0).toUpperCase() + operationId.slice(1);
360
+ return `${method}${operationId}`;
361
+ }
362
+
363
+ return `${method}_${operationId}`;
342
364
  }
343
365
 
344
366
  /**
@@ -405,6 +427,14 @@ export default class Operation {
405
427
  return parameters;
406
428
  }
407
429
 
430
+ /**
431
+ * Determine if this operation has any required parameters.
432
+ *
433
+ */
434
+ hasRequiredParameters() {
435
+ return this.getParameters().some(param => 'required' in param && param.required);
436
+ }
437
+
408
438
  /**
409
439
  * Convert the operation into an array of JSON Schema schemas for each available type of parameter available on the
410
440
  * operation.
@@ -412,7 +442,12 @@ export default class Operation {
412
442
  * @param globalDefaults Contains an object of user defined schema defaults.
413
443
  */
414
444
  getParametersAsJsonSchema(globalDefaults?: Record<string, unknown>) {
415
- return getParametersAsJsonSchema(this, this.api, globalDefaults);
445
+ if (this.parameterJsonSchema) {
446
+ return this.parameterJsonSchema;
447
+ }
448
+
449
+ this.parameterJsonSchema = getParametersAsJsonSchema(this, this.api, globalDefaults);
450
+ return this.parameterJsonSchema;
416
451
  }
417
452
 
418
453
  /**
@@ -461,6 +496,37 @@ export default class Operation {
461
496
  return Object.keys(requestBody.content);
462
497
  }
463
498
 
499
+ /**
500
+ * Determine if this operation has a required request body.
501
+ *
502
+ */
503
+ hasRequiredRequestBody() {
504
+ if (!this.hasRequestBody()) {
505
+ return false;
506
+ }
507
+
508
+ const requestBody = this.schema.requestBody;
509
+ if (RMOAS.isRef(requestBody)) {
510
+ return false;
511
+ }
512
+
513
+ if (requestBody.required) {
514
+ return true;
515
+ }
516
+
517
+ // The OpenAPI spec isn't clear on the differentiation between schema `required` and
518
+ // `requestBody.required` because you can have required top-level schema properties but a
519
+ // non-required requestBody that negates each other.
520
+ //
521
+ // To kind of work ourselves around this and present a better QOL for this accessor, if at this
522
+ // final point where we don't have a required request body, but the underlying Media Type Object
523
+ // schema says that it has required properties then we should ultimately recognize that this
524
+ // request body is required -- even as the request body description says otherwise.
525
+ return !!this.getParametersAsJsonSchema()
526
+ .filter(js => ['body', 'formData'].includes(js.type))
527
+ .find(js => js.schema && Array.isArray(js.schema.required) && js.schema.required.length);
528
+ }
529
+
464
530
  /**
465
531
  * Retrieve a specific request body content schema off this operation.
466
532
  *