oas 20.8.0 → 20.8.2
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 +14 -0
- package/dist/lib/helpers.d.ts +2 -0
- package/dist/lib/helpers.js +8 -1
- package/dist/lib/openapi-to-json-schema.js +110 -30
- package/dist/samples/index.js +2 -2
- package/package.json +3 -2
- package/src/lib/helpers.ts +9 -0
- package/src/lib/openapi-to-json-schema.ts +121 -38
- package/src/samples/index.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## <small>20.8.2 (2023-05-04)</small>
|
|
2
|
+
|
|
3
|
+
* feat: adding a broken skipped test, retaining soeme more jsonschema props ([94436b8](https://github.com/readmeio/oas/commit/94436b8))
|
|
4
|
+
* feat: additional tweaks to json schema generation (#762) ([2bae3fe](https://github.com/readmeio/oas/commit/2bae3fe)), closes [#762](https://github.com/readmeio/oas/issues/762)
|
|
5
|
+
* feat: additional tweaks to json schema mixed type handling (#761) ([57d4442](https://github.com/readmeio/oas/commit/57d4442)), closes [#761](https://github.com/readmeio/oas/issues/761)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## <small>20.8.1 (2023-05-04)</small>
|
|
10
|
+
|
|
11
|
+
* feat: additional tweaks to json schema mixed type handling (#761) ([57d4442](https://github.com/readmeio/oas/commit/57d4442)), closes [#761](https://github.com/readmeio/oas/issues/761)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
## 20.8.0 (2023-05-03)
|
|
2
16
|
|
|
3
17
|
* feat: improved support for mixed types and `nullable` (#760) ([5eff5c4](https://github.com/readmeio/oas/commit/5eff5c4)), closes [#760](https://github.com/readmeio/oas/issues/760)
|
package/dist/lib/helpers.d.ts
CHANGED
package/dist/lib/helpers.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isPrimitive = void 0;
|
|
3
|
+
exports.isPrimitive = exports.hasSchemaType = void 0;
|
|
4
|
+
function hasSchemaType(schema, discriminator) {
|
|
5
|
+
if (Array.isArray(schema.type)) {
|
|
6
|
+
return schema.type.includes(discriminator);
|
|
7
|
+
}
|
|
8
|
+
return schema.type === discriminator;
|
|
9
|
+
}
|
|
10
|
+
exports.hasSchemaType = hasSchemaType;
|
|
4
11
|
function isPrimitive(val) {
|
|
5
12
|
return typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean';
|
|
6
13
|
}
|
|
@@ -60,6 +60,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
60
60
|
exports.getSchemaVersionString = void 0;
|
|
61
61
|
var json_schema_merge_allof_1 = __importDefault(require("json-schema-merge-allof"));
|
|
62
62
|
var jsonpointer_1 = __importDefault(require("jsonpointer"));
|
|
63
|
+
var remove_undefined_objects_1 = __importDefault(require("remove-undefined-objects"));
|
|
63
64
|
var RMOAS = __importStar(require("../rmoas.types"));
|
|
64
65
|
var helpers_1 = require("./helpers");
|
|
65
66
|
/**
|
|
@@ -203,8 +204,8 @@ function searchForExampleByPointer(pointer, examples) {
|
|
|
203
204
|
example = jsonpointer_1.default.get(schema, pointers[i]);
|
|
204
205
|
}
|
|
205
206
|
catch (err) {
|
|
206
|
-
// If the schema we're looking at is `{obj: null}` and our pointer
|
|
207
|
-
// jsonpointer will throw an error. If that happens, we should silently catch and toss it
|
|
207
|
+
// If the schema we're looking at is `{obj: null}` and our pointer is `/obj/propertyName`
|
|
208
|
+
// `jsonpointer` will throw an error. If that happens, we should silently catch and toss it
|
|
208
209
|
// and return no example.
|
|
209
210
|
}
|
|
210
211
|
if (example !== undefined) {
|
|
@@ -326,29 +327,6 @@ function toJSONSchema(data, opts) {
|
|
|
326
327
|
});
|
|
327
328
|
}
|
|
328
329
|
}
|
|
329
|
-
// To ease some of the burden on our frontend having to juggle mixed types we're opting to
|
|
330
|
-
// transform them into a `oneOf`.
|
|
331
|
-
if ('type' in schema && Array.isArray(schema.type)) {
|
|
332
|
-
schema.type = Array.from(new Set(schema.type));
|
|
333
|
-
// If we have a `null` type but there's only two types present then we can remove `null` as
|
|
334
|
-
// an option and flag the whole schema as `nullable`.
|
|
335
|
-
if (schema.type.includes('null')) {
|
|
336
|
-
schema.type = schema.type.filter(function (t) { return t !== 'null'; });
|
|
337
|
-
schema.nullable = true;
|
|
338
|
-
}
|
|
339
|
-
if (schema.type.length === 1) {
|
|
340
|
-
schema.type = schema.type.shift();
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
var mixedOneOf_1 = [];
|
|
344
|
-
schema.type.forEach(function (schemaType) {
|
|
345
|
-
mixedOneOf_1.push(__assign(__assign({}, schema), { type: schemaType }));
|
|
346
|
-
});
|
|
347
|
-
schema = {
|
|
348
|
-
oneOf: mixedOneOf_1,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
330
|
['anyOf', 'oneOf'].forEach(function (polyType) {
|
|
353
331
|
if (polyType in schema && Array.isArray(schema[polyType])) {
|
|
354
332
|
schema[polyType].forEach(function (item, idx) {
|
|
@@ -406,6 +384,110 @@ function toJSONSchema(data, opts) {
|
|
|
406
384
|
// Whatever tooling that ingests the generated schema should handle it however it needs to.
|
|
407
385
|
}
|
|
408
386
|
}
|
|
387
|
+
if ('type' in schema) {
|
|
388
|
+
// `nullable` isn't a thing in JSON Schema but it was in OpenAPI 3.0 so we should retain and
|
|
389
|
+
// translate it into something that's compatible with JSON Schema.
|
|
390
|
+
if ('nullable' in schema) {
|
|
391
|
+
if (Array.isArray(schema.type)) {
|
|
392
|
+
schema.type.push('null');
|
|
393
|
+
}
|
|
394
|
+
else if (schema.type !== null && schema.type !== 'null') {
|
|
395
|
+
schema.type = [schema.type, 'null'];
|
|
396
|
+
}
|
|
397
|
+
delete schema.nullable;
|
|
398
|
+
}
|
|
399
|
+
if (schema.type === null) {
|
|
400
|
+
// `type: null` is possible in JSON Schema but we're translating it to a string version
|
|
401
|
+
// so we don't need to worry about asserting nullish types in our implementations of this
|
|
402
|
+
// generated schema.
|
|
403
|
+
schema.type = 'null';
|
|
404
|
+
}
|
|
405
|
+
else if (Array.isArray(schema.type)) {
|
|
406
|
+
if (schema.type.includes(null)) {
|
|
407
|
+
schema.type[schema.type.indexOf(null)] = 'null';
|
|
408
|
+
}
|
|
409
|
+
schema.type = Array.from(new Set(schema.type));
|
|
410
|
+
// We don't need `type: [<type>]` when we can just as easily make it `type: <type>`.
|
|
411
|
+
if (schema.type.length === 1) {
|
|
412
|
+
schema.type = schema.type.shift();
|
|
413
|
+
}
|
|
414
|
+
else if (schema.type.includes('array') || schema.type.includes('object')) {
|
|
415
|
+
// If we have a `null` type but there's only two types present then we can remove `null`
|
|
416
|
+
// as an option and flag the whole schema as `nullable`.
|
|
417
|
+
var isNullable_1 = schema.type.includes('null');
|
|
418
|
+
if (schema.type.length === 2 && isNullable_1) {
|
|
419
|
+
// If this is `array | null` or `object | null` then we don't need to do anything.
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
// If this mixed type has non-primitives then we for convenience of our implementation
|
|
423
|
+
// we're moving them into a `oneOf`.
|
|
424
|
+
var nonPrimitives_1 = [];
|
|
425
|
+
// Because we're moving an `array` and/or an `object` into a `oneOf` we also want to take
|
|
426
|
+
// with it its specific properties that maybe present on our current schema.
|
|
427
|
+
Object.entries({
|
|
428
|
+
// json-schema.org/understanding-json-schema/reference/array.html
|
|
429
|
+
array: [
|
|
430
|
+
'additionalItems',
|
|
431
|
+
'contains',
|
|
432
|
+
'items',
|
|
433
|
+
'maxContains',
|
|
434
|
+
'maxItems',
|
|
435
|
+
'minContains',
|
|
436
|
+
'minItems',
|
|
437
|
+
'prefixItems',
|
|
438
|
+
'uniqueItems',
|
|
439
|
+
],
|
|
440
|
+
// https://json-schema.org/understanding-json-schema/reference/object.html
|
|
441
|
+
object: [
|
|
442
|
+
'additionalProperties',
|
|
443
|
+
'maxProperties',
|
|
444
|
+
'minProperties',
|
|
445
|
+
'nullable',
|
|
446
|
+
'patternProperties',
|
|
447
|
+
'properties',
|
|
448
|
+
'propertyNames',
|
|
449
|
+
'required',
|
|
450
|
+
],
|
|
451
|
+
}).forEach(function (_a) {
|
|
452
|
+
var _b, _c, _d, _e, _f, _g;
|
|
453
|
+
var typeKey = _a[0], keywords = _a[1];
|
|
454
|
+
if (!schema.type.includes(typeKey)) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
var reducedSchema = (0, remove_undefined_objects_1.default)({
|
|
458
|
+
type: isNullable_1 ? [typeKey, 'null'] : typeKey,
|
|
459
|
+
allowEmptyValue: (_b = schema.allowEmptyValue) !== null && _b !== void 0 ? _b : undefined,
|
|
460
|
+
deprecated: (_c = schema.deprecated) !== null && _c !== void 0 ? _c : undefined,
|
|
461
|
+
description: (_d = schema.description) !== null && _d !== void 0 ? _d : undefined,
|
|
462
|
+
readOnly: (_e = schema.readOnly) !== null && _e !== void 0 ? _e : undefined,
|
|
463
|
+
title: (_f = schema.title) !== null && _f !== void 0 ? _f : undefined,
|
|
464
|
+
writeOnly: (_g = schema.writeOnly) !== null && _g !== void 0 ? _g : undefined,
|
|
465
|
+
});
|
|
466
|
+
keywords.forEach(function (t) {
|
|
467
|
+
if (t in schema) {
|
|
468
|
+
reducedSchema[t] = schema[t];
|
|
469
|
+
delete schema[t];
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
nonPrimitives_1.push(reducedSchema);
|
|
473
|
+
});
|
|
474
|
+
schema.type = schema.type.filter(function (t) { return t !== 'array' && t !== 'object'; });
|
|
475
|
+
if (schema.type.length === 1) {
|
|
476
|
+
schema.type = schema.type.shift();
|
|
477
|
+
}
|
|
478
|
+
// Because we may have encountered a fully mixed non-primitive type like `array | object`
|
|
479
|
+
// we only want to retain the existing schema object if we still have types remaining
|
|
480
|
+
// in it.
|
|
481
|
+
if (schema.type.length > 1) {
|
|
482
|
+
schema = { oneOf: __spreadArray([schema], nonPrimitives_1, true) };
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
schema = { oneOf: nonPrimitives_1 };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
409
491
|
if (RMOAS.isSchema(schema, isPolymorphicAllOfChild)) {
|
|
410
492
|
// JSON Schema doesn't support OpenAPI-style examples so we need to reshape them a bit.
|
|
411
493
|
if ('example' in schema) {
|
|
@@ -471,7 +553,7 @@ function toJSONSchema(data, opts) {
|
|
|
471
553
|
// If we didn't have any immediately defined examples, let's search backwards and see if we can
|
|
472
554
|
// find one. But as we're only looking for primitive example, only try to search for one if
|
|
473
555
|
// we're dealing with a primitive schema.
|
|
474
|
-
if (schema
|
|
556
|
+
if (!(0, helpers_1.hasSchemaType)(schema, 'array') && !(0, helpers_1.hasSchemaType)(schema, 'object') && !schema.examples) {
|
|
475
557
|
var foundExample = searchForExampleByPointer(currentLocation, prevSchemas);
|
|
476
558
|
if (foundExample) {
|
|
477
559
|
// We can only really deal with primitives, so only promote those as the found example if
|
|
@@ -481,7 +563,7 @@ function toJSONSchema(data, opts) {
|
|
|
481
563
|
}
|
|
482
564
|
}
|
|
483
565
|
}
|
|
484
|
-
if (schema
|
|
566
|
+
if ((0, helpers_1.hasSchemaType)(schema, 'array')) {
|
|
485
567
|
if ('items' in schema) {
|
|
486
568
|
if (!Array.isArray(schema.items) && Object.keys(schema.items).length === 1 && RMOAS.isRef(schema.items)) {
|
|
487
569
|
// `items` contains a `$ref`, so since it's circular we should do a no-op here and log
|
|
@@ -505,17 +587,15 @@ function toJSONSchema(data, opts) {
|
|
|
505
587
|
// array. Since throwing a complete failure isn't ideal, we can see that they meant for the
|
|
506
588
|
// type to be `object`, so we can do our best to shape the data into what they were
|
|
507
589
|
// intending it to be.
|
|
508
|
-
// README-6R
|
|
509
590
|
schema.type = 'object';
|
|
510
591
|
}
|
|
511
592
|
else {
|
|
512
593
|
// This is a fix to handle cases where we have a malformed array with no `items` property
|
|
513
594
|
// present.
|
|
514
|
-
// README-8E
|
|
515
595
|
schema.items = {};
|
|
516
596
|
}
|
|
517
597
|
}
|
|
518
|
-
else if (schema
|
|
598
|
+
else if ((0, helpers_1.hasSchemaType)(schema, 'object')) {
|
|
519
599
|
if ('properties' in schema) {
|
|
520
600
|
Object.keys(schema.properties).forEach(function (prop) {
|
|
521
601
|
if (Array.isArray(schema.properties[prop]) ||
|
package/dist/samples/index.js
CHANGED
|
@@ -85,7 +85,7 @@ function sampleFromSchema(schema, opts) {
|
|
|
85
85
|
return undefined;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
if (type === 'object') {
|
|
88
|
+
if (type === 'object' || (Array.isArray(type) && type.includes('object'))) {
|
|
89
89
|
var props = (0, utils_1.objectify)(properties);
|
|
90
90
|
var obj = {};
|
|
91
91
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -114,7 +114,7 @@ function sampleFromSchema(schema, opts) {
|
|
|
114
114
|
}
|
|
115
115
|
return obj;
|
|
116
116
|
}
|
|
117
|
-
if (type === 'array') {
|
|
117
|
+
if (type === 'array' || (Array.isArray(type) && type.includes('array'))) {
|
|
118
118
|
// `items` should always be present on arrays, but if it isn't we should at least do our best
|
|
119
119
|
// to support its absence.
|
|
120
120
|
if (typeof items === 'undefined') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oas",
|
|
3
|
-
"version": "20.8.
|
|
3
|
+
"version": "20.8.2",
|
|
4
4
|
"description": "Comprehensive tooling for working with OpenAPI definitions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ReadMe <support@readme.io> (https://readme.com)",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"memoizee": "^0.4.14",
|
|
55
55
|
"oas-normalize": "^8.4.0",
|
|
56
56
|
"openapi-types": "^12.1.0",
|
|
57
|
-
"path-to-regexp": "^6.2.0"
|
|
57
|
+
"path-to-regexp": "^6.2.0",
|
|
58
|
+
"remove-undefined-objects": "^2.0.2"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
61
|
"@commitlint/cli": "^17.6.1",
|
package/src/lib/helpers.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import type { SchemaObject } from 'rmoas.types';
|
|
2
|
+
|
|
3
|
+
export function hasSchemaType(schema: SchemaObject, discriminator: 'array' | 'object') {
|
|
4
|
+
if (Array.isArray(schema.type)) {
|
|
5
|
+
return schema.type.includes(discriminator);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return schema.type === discriminator;
|
|
9
|
+
}
|
|
1
10
|
export function isPrimitive(val: unknown): boolean {
|
|
2
11
|
return typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean';
|
|
3
12
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/* eslint-disable no-continue */
|
|
2
|
+
import type { SchemaObject } from '../rmoas.types';
|
|
3
|
+
import type { JSONSchema7TypeName } from 'json-schema';
|
|
2
4
|
import type { OpenAPIV3_1 } from 'openapi-types';
|
|
3
5
|
|
|
4
6
|
import mergeJSONSchemaAllOf from 'json-schema-merge-allof';
|
|
5
7
|
import jsonpointer from 'jsonpointer';
|
|
8
|
+
import removeUndefinedObjects from 'remove-undefined-objects';
|
|
6
9
|
|
|
7
10
|
import * as RMOAS from '../rmoas.types';
|
|
8
11
|
|
|
9
|
-
import { isPrimitive } from './helpers';
|
|
12
|
+
import { hasSchemaType, isPrimitive } from './helpers';
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* This list has been pulled from `openapi-schema-to-json-schema` but been slightly modified to fit
|
|
@@ -205,8 +208,8 @@ function searchForExampleByPointer(pointer: string, examples: PrevSchemasType =
|
|
|
205
208
|
try {
|
|
206
209
|
example = jsonpointer.get(schema, pointers[i]);
|
|
207
210
|
} catch (err) {
|
|
208
|
-
// If the schema we're looking at is `{obj: null}` and our pointer
|
|
209
|
-
// jsonpointer will throw an error. If that happens, we should silently catch and toss it
|
|
211
|
+
// If the schema we're looking at is `{obj: null}` and our pointer is `/obj/propertyName`
|
|
212
|
+
// `jsonpointer` will throw an error. If that happens, we should silently catch and toss it
|
|
210
213
|
// and return no example.
|
|
211
214
|
}
|
|
212
215
|
|
|
@@ -362,35 +365,6 @@ export default function toJSONSchema(
|
|
|
362
365
|
}
|
|
363
366
|
}
|
|
364
367
|
|
|
365
|
-
// To ease some of the burden on our frontend having to juggle mixed types we're opting to
|
|
366
|
-
// transform them into a `oneOf`.
|
|
367
|
-
if ('type' in schema && Array.isArray(schema.type)) {
|
|
368
|
-
schema.type = Array.from(new Set(schema.type));
|
|
369
|
-
|
|
370
|
-
// If we have a `null` type but there's only two types present then we can remove `null` as
|
|
371
|
-
// an option and flag the whole schema as `nullable`.
|
|
372
|
-
if (schema.type.includes('null')) {
|
|
373
|
-
schema.type = schema.type.filter(t => t !== 'null');
|
|
374
|
-
schema.nullable = true;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (schema.type.length === 1) {
|
|
378
|
-
schema.type = schema.type.shift();
|
|
379
|
-
} else {
|
|
380
|
-
const mixedOneOf: any[] = [];
|
|
381
|
-
schema.type.forEach(schemaType => {
|
|
382
|
-
mixedOneOf.push({
|
|
383
|
-
...schema,
|
|
384
|
-
type: schemaType,
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
schema = {
|
|
389
|
-
oneOf: mixedOneOf,
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
368
|
['anyOf', 'oneOf'].forEach((polyType: 'anyOf' | 'oneOf') => {
|
|
395
369
|
if (polyType in schema && Array.isArray(schema[polyType])) {
|
|
396
370
|
schema[polyType].forEach((item, idx) => {
|
|
@@ -454,6 +428,117 @@ export default function toJSONSchema(
|
|
|
454
428
|
}
|
|
455
429
|
}
|
|
456
430
|
|
|
431
|
+
if ('type' in schema) {
|
|
432
|
+
// `nullable` isn't a thing in JSON Schema but it was in OpenAPI 3.0 so we should retain and
|
|
433
|
+
// translate it into something that's compatible with JSON Schema.
|
|
434
|
+
if ('nullable' in schema) {
|
|
435
|
+
if (Array.isArray(schema.type)) {
|
|
436
|
+
schema.type.push('null');
|
|
437
|
+
} else if (schema.type !== null && schema.type !== 'null') {
|
|
438
|
+
schema.type = [schema.type, 'null'];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
delete schema.nullable;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (schema.type === null) {
|
|
445
|
+
// `type: null` is possible in JSON Schema but we're translating it to a string version
|
|
446
|
+
// so we don't need to worry about asserting nullish types in our implementations of this
|
|
447
|
+
// generated schema.
|
|
448
|
+
schema.type = 'null';
|
|
449
|
+
} else if (Array.isArray(schema.type)) {
|
|
450
|
+
if (schema.type.includes(null)) {
|
|
451
|
+
schema.type[schema.type.indexOf(null)] = 'null';
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
schema.type = Array.from(new Set(schema.type));
|
|
455
|
+
|
|
456
|
+
// We don't need `type: [<type>]` when we can just as easily make it `type: <type>`.
|
|
457
|
+
if (schema.type.length === 1) {
|
|
458
|
+
schema.type = schema.type.shift();
|
|
459
|
+
} else if (schema.type.includes('array') || schema.type.includes('object')) {
|
|
460
|
+
// If we have a `null` type but there's only two types present then we can remove `null`
|
|
461
|
+
// as an option and flag the whole schema as `nullable`.
|
|
462
|
+
const isNullable = schema.type.includes('null');
|
|
463
|
+
|
|
464
|
+
if (schema.type.length === 2 && isNullable) {
|
|
465
|
+
// If this is `array | null` or `object | null` then we don't need to do anything.
|
|
466
|
+
} else {
|
|
467
|
+
// If this mixed type has non-primitives then we for convenience of our implementation
|
|
468
|
+
// we're moving them into a `oneOf`.
|
|
469
|
+
const nonPrimitives: any[] = [];
|
|
470
|
+
|
|
471
|
+
// Because we're moving an `array` and/or an `object` into a `oneOf` we also want to take
|
|
472
|
+
// with it its specific properties that maybe present on our current schema.
|
|
473
|
+
Object.entries({
|
|
474
|
+
// json-schema.org/understanding-json-schema/reference/array.html
|
|
475
|
+
array: [
|
|
476
|
+
'additionalItems',
|
|
477
|
+
'contains',
|
|
478
|
+
'items',
|
|
479
|
+
'maxContains',
|
|
480
|
+
'maxItems',
|
|
481
|
+
'minContains',
|
|
482
|
+
'minItems',
|
|
483
|
+
'prefixItems',
|
|
484
|
+
'uniqueItems',
|
|
485
|
+
],
|
|
486
|
+
|
|
487
|
+
// https://json-schema.org/understanding-json-schema/reference/object.html
|
|
488
|
+
object: [
|
|
489
|
+
'additionalProperties',
|
|
490
|
+
'maxProperties',
|
|
491
|
+
'minProperties',
|
|
492
|
+
'nullable',
|
|
493
|
+
'patternProperties',
|
|
494
|
+
'properties',
|
|
495
|
+
'propertyNames',
|
|
496
|
+
'required',
|
|
497
|
+
],
|
|
498
|
+
}).forEach(([typeKey, keywords]) => {
|
|
499
|
+
if (!schema.type.includes(typeKey as JSONSchema7TypeName)) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const reducedSchema: any = removeUndefinedObjects({
|
|
504
|
+
type: isNullable ? [typeKey, 'null'] : typeKey,
|
|
505
|
+
|
|
506
|
+
allowEmptyValue: (schema as any).allowEmptyValue ?? undefined,
|
|
507
|
+
deprecated: schema.deprecated ?? undefined,
|
|
508
|
+
description: schema.description ?? undefined,
|
|
509
|
+
readOnly: schema.readOnly ?? undefined,
|
|
510
|
+
title: schema.title ?? undefined,
|
|
511
|
+
writeOnly: schema.writeOnly ?? undefined,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
keywords.forEach((t: keyof SchemaObject) => {
|
|
515
|
+
if (t in schema) {
|
|
516
|
+
reducedSchema[t] = schema[t];
|
|
517
|
+
delete schema[t];
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
nonPrimitives.push(reducedSchema);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
schema.type = schema.type.filter(t => t !== 'array' && t !== 'object');
|
|
525
|
+
if (schema.type.length === 1) {
|
|
526
|
+
schema.type = schema.type.shift();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Because we may have encountered a fully mixed non-primitive type like `array | object`
|
|
530
|
+
// we only want to retain the existing schema object if we still have types remaining
|
|
531
|
+
// in it.
|
|
532
|
+
if (schema.type.length > 1) {
|
|
533
|
+
schema = { oneOf: [schema, ...nonPrimitives] };
|
|
534
|
+
} else {
|
|
535
|
+
schema = { oneOf: nonPrimitives };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
457
542
|
if (RMOAS.isSchema(schema, isPolymorphicAllOfChild)) {
|
|
458
543
|
// JSON Schema doesn't support OpenAPI-style examples so we need to reshape them a bit.
|
|
459
544
|
if ('example' in schema) {
|
|
@@ -516,7 +601,7 @@ export default function toJSONSchema(
|
|
|
516
601
|
// If we didn't have any immediately defined examples, let's search backwards and see if we can
|
|
517
602
|
// find one. But as we're only looking for primitive example, only try to search for one if
|
|
518
603
|
// we're dealing with a primitive schema.
|
|
519
|
-
if (schema
|
|
604
|
+
if (!hasSchemaType(schema, 'array') && !hasSchemaType(schema, 'object') && !schema.examples) {
|
|
520
605
|
const foundExample = searchForExampleByPointer(currentLocation, prevSchemas);
|
|
521
606
|
if (foundExample) {
|
|
522
607
|
// We can only really deal with primitives, so only promote those as the found example if
|
|
@@ -527,7 +612,7 @@ export default function toJSONSchema(
|
|
|
527
612
|
}
|
|
528
613
|
}
|
|
529
614
|
|
|
530
|
-
if (schema
|
|
615
|
+
if (hasSchemaType(schema, 'array')) {
|
|
531
616
|
if ('items' in schema) {
|
|
532
617
|
if (!Array.isArray(schema.items) && Object.keys(schema.items).length === 1 && RMOAS.isRef(schema.items)) {
|
|
533
618
|
// `items` contains a `$ref`, so since it's circular we should do a no-op here and log
|
|
@@ -549,15 +634,13 @@ export default function toJSONSchema(
|
|
|
549
634
|
// array. Since throwing a complete failure isn't ideal, we can see that they meant for the
|
|
550
635
|
// type to be `object`, so we can do our best to shape the data into what they were
|
|
551
636
|
// intending it to be.
|
|
552
|
-
// README-6R
|
|
553
637
|
schema.type = 'object';
|
|
554
638
|
} else {
|
|
555
639
|
// This is a fix to handle cases where we have a malformed array with no `items` property
|
|
556
640
|
// present.
|
|
557
|
-
|
|
558
|
-
schema.items = {};
|
|
641
|
+
(schema as any).items = {};
|
|
559
642
|
}
|
|
560
|
-
} else if (schema
|
|
643
|
+
} else if (hasSchemaType(schema, 'object')) {
|
|
561
644
|
if ('properties' in schema) {
|
|
562
645
|
Object.keys(schema.properties).forEach(prop => {
|
|
563
646
|
if (
|
package/src/samples/index.ts
CHANGED
|
@@ -110,7 +110,7 @@ function sampleFromSchema(
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
if (type === 'object') {
|
|
113
|
+
if (type === 'object' || (Array.isArray(type) && type.includes('object'))) {
|
|
114
114
|
const props = objectify(properties);
|
|
115
115
|
const obj: Record<string, any> = {};
|
|
116
116
|
// eslint-disable-next-line no-restricted-syntax
|
|
@@ -145,7 +145,7 @@ function sampleFromSchema(
|
|
|
145
145
|
return obj;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
if (type === 'array') {
|
|
148
|
+
if (type === 'array' || (Array.isArray(type) && type.includes('array'))) {
|
|
149
149
|
// `items` should always be present on arrays, but if it isn't we should at least do our best
|
|
150
150
|
// to support its absence.
|
|
151
151
|
if (typeof items === 'undefined') {
|