mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 01:37:32 +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:
 | 
			
		||||
    ApiResponse:
 | 
			
		||||
      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:
 | 
			
		||||
        code:
 | 
			
		||||
          type: integer
 | 
			
		||||
| 
						 | 
				
			
			@ -975,7 +1002,7 @@ components:
 | 
			
		|||
        - type: object
 | 
			
		||||
          properties:
 | 
			
		||||
            huntingSkill:
 | 
			
		||||
              type: string
 | 
			
		||||
              type: [string, boolean]
 | 
			
		||||
              description: The measured skill for hunting
 | 
			
		||||
              default: lazy
 | 
			
		||||
              example: adventurous
 | 
			
		||||
| 
						 | 
				
			
			@ -1099,15 +1126,26 @@ components:
 | 
			
		|||
          example: Guru
 | 
			
		||||
        photoUrls:
 | 
			
		||||
          description: The list of URL to a cute photos featuring pet
 | 
			
		||||
          type: [string, integer, 'null', array]
 | 
			
		||||
          type: [string, integer, 'null']
 | 
			
		||||
          minItems: 1
 | 
			
		||||
          maxItems: 20
 | 
			
		||||
          maxItems: 10
 | 
			
		||||
          xml:
 | 
			
		||||
            name: photoUrl
 | 
			
		||||
            wrapped: true
 | 
			
		||||
          items:
 | 
			
		||||
            type: string
 | 
			
		||||
            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:
 | 
			
		||||
          $ref: '#/components/schemas/Pet'
 | 
			
		||||
        tags:
 | 
			
		||||
| 
						 | 
				
			
			@ -1131,6 +1169,12 @@ components:
 | 
			
		|||
        petType:
 | 
			
		||||
          description: Type of a pet
 | 
			
		||||
          type: string
 | 
			
		||||
        huntingSkill:
 | 
			
		||||
          type: [integer]
 | 
			
		||||
          enum:
 | 
			
		||||
            - 0
 | 
			
		||||
            - 1
 | 
			
		||||
            - 2
 | 
			
		||||
      xml:
 | 
			
		||||
        name: Pet
 | 
			
		||||
    Tag:
 | 
			
		||||
| 
						 | 
				
			
			@ -1198,6 +1242,15 @@ components:
 | 
			
		|||
          type: string
 | 
			
		||||
          contentEncoding: base64
 | 
			
		||||
          contentMediaType: image/png
 | 
			
		||||
      if:
 | 
			
		||||
        title: userStatus === 10
 | 
			
		||||
        properties:
 | 
			
		||||
          userStatus:
 | 
			
		||||
            enum: [10]
 | 
			
		||||
      then:
 | 
			
		||||
        required: ['phone']
 | 
			
		||||
      else:
 | 
			
		||||
        required: []
 | 
			
		||||
      xml:
 | 
			
		||||
        name: User
 | 
			
		||||
  requestBodies:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,7 @@ import {
 | 
			
		|||
} from '../../common-elements/fields-layout';
 | 
			
		||||
import { ShelfIcon } from '../../common-elements/';
 | 
			
		||||
import { Schema } from '../Schema/Schema';
 | 
			
		||||
 | 
			
		||||
import type { SchemaOptions } from '../Schema/Schema';
 | 
			
		||||
