mirror of
https://github.com/Redocly/redoc.git
synced 2025-04-26 19:53:49 +03:00
feat: support conditional operators (#1939)
* fix: merge type and enum in allOf for 3.1 * chore: add example for OpenApi 3.1 * fix: correct merge constraints in allOf
This commit is contained in:
parent
ddcc76b5fa
commit
291b62a206
|
@ -960,6 +960,33 @@ components:
|
||||||
schemas:
|
schemas:
|
||||||
ApiResponse:
|
ApiResponse:
|
||||||
type: object
|
type: object
|
||||||
|
patternProperties:
|
||||||
|
^S_\\w+\\.[1-9]{2,4}$:
|
||||||
|
description: The measured skill for hunting
|
||||||
|
if:
|
||||||
|
x-displayName: fieldName === 'status'
|
||||||
|
else:
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 10
|
||||||
|
then:
|
||||||
|
format: url
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- success
|
||||||
|
- failed
|
||||||
|
^O_\\w+\\.[1-9]{2,4}$:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
nestedProperty:
|
||||||
|
type: [string, boolean]
|
||||||
|
description: The measured skill for hunting
|
||||||
|
default: lazy
|
||||||
|
example: adventurous
|
||||||
|
enum:
|
||||||
|
- clueless
|
||||||
|
- lazy
|
||||||
|
- adventurous
|
||||||
|
- aggressive
|
||||||
properties:
|
properties:
|
||||||
code:
|
code:
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -975,7 +1002,7 @@ components:
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
huntingSkill:
|
huntingSkill:
|
||||||
type: string
|
type: [string, boolean]
|
||||||
description: The measured skill for hunting
|
description: The measured skill for hunting
|
||||||
default: lazy
|
default: lazy
|
||||||
example: adventurous
|
example: adventurous
|
||||||
|
@ -1099,15 +1126,26 @@ components:
|
||||||
example: Guru
|
example: Guru
|
||||||
photoUrls:
|
photoUrls:
|
||||||
description: The list of URL to a cute photos featuring pet
|
description: The list of URL to a cute photos featuring pet
|
||||||
type: [string, integer, 'null', array]
|
type: [string, integer, 'null']
|
||||||
minItems: 1
|
minItems: 1
|
||||||
maxItems: 20
|
maxItems: 10
|
||||||
xml:
|
xml:
|
||||||
name: photoUrl
|
name: photoUrl
|
||||||
wrapped: true
|
wrapped: true
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
format: url
|
format: url
|
||||||
|
if:
|
||||||
|
x-displayName: isString
|
||||||
|
type: string
|
||||||
|
then:
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 15
|
||||||
|
else:
|
||||||
|
x-displayName: notString
|
||||||
|
type: [integer, 'null']
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 20
|
||||||
friend:
|
friend:
|
||||||
$ref: '#/components/schemas/Pet'
|
$ref: '#/components/schemas/Pet'
|
||||||
tags:
|
tags:
|
||||||
|
@ -1131,6 +1169,12 @@ components:
|
||||||
petType:
|
petType:
|
||||||
description: Type of a pet
|
description: Type of a pet
|
||||||
type: string
|
type: string
|
||||||
|
huntingSkill:
|
||||||
|
type: [integer]
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
xml:
|
xml:
|
||||||
name: Pet
|
name: Pet
|
||||||
Tag:
|
Tag:
|
||||||
|
@ -1198,6 +1242,15 @@ components:
|
||||||
type: string
|
type: string
|
||||||
contentEncoding: base64
|
contentEncoding: base64
|
||||||
contentMediaType: image/png
|
contentMediaType: image/png
|
||||||
|
if:
|
||||||
|
title: userStatus === 10
|
||||||
|
properties:
|
||||||
|
userStatus:
|
||||||
|
enum: [10]
|
||||||
|
then:
|
||||||
|
required: ['phone']
|
||||||
|
else:
|
||||||
|
required: []
|
||||||
xml:
|
xml:
|
||||||
name: User
|
name: User
|
||||||
requestBodies:
|
requestBodies:
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
} from '../../common-elements/fields-layout';
|
} from '../../common-elements/fields-layout';
|
||||||
import { ShelfIcon } from '../../common-elements/';
|
import { ShelfIcon } from '../../common-elements/';
|
||||||
import { Schema } from '../Schema/Schema';
|
import { Schema } from '../Schema/Schema';
|
||||||
|
|
||||||
import type { SchemaOptions } from '../Schema/Schema';
|
import type { SchemaOptions } from '../Schema/Schema';
|
||||||
import type { FieldModel } from '../../services/models';
|
import type { FieldModel } from '../../services/models';
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ export class Field extends React.Component<FieldProps> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, field, isLast, expandByDefault } = this.props;
|
const { className = '', field, isLast, expandByDefault } = this.props;
|
||||||
const { name, deprecated, required, kind } = field;
|
const { name, deprecated, required, kind } = field;
|
||||||
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
|
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RecursiveLabel,
|
RecursiveLabel,
|
||||||
|
@ -24,7 +25,7 @@ import { OptionsContext } from '../OptionsProvider';
|
||||||
import { Pattern } from './Pattern';
|
import { Pattern } from './Pattern';
|
||||||
import { ArrayItemDetails } from './ArrayItemDetails';
|
import { ArrayItemDetails } from './ArrayItemDetails';
|
||||||
|
|
||||||
function FieldDetailsComponent(props: FieldProps) {
|
export const FieldDetailsComponent = observer((props: FieldProps) => {
|
||||||
const { enumSkipQuotes, hideSchemaTitles } = React.useContext(OptionsContext);
|
const { enumSkipQuotes, hideSchemaTitles } = React.useContext(OptionsContext);
|
||||||
|
|
||||||
const { showExamples, field, renderDiscriminatorSwitch } = props;
|
const { showExamples, field, renderDiscriminatorSwitch } = props;
|
||||||
|
@ -107,6 +108,6 @@ function FieldDetailsComponent(props: FieldProps) {
|
||||||
{(_const && <FieldDetail label={l('const') + ':'} value={_const} />) || null}
|
{(_const && <FieldDetail label={l('const') + ':'} value={_const} />) || null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
export const FieldDetails = React.memo<FieldProps>(FieldDetailsComponent);
|
export const FieldDetails = React.memo<FieldProps>(FieldDetailsComponent);
|
||||||
|
|
|
@ -27,20 +27,20 @@ class Json extends React.PureComponent<JsonProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInner = ({ renderCopyButton }) => {
|
renderInner = ({ renderCopyButton }) => {
|
||||||
const showFoldingButtons = this.props.data && Object.values(this.props.data).some(
|
const showFoldingButtons =
|
||||||
(value) => typeof value === 'object' && value !== null,
|
this.props.data &&
|
||||||
);
|
Object.values(this.props.data).some(value => typeof value === 'object' && value !== null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JsonViewerWrap>
|
<JsonViewerWrap>
|
||||||
<SampleControls>
|
<SampleControls>
|
||||||
{renderCopyButton()}
|
{renderCopyButton()}
|
||||||
{showFoldingButtons &&
|
{showFoldingButtons && (
|
||||||
<>
|
<>
|
||||||
<button onClick={this.expandAll}> Expand all </button>
|
<button onClick={this.expandAll}> Expand all </button>
|
||||||
<button onClick={this.collapseAll}> Collapse all </button>
|
<button onClick={this.collapseAll}> Collapse all </button>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</SampleControls>
|
</SampleControls>
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
|
|
|
@ -268,29 +268,44 @@ export class OpenAPIParser {
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
for (const { $ref: subSchemaRef, schema: subSchema } of allOfSchemas) {
|
for (const { $ref: subSchemaRef, schema: subSchema } of allOfSchemas) {
|
||||||
if (
|
const {
|
||||||
receiver.type !== subSchema.type &&
|
type,
|
||||||
receiver.type !== undefined &&
|
enum: enumProperty,
|
||||||
subSchema.type !== undefined
|
properties,
|
||||||
) {
|
items,
|
||||||
console.warn(
|
required,
|
||||||
`Incompatible types in allOf at "${$ref}": "${receiver.type}" and "${subSchema.type}"`,
|
...otherConstraints
|
||||||
);
|
} = subSchema;
|
||||||
|
|
||||||
|
if (receiver.type !== type && receiver.type !== undefined && type !== undefined) {
|
||||||
|
console.warn(`Incompatible types in allOf at "${$ref}": "${receiver.type}" and "${type}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subSchema.type !== undefined) {
|
if (type !== undefined) {
|
||||||
receiver.type = subSchema.type;
|
if (Array.isArray(type) && Array.isArray(receiver.type)) {
|
||||||
|
receiver.type = [...type, ...receiver.type];
|
||||||
|
} else {
|
||||||
|
receiver.type = type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subSchema.properties !== undefined) {
|
if (enumProperty !== undefined) {
|
||||||
|
if (Array.isArray(enumProperty) && Array.isArray(receiver.enum)) {
|
||||||
|
receiver.enum = [...enumProperty, ...receiver.enum];
|
||||||
|
} else {
|
||||||
|
receiver.enum = enumProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties !== undefined) {
|
||||||
receiver.properties = receiver.properties || {};
|
receiver.properties = receiver.properties || {};
|
||||||
for (const prop in subSchema.properties) {
|
for (const prop in properties) {
|
||||||
if (!receiver.properties[prop]) {
|
if (!receiver.properties[prop]) {
|
||||||
receiver.properties[prop] = subSchema.properties[prop];
|
receiver.properties[prop] = properties[prop];
|
||||||
} else {
|
} else {
|
||||||
// merge inner properties
|
// merge inner properties
|
||||||
const mergedProp = this.mergeAllOf(
|
const mergedProp = this.mergeAllOf(
|
||||||
{ allOf: [receiver.properties[prop], subSchema.properties[prop]] },
|
{ allOf: [receiver.properties[prop], properties[prop]] },
|
||||||
$ref + '/properties/' + prop,
|
$ref + '/properties/' + prop,
|
||||||
);
|
);
|
||||||
receiver.properties[prop] = mergedProp;
|
receiver.properties[prop] = mergedProp;
|
||||||
|
@ -299,22 +314,19 @@ export class OpenAPIParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subSchema.items !== undefined) {
|
if (items !== undefined) {
|
||||||
receiver.items = receiver.items || {};
|
receiver.items = receiver.items || {};
|
||||||
// merge inner properties
|
// merge inner properties
|
||||||
receiver.items = this.mergeAllOf(
|
receiver.items = this.mergeAllOf({ allOf: [receiver.items, items] }, $ref + '/items');
|
||||||
{ allOf: [receiver.items, subSchema.items] },
|
|
||||||
$ref + '/items',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subSchema.required !== undefined) {
|
if (required !== undefined) {
|
||||||
receiver.required = (receiver.required || []).concat(subSchema.required);
|
receiver.required = (receiver.required || []).concat(required);
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge rest of constraints
|
// merge rest of constraints
|
||||||
// TODO: do more intelligent merge
|
// TODO: do more intelligent merge
|
||||||
receiver = { ...subSchema, ...receiver };
|
receiver = { ...receiver, ...otherConstraints };
|
||||||
|
|
||||||
if (subSchemaRef) {
|
if (subSchemaRef) {
|
||||||
receiver.parentRefs!.push(subSchemaRef);
|
receiver.parentRefs!.push(subSchemaRef);
|
||||||
|
|
40
src/services/__tests__/fixtures/3.1/conditionalField.json
Normal file
40
src/services/__tests__/fixtures/3.1/conditionalField.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Schema definition field with conditional operators",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Test": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"test": {
|
||||||
|
"type": ["string", "integer", "null"],
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 20,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
},
|
||||||
|
"if": {
|
||||||
|
"x-displayName": "isString",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"type": "string",
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 20
|
||||||
|
},
|
||||||
|
"else": {
|
||||||
|
"x-displayName": "notString",
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 10,
|
||||||
|
"pattern": "\\d+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/services/__tests__/fixtures/3.1/conditionalSchema.json
Normal file
40
src/services/__tests__/fixtures/3.1/conditionalSchema.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Schema definition with conditional operators",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Test": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"test": {
|
||||||
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
|
"type": ["string", "integer", "null"],
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 20,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"if": {
|
||||||
|
"title": "=== 10",
|
||||||
|
"properties": {
|
||||||
|
"test": {
|
||||||
|
"enum": [10]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"maxItems": 2
|
||||||
|
},
|
||||||
|
"else": {
|
||||||
|
"maxItems": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,6 +49,32 @@ describe('Models', () => {
|
||||||
expect(schema.pointer).toBe('#/components/schemas/Child');
|
expect(schema.pointer).toBe('#/components/schemas/Child');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('schemaDefinition should resolve schema with conditional operators', () => {
|
||||||
|
const spec = require('../fixtures/3.1/conditionalSchema.json');
|
||||||
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
const schema = new SchemaModel(parser, spec.components.schemas.Test, '', opts);
|
||||||
|
expect(schema.oneOf).toHaveLength(2);
|
||||||
|
|
||||||
|
expect(schema.oneOf![0].schema.title).toBe('=== 10');
|
||||||
|
expect(schema.oneOf![1].schema.title).toBe('case 2');
|
||||||
|
|
||||||
|
expect(schema.oneOf![0].schema).toMatchSnapshot();
|
||||||
|
expect(schema.oneOf![1].schema).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('schemaDefinition should resolve field with conditional operators', () => {
|
||||||
|
const spec = require('../fixtures/3.1/conditionalField.json');
|
||||||
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
const schema = new SchemaModel(parser, spec.components.schemas.Test, '', opts);
|
||||||
|
expect(schema.fields).toHaveLength(1);
|
||||||
|
expect(schema.fields && schema.fields[0].schema.oneOf).toHaveLength(2);
|
||||||
|
expect(schema.fields && schema.fields[0].schema.oneOf![0].schema.title).toBe('isString');
|
||||||
|
expect(schema.fields && schema.fields[0].schema.oneOf![1].schema.title).toBe('notString');
|
||||||
|
|
||||||
|
expect(schema.fields && schema.fields[0].schema.oneOf![0].schema).toMatchSnapshot();
|
||||||
|
expect(schema.fields && schema.fields[0].schema.oneOf![1].schema).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
test('schemaDefinition should resolve unevaluatedProperties in properties', () => {
|
test('schemaDefinition should resolve unevaluatedProperties in properties', () => {
|
||||||
const spec = require('../fixtures/3.1/unevaluatedProperties.json');
|
const spec = require('../fixtures/3.1/unevaluatedProperties.json');
|
||||||
parser = new OpenAPIParser(spec, undefined, opts);
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
|
107
src/services/__tests__/models/__snapshots__/Schema.test.ts.snap
Normal file
107
src/services/__tests__/models/__snapshots__/Schema.test.ts.snap
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Models Schema schemaDefinition should resolve field with conditional operators 1`] = `
|
||||||
|
Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"default": undefined,
|
||||||
|
"items": Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"format": "url",
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"title": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"maxItems": 20,
|
||||||
|
"minItems": 1,
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"title": "isString",
|
||||||
|
"type": "string",
|
||||||
|
"x-displayName": "isString",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Models Schema schemaDefinition should resolve field with conditional operators 2`] = `
|
||||||
|
Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"default": undefined,
|
||||||
|
"items": Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"format": "url",
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"title": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"maxItems": 10,
|
||||||
|
"minItems": 1,
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"pattern": "\\\\d+",
|
||||||
|
"title": "notString",
|
||||||
|
"type": Array [
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"null",
|
||||||
|
],
|
||||||
|
"x-displayName": "notString",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 1`] = `
|
||||||
|
Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"maxItems": 2,
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"properties": Object {
|
||||||
|
"test": Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
|
"enum": Array [
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
"items": Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"format": "url",
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"title": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"maxItems": 20,
|
||||||
|
"minItems": 1,
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"title": undefined,
|
||||||
|
"type": Array [
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"null",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "=== 10",
|
||||||
|
"type": "object",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 2`] = `
|
||||||
|
Object {
|
||||||
|
"allOf": undefined,
|
||||||
|
"maxItems": 20,
|
||||||
|
"parentRefs": Array [],
|
||||||
|
"properties": Object {
|
||||||
|
"test": Object {
|
||||||
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
|
"items": Object {
|
||||||
|
"format": "url",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"maxItems": 20,
|
||||||
|
"minItems": 1,
|
||||||
|
"type": Array [
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"null",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "case 2",
|
||||||
|
"type": "object",
|
||||||
|
}
|
||||||
|
`;
|
|
@ -152,6 +152,11 @@ export class SchemaModel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((schema.if && schema.then) || (schema.if && schema.else)) {
|
||||||
|
this.initConditionalOperators(schema, parser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isChild && getDiscriminator(schema) !== undefined) {
|
if (!isChild && getDiscriminator(schema) !== undefined) {
|
||||||
this.initDiscriminator(schema, parser);
|
this.initDiscriminator(schema, parser);
|
||||||
return;
|
return;
|
||||||
|
@ -355,6 +360,38 @@ export class SchemaModel {
|
||||||
return innerSchema;
|
return innerSchema;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initConditionalOperators(schema: OpenAPISchema, parser: OpenAPIParser) {
|
||||||
|
const {
|
||||||
|
if: ifOperator,
|
||||||
|
else: elseOperator = {},
|
||||||
|
then: thenOperator = {},
|
||||||
|
...restSchema
|
||||||
|
} = schema;
|
||||||
|
const groupedOperators = [
|
||||||
|
{
|
||||||
|
allOf: [restSchema, thenOperator, ifOperator],
|
||||||
|
title: (ifOperator && ifOperator['x-displayName']) || ifOperator?.title || 'case 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
allOf: [restSchema, elseOperator],
|
||||||
|
title: (elseOperator && elseOperator['x-displayName']) || elseOperator?.title || 'case 2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
this.oneOf = groupedOperators.map(
|
||||||
|
(variant, idx) =>
|
||||||
|
new SchemaModel(
|
||||||
|
parser,
|
||||||
|
{
|
||||||
|
...variant,
|
||||||
|
} as OpenAPISchema,
|
||||||
|
this.pointer + '/oneOf/' + idx,
|
||||||
|
this.options,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.oneOfType = 'One of';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFields(
|
function buildFields(
|
||||||
|
|
|
@ -148,6 +148,10 @@ export interface OpenAPISchema {
|
||||||
minProperties?: number;
|
minProperties?: number;
|
||||||
enum?: any[];
|
enum?: any[];
|
||||||
example?: any;
|
example?: any;
|
||||||
|
|
||||||
|
if?: OpenAPISchema;
|
||||||
|
else?: OpenAPISchema;
|
||||||
|
then?: OpenAPISchema;
|
||||||
examples?: any[];
|
examples?: any[];
|
||||||
const?: string;
|
const?: string;
|
||||||
contentEncoding?: string;
|
contentEncoding?: string;
|
||||||
|
|
|
@ -1903,6 +1903,46 @@ Object {
|
||||||
},
|
},
|
||||||
"schemas": Object {
|
"schemas": Object {
|
||||||
"ApiResponse": Object {
|
"ApiResponse": Object {
|
||||||
|
"patternProperties": Object {
|
||||||
|
"^O_\\\\\\\\w+\\\\\\\\.[1-9]{2,4}$": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"nestedProperty": Object {
|
||||||
|
"default": "lazy",
|
||||||
|
"description": "The measured skill for hunting",
|
||||||
|
"enum": Array [
|
||||||
|
"clueless",
|
||||||
|
"lazy",
|
||||||
|
"adventurous",
|
||||||
|
"aggressive",
|
||||||
|
],
|
||||||
|
"example": "adventurous",
|
||||||
|
"type": Array [
|
||||||
|
"string",
|
||||||
|
"boolean",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
"^S_\\\\\\\\w+\\\\\\\\.[1-9]{2,4}$": Object {
|
||||||
|
"description": "The measured skill for hunting",
|
||||||
|
"else": Object {
|
||||||
|
"maxLength": 10,
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"if": Object {
|
||||||
|
"x-displayName": "fieldName === 'status'",
|
||||||
|
},
|
||||||
|
"then": Object {
|
||||||
|
"enum": Array [
|
||||||
|
"success",
|
||||||
|
"failed",
|
||||||
|
],
|
||||||
|
"format": "url",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"code": Object {
|
"code": Object {
|
||||||
"format": "int32",
|
"format": "int32",
|
||||||
|
@ -1934,7 +1974,10 @@ Object {
|
||||||
"aggressive",
|
"aggressive",
|
||||||
],
|
],
|
||||||
"example": "adventurous",
|
"example": "adventurous",
|
||||||
"type": "string",
|
"type": Array [
|
||||||
|
"string",
|
||||||
|
"boolean",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"required": Array [
|
"required": Array [
|
||||||
|
@ -2086,6 +2129,16 @@ Object {
|
||||||
"friend": Object {
|
"friend": Object {
|
||||||
"$ref": "#/components/schemas/Pet",
|
"$ref": "#/components/schemas/Pet",
|
||||||
},
|
},
|
||||||
|
"huntingSkill": Object {
|
||||||
|
"enum": Array [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
"type": Array [
|
||||||
|
"integer",
|
||||||
|
],
|
||||||
|
},
|
||||||
"id": Object {
|
"id": Object {
|
||||||
"$ref": "#/components/schemas/Id",
|
"$ref": "#/components/schemas/Id",
|
||||||
"description": "Pet ID",
|
"description": "Pet ID",
|
||||||
|
@ -2105,17 +2158,33 @@ Object {
|
||||||
},
|
},
|
||||||
"photoUrls": Object {
|
"photoUrls": Object {
|
||||||
"description": "The list of URL to a cute photos featuring pet",
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
|
"else": Object {
|
||||||
|
"maxItems": 20,
|
||||||
|
"minItems": 1,
|
||||||
|
"type": Array [
|
||||||
|
"integer",
|
||||||
|
"null",
|
||||||
|
],
|
||||||
|
"x-displayName": "notString",
|
||||||
|
},
|
||||||
|
"if": Object {
|
||||||
|
"type": "string",
|
||||||
|
"x-displayName": "isString",
|
||||||
|
},
|
||||||
"items": Object {
|
"items": Object {
|
||||||
"format": "url",
|
"format": "url",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"maxItems": 20,
|
"maxItems": 10,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
|
"then": Object {
|
||||||
|
"maxItems": 15,
|
||||||
|
"minItems": 1,
|
||||||
|
},
|
||||||
"type": Array [
|
"type": Array [
|
||||||
"string",
|
"string",
|
||||||
"integer",
|
"integer",
|
||||||
"null",
|
"null",
|
||||||
"array",
|
|
||||||
],
|
],
|
||||||
"xml": Object {
|
"xml": Object {
|
||||||
"name": "photoUrl",
|
"name": "photoUrl",
|
||||||
|
@ -2173,6 +2242,19 @@ Object {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"User": Object {
|
"User": Object {
|
||||||
|
"else": Object {
|
||||||
|
"required": Array [],
|
||||||
|
},
|
||||||
|
"if": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"userStatus": Object {
|
||||||
|
"enum": Array [
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "userStatus === 10",
|
||||||
|
},
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
"email": Object {
|
"email": Object {
|
||||||
"description": "User email address",
|
"description": "User email address",
|
||||||
|
@ -2238,6 +2320,11 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"then": Object {
|
||||||
|
"required": Array [
|
||||||
|
"phone",
|
||||||
|
],
|
||||||
|
},
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"xml": Object {
|
"xml": Object {
|
||||||
"name": "User",
|
"name": "User",
|
||||||
|
|
|
@ -277,7 +277,7 @@ describe('Utils', () => {
|
||||||
expect(isPrimitiveType(schema)).toEqual(true);
|
expect(isPrimitiveType(schema)).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should return false for array of string which include the null value', () => {
|
it('Should return true for array of string which include the null value', () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
type: ['object', 'string', 'null'],
|
type: ['object', 'string', 'null'],
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,6 +125,10 @@ export function isPrimitiveType(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((schema.if && schema.then) || (schema.if && schema.else)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let isPrimitive = true;
|
let isPrimitive = true;
|
||||||
const isArrayType = isArray(type);
|
const isArrayType = isArray(type);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user