import type { FieldModel } from '../../services/models';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +49,7 @@ export class Field extends React.Component<FieldProps> {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { className, field, isLast, expandByDefault } = this.props;
 | 
			
		||||
    const { className = '', field, isLast, expandByDefault } = this.props;
 | 
			
		||||
    const { name, deprecated, required, kind } = field;
 | 
			
		||||
    const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import * as React from 'react';
 | 
			
		||||
import { observer } from 'mobx-react';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  RecursiveLabel,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +25,7 @@ import { OptionsContext } from '../OptionsProvider';
 | 
			
		|||
import { Pattern } from './Pattern';
 | 
			
		||||
import { ArrayItemDetails } from './ArrayItemDetails';
 | 
			
		||||
 | 
			
		||||
function FieldDetailsComponent(props: FieldProps) {
 | 
			
		||||
export const FieldDetailsComponent = observer((props: FieldProps) => {
 | 
			
		||||
  const { enumSkipQuotes, hideSchemaTitles } = React.useContext(OptionsContext);
 | 
			
		||||
 | 
			
		||||
  const { showExamples, field, renderDiscriminatorSwitch } = props;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +108,6 @@ function FieldDetailsComponent(props: FieldProps) {
 | 
			
		|||
      {(_const && <FieldDetail label={l('const') + ':'} value={_const} />) || null}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const FieldDetails = React.memo<FieldProps>(FieldDetailsComponent);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,20 +27,20 @@ class Json extends React.PureComponent<JsonProps> {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  renderInner = ({ renderCopyButton }) => {
 | 
			
		||||
    const showFoldingButtons = this.props.data && Object.values(this.props.data).some(
 | 
			
		||||
      (value) => typeof value === 'object' && value !== null,
 | 
			
		||||
    );
 | 
			
		||||
    const showFoldingButtons =
 | 
			
		||||
      this.props.data &&
 | 
			
		||||
      Object.values(this.props.data).some(value => typeof value === 'object' && value !== null);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <JsonViewerWrap>
 | 
			
		||||
        <SampleControls>
 | 
			
		||||
          {renderCopyButton()}
 | 
			
		||||
          {showFoldingButtons &&
 | 
			
		||||
          {showFoldingButtons && (
 | 
			
		||||
            <>
 | 
			
		||||
              <button onClick={this.expandAll}> Expand all </button>
 | 
			
		||||
              <button onClick={this.collapseAll}> Collapse all </button>
 | 
			
		||||
            </>
 | 
			
		||||
          }
 | 
			
		||||
          )}
 | 
			
		||||
        </SampleControls>
 | 
			
		||||
        <OptionsContext.Consumer>
 | 
			
		||||
          {options => (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,29 +268,44 @@ export class OpenAPIParser {
 | 
			
		|||
    }>;
 | 
			
		||||
 | 
			
		||||
    for (const { $ref: subSchemaRef, schema: subSchema } of allOfSchemas) {
 | 
			
		||||
      if (
 | 
			
		||||
        receiver.type !== subSchema.type &&
 | 
			
		||||
        receiver.type !== undefined &&
 | 
			
		||||
        subSchema.type !== undefined
 | 
			
		||||
      ) {
 | 
			
		||||
        console.warn(
 | 
			
		||||
          `Incompatible types in allOf at "${$ref}": "${receiver.type}" and "${subSchema.type}"`,
 | 
			
		||||
        );
 | 
			
		||||
      const {
 | 
			
		||||
        type,
 | 
			
		||||
        enum: enumProperty,
 | 
			
		||||
        properties,
 | 
			
		||||
        items,
 | 
			
		||||
        required,
 | 
			
		||||
        ...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) {
 | 
			
		||||
        receiver.type = subSchema.type;
 | 
			
		||||
      if (type !== undefined) {
 | 
			
		||||
        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 || {};
 | 
			
		||||
        for (const prop in subSchema.properties) {
 | 
			
		||||
        for (const prop in properties) {
 | 
			
		||||
          if (!receiver.properties[prop]) {
 | 
			
		||||
            receiver.properties[prop] = subSchema.properties[prop];
 | 
			
		||||
            receiver.properties[prop] = properties[prop];
 | 
			
		||||
          } else {
 | 
			
		||||
            // merge inner properties
 | 
			
		||||
            const mergedProp = this.mergeAllOf(
 | 
			
		||||
              { allOf: [receiver.properties[prop], subSchema.properties[prop]] },
 | 
			
		||||
              { allOf: [receiver.properties[prop], properties[prop]] },
 | 
			
		||||
              $ref + '/properties/' + prop,
 | 
			
		||||
            );
 | 
			
		||||
            receiver.properties[prop] = mergedProp;
 | 
			
		||||
| 
						 | 
				
			
			@ -299,22 +314,19 @@ export class OpenAPIParser {
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (subSchema.items !== undefined) {
 | 
			
		||||
      if (items !== undefined) {
 | 
			
		||||
        receiver.items = receiver.items || {};
 | 
			
		||||
        // merge inner properties
 | 
			
		||||
        receiver.items = this.mergeAllOf(
 | 
			
		||||
          { allOf: [receiver.items, subSchema.items] },
 | 
			
		||||
          $ref + '/items',
 | 
			
		||||
        );
 | 
			
		||||
        receiver.items = this.mergeAllOf({ allOf: [receiver.items, items] }, $ref + '/items');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (subSchema.required !== undefined) {
 | 
			
		||||
        receiver.required = (receiver.required || []).concat(subSchema.required);
 | 
			
		||||
      if (required !== undefined) {
 | 
			
		||||
        receiver.required = (receiver.required || []).concat(required);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // merge rest of constraints
 | 
			
		||||
      // TODO: do more intelligent merge
 | 
			
		||||
      receiver = { ...subSchema, ...receiver };
 | 
			
		||||
      receiver = { ...receiver, ...otherConstraints };
 | 
			
		||||
 | 
			
		||||
      if (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');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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', () => {
 | 
			
		||||
      const spec = require('../fixtures/3.1/unevaluatedProperties.json');
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((schema.if && schema.then) || (schema.if && schema.else)) {
 | 
			
		||||
      this.initConditionalOperators(schema, parser);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!isChild && getDiscriminator(schema) !== undefined) {
 | 
			
		||||
      this.initDiscriminator(schema, parser);
 | 
			
		||||
      return;
 | 
			
		||||
| 
						 | 
				
			
			@ -355,6 +360,38 @@ export class SchemaModel {
 | 
			
		|||
      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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,6 +148,10 @@ export interface OpenAPISchema {
 | 
			
		|||
  minProperties?: number;
 | 
			
		||||
  enum?: any[];
 | 
			
		||||
  example?: any;
 | 
			
		||||
 | 
			
		||||
  if?: OpenAPISchema;
 | 
			
		||||
  else?: OpenAPISchema;
 | 
			
		||||
  then?: OpenAPISchema;
 | 
			
		||||
  examples?: any[];
 | 
			
		||||
  const?: string;
 | 
			
		||||
  contentEncoding?: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1903,6 +1903,46 @@ Object {
 | 
			
		|||
    },
 | 
			
		||||
    "schemas": 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 {
 | 
			
		||||
          "code": Object {
 | 
			
		||||
            "format": "int32",
 | 
			
		||||
| 
						 | 
				
			
			@ -1934,7 +1974,10 @@ Object {
 | 
			
		|||
                  "aggressive",
 | 
			
		||||
                ],
 | 
			
		||||
                "example": "adventurous",
 | 
			
		||||
                "type": "string",
 | 
			
		||||
                "type": Array [
 | 
			
		||||
                  "string",
 | 
			
		||||
                  "boolean",
 | 
			
		||||
                ],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            "required": Array [
 | 
			
		||||
| 
						 | 
				
			
			@ -2086,6 +2129,16 @@ Object {
 | 
			
		|||
          "friend": Object {
 | 
			
		||||
            "$ref": "#/components/schemas/Pet",
 | 
			
		||||
          },
 | 
			
		||||
          "huntingSkill": Object {
 | 
			
		||||
            "enum": Array [
 | 
			
		||||
              0,
 | 
			
		||||
              1,
 | 
			
		||||
              2,
 | 
			
		||||
            ],
 | 
			
		||||
            "type": Array [
 | 
			
		||||
              "integer",
 | 
			
		||||
            ],
 | 
			
		||||
          },
 | 
			
		||||
          "id": Object {
 | 
			
		||||
            "$ref": "#/components/schemas/Id",
 | 
			
		||||
            "description": "Pet ID",
 | 
			
		||||
| 
						 | 
				
			
			@ -2105,17 +2158,33 @@ Object {
 | 
			
		|||
          },
 | 
			
		||||
          "photoUrls": Object {
 | 
			
		||||
            "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 {
 | 
			
		||||
              "format": "url",
 | 
			
		||||
              "type": "string",
 | 
			
		||||
            },
 | 
			
		||||
            "maxItems": 20,
 | 
			
		||||
            "maxItems": 10,
 | 
			
		||||
            "minItems": 1,
 | 
			
		||||
            "then": Object {
 | 
			
		||||
              "maxItems": 15,
 | 
			
		||||
              "minItems": 1,
 | 
			
		||||
            },
 | 
			
		||||
            "type": Array [
 | 
			
		||||
              "string",
 | 
			
		||||
              "integer",
 | 
			
		||||
              "null",
 | 
			
		||||
              "array",
 | 
			
		||||
            ],
 | 
			
		||||
            "xml": Object {
 | 
			
		||||
              "name": "photoUrl",
 | 
			
		||||
| 
						 | 
				
			
			@ -2173,6 +2242,19 @@ Object {
 | 
			
		|||
        },
 | 
			
		||||
      },
 | 
			
		||||
      "User": Object {
 | 
			
		||||
        "else": Object {
 | 
			
		||||
          "required": Array [],
 | 
			
		||||
        },
 | 
			
		||||
        "if": Object {
 | 
			
		||||
          "properties": Object {
 | 
			
		||||
            "userStatus": Object {
 | 
			
		||||
              "enum": Array [
 | 
			
		||||
                10,
 | 
			
		||||
              ],
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          "title": "userStatus === 10",
 | 
			
		||||
        },
 | 
			
		||||
        "properties": Object {
 | 
			
		||||
          "email": Object {
 | 
			
		||||
            "description": "User email address",
 | 
			
		||||
| 
						 | 
				
			
			@ -2238,6 +2320,11 @@ Object {
 | 
			
		|||
            "type": "string",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "then": Object {
 | 
			
		||||
          "required": Array [
 | 
			
		||||
            "phone",
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
        "type": "object",
 | 
			
		||||
        "xml": Object {
 | 
			
		||||
          "name": "User",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -277,7 +277,7 @@ describe('Utils', () => {
 | 
			
		|||
      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 = {
 | 
			
		||||
        type: ['object', 'string', 'null'],
 | 
			
		||||
      };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,6 +125,10 @@ export function isPrimitiveType(
 | 
			
		|||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((schema.if && schema.then) || (schema.if && schema.else)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let isPrimitive = true;
 | 
			
		||||
  const isArrayType = isArray(type);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user