mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-07 21:54:53 +03:00
#573 fix discriminator handling when referencing classes which only have a superclass, but no subclasses (and add description to ObjectSchema.tsx)
This commit is contained in:
parent
57e93ec435
commit
26a8cff142
|
@ -1,9 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import styled from '../../styled-components';
|
|
||||||
|
|
||||||
import { DropdownProps } from '../../common-elements';
|
import { DropdownProps } from '../../common-elements';
|
||||||
import { MediaTypeModel } from '../../services/models';
|
import { MediaTypeModel } from '../../services/models';
|
||||||
|
import styled from '../../styled-components';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { Example } from './Example';
|
import { Example } from './Example';
|
||||||
import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements';
|
import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements';
|
||||||
|
@ -28,6 +26,7 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { activeIdx } = this.state;
|
const { activeIdx } = this.state;
|
||||||
|
console.log(this.props);
|
||||||
const examples = this.props.mediaType.examples || {};
|
const examples = this.props.mediaType.examples || {};
|
||||||
const mimeType = this.props.mediaType.name;
|
const mimeType = this.props.mediaType.name;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { SchemaModel } from '../../services/models';
|
|
||||||
|
|
||||||
import { PropertiesTable, PropertiesTableCaption } from '../../common-elements/fields-layout';
|
import { PropertiesTable, PropertiesTableCaption } from '../../common-elements/fields-layout';
|
||||||
|
import { SchemaModel } from '../../services/models';
|
||||||
|
import { mapWithLast } from '../../utils';
|
||||||
import { Field } from '../Fields/Field';
|
import { Field } from '../Fields/Field';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { DiscriminatorDropdown } from './DiscriminatorDropdown';
|
import { DiscriminatorDropdown } from './DiscriminatorDropdown';
|
||||||
import { SchemaProps } from './Schema';
|
import { SchemaProps } from './Schema';
|
||||||
|
|
||||||
import { mapWithLast } from '../../utils';
|
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
|
||||||
|
|
||||||
export interface ObjectSchemaProps extends SchemaProps {
|
export interface ObjectSchemaProps extends SchemaProps {
|
||||||
discriminator?: {
|
discriminator?: {
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
|
@ -47,37 +45,40 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
||||||
const expandByDefault = this.context.expandSingleSchemaField && filteredFields.length === 1;
|
const expandByDefault = this.context.expandSingleSchemaField && filteredFields.length === 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PropertiesTable>
|
<div>
|
||||||
{showTitle && <PropertiesTableCaption>{this.props.schema.title}</PropertiesTableCaption>}
|
<Markdown compact={true} inline={true} source={this.props.schema.description} />
|
||||||
<tbody>
|
<PropertiesTable>
|
||||||
{mapWithLast(filteredFields, (field, isLast) => {
|
{showTitle && <PropertiesTableCaption>{this.props.schema.title}</PropertiesTableCaption>}
|
||||||
return (
|
<tbody>
|
||||||
<Field
|
{mapWithLast(filteredFields, (field, isLast) => {
|
||||||
key={field.name}
|
return (
|
||||||
isLast={isLast}
|
<Field
|
||||||
field={field}
|
key={field.name}
|
||||||
expandByDefault={expandByDefault}
|
isLast={isLast}
|
||||||
renderDiscriminatorSwitch={
|
field={field}
|
||||||
(discriminator &&
|
expandByDefault={expandByDefault}
|
||||||
discriminator.fieldName === field.name &&
|
renderDiscriminatorSwitch={
|
||||||
(() => (
|
(discriminator &&
|
||||||
<DiscriminatorDropdown
|
discriminator.fieldName === field.name &&
|
||||||
parent={this.parentSchema}
|
(() => (
|
||||||
enumValues={field.schema.enum}
|
<DiscriminatorDropdown
|
||||||
/>
|
parent={this.parentSchema}
|
||||||
))) ||
|
enumValues={field.schema.enum}
|
||||||
undefined
|
/>
|
||||||
}
|
))) ||
|
||||||
className={field.expanded ? 'expanded' : undefined}
|
undefined
|
||||||
showExamples={false}
|
}
|
||||||
skipReadOnly={this.props.skipReadOnly}
|
className={field.expanded ? 'expanded' : undefined}
|
||||||
skipWriteOnly={this.props.skipWriteOnly}
|
showExamples={false}
|
||||||
showTitle={this.props.showTitle}
|
skipReadOnly={this.props.skipReadOnly}
|
||||||
/>
|
skipWriteOnly={this.props.skipWriteOnly}
|
||||||
);
|
showTitle={this.props.showTitle}
|
||||||
})}
|
/>
|
||||||
</tbody>
|
);
|
||||||
</PropertiesTable>
|
})}
|
||||||
|
</tbody>
|
||||||
|
</PropertiesTable>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { RecursiveLabel, TypeName, TypeTitle } from '../../common-elements/fields';
|
import { RecursiveLabel, TypeName, TypeTitle } from '../../common-elements/fields';
|
||||||
import { FieldDetails } from '../Fields/FieldDetails';
|
import { l } from '../../services/Labels';
|
||||||
|
|
||||||
import { FieldModel, SchemaModel } from '../../services/models';
|
import { FieldModel, SchemaModel } from '../../services/models';
|
||||||
|
import { FieldDetails } from '../Fields/FieldDetails';
|
||||||
import { ArraySchema } from './ArraySchema';
|
import { ArraySchema } from './ArraySchema';
|
||||||
import { ObjectSchema } from './ObjectSchema';
|
import { ObjectSchema } from './ObjectSchema';
|
||||||
import { OneOfSchema } from './OneOfSchema';
|
import { OneOfSchema } from './OneOfSchema';
|
||||||
|
|
||||||
import { l } from '../../services/Labels';
|
|
||||||
|
|
||||||
export interface SchemaOptions {
|
export interface SchemaOptions {
|
||||||
showTitle?: boolean;
|
showTitle?: boolean;
|
||||||
skipReadOnly?: boolean;
|
skipReadOnly?: boolean;
|
||||||
|
@ -43,10 +39,9 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
||||||
|
|
||||||
if (discriminatorProp !== undefined) {
|
if (discriminatorProp !== undefined) {
|
||||||
if (!oneOf || !oneOf.length) {
|
if (!oneOf || !oneOf.length) {
|
||||||
throw new Error(
|
return <ObjectSchema {...(this.props as any)} />;
|
||||||
`Looks like you are using discriminator wrong: you don't have any definition inherited from the ${schema.title}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ObjectSchema
|
<ObjectSchema
|
||||||
{...{ ...this.props, schema: oneOf![schema.activeOneOf] }}
|
{...{ ...this.props, schema: oneOf![schema.activeOneOf] }}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { DarkRightPanel, MiddlePanel, MimeLabel, Row, Section } from '../../common-elements';
|
import { DarkRightPanel, MiddlePanel, MimeLabel, Row, Section } from '../../common-elements';
|
||||||
import { MediaTypeModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
import { MediaTypeModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
|
@ -1,109 +1,116 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Components SchemaView discriminator should correctly render discriminator dropdown 1`] = `
|
exports[`Components SchemaView discriminator should correctly render discriminator dropdown 1`] = `
|
||||||
<styled.table>
|
<div>
|
||||||
<tbody>
|
<Markdown
|
||||||
<Field
|
compact={true}
|
||||||
field={
|
inline={true}
|
||||||
FieldModel {
|
source=""
|
||||||
"deprecated": false,
|
/>
|
||||||
"description": "",
|
<styled.table>
|
||||||
"example": undefined,
|
<tbody>
|
||||||
"expanded": undefined,
|
<Field
|
||||||
"explode": false,
|
field={
|
||||||
"in": undefined,
|
FieldModel {
|
||||||
"kind": "field",
|
|
||||||
"name": "packSize",
|
|
||||||
"required": false,
|
|
||||||
"schema": SchemaModel {
|
|
||||||
"activeOneOf": 0,
|
|
||||||
"constraints": Array [],
|
|
||||||
"default": undefined,
|
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"displayFormat": undefined,
|
|
||||||
"displayType": "number",
|
|
||||||
"enum": Array [],
|
|
||||||
"example": undefined,
|
"example": undefined,
|
||||||
"externalDocs": undefined,
|
"expanded": undefined,
|
||||||
"format": undefined,
|
"explode": false,
|
||||||
"isCircular": undefined,
|
"in": undefined,
|
||||||
"isPrimitive": true,
|
"kind": "field",
|
||||||
"nullable": false,
|
"name": "packSize",
|
||||||
"options": "<<<filtered>>>",
|
"required": false,
|
||||||
"pattern": undefined,
|
"schema": SchemaModel {
|
||||||
"pointer": "#/components/schemas/Dog/properties/packSize",
|
"activeOneOf": 0,
|
||||||
"rawSchema": Object {
|
"constraints": Array [],
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "",
|
||||||
|
"displayFormat": undefined,
|
||||||
|
"displayType": "number",
|
||||||
|
"enum": Array [],
|
||||||
|
"example": undefined,
|
||||||
|
"externalDocs": undefined,
|
||||||
|
"format": undefined,
|
||||||
|
"isCircular": undefined,
|
||||||
|
"isPrimitive": true,
|
||||||
|
"nullable": false,
|
||||||
|
"options": "<<<filtered>>>",
|
||||||
|
"pattern": undefined,
|
||||||
|
"pointer": "#/components/schemas/Dog/properties/packSize",
|
||||||
|
"rawSchema": Object {
|
||||||
|
"default": undefined,
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"readOnly": false,
|
||||||
|
"schema": Object {
|
||||||
|
"default": undefined,
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"title": "",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
"typePrefix": "",
|
||||||
|
"writeOnly": false,
|
||||||
},
|
},
|
||||||
"readOnly": false,
|
}
|
||||||
"schema": Object {
|
|
||||||
"default": undefined,
|
|
||||||
"type": "number",
|
|
||||||
},
|
|
||||||
"title": "",
|
|
||||||
"type": "number",
|
|
||||||
"typePrefix": "",
|
|
||||||
"writeOnly": false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
isLast={false}
|
||||||
isLast={false}
|
key="packSize"
|
||||||
key="packSize"
|
showExamples={false}
|
||||||
showExamples={false}
|
/>
|
||||||
/>
|
<Field
|
||||||
<Field
|
field={
|
||||||
field={
|
FieldModel {
|
||||||
FieldModel {
|
|
||||||
"deprecated": false,
|
|
||||||
"description": "",
|
|
||||||
"example": undefined,
|
|
||||||
"expanded": undefined,
|
|
||||||
"explode": false,
|
|
||||||
"in": undefined,
|
|
||||||
"kind": "field",
|
|
||||||
"name": "type",
|
|
||||||
"required": true,
|
|
||||||
"schema": SchemaModel {
|
|
||||||
"activeOneOf": 0,
|
|
||||||
"constraints": Array [],
|
|
||||||
"default": undefined,
|
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"displayFormat": undefined,
|
|
||||||
"displayType": "string",
|
|
||||||
"enum": Array [],
|
|
||||||
"example": undefined,
|
"example": undefined,
|
||||||
"externalDocs": undefined,
|
"expanded": undefined,
|
||||||
"format": undefined,
|
"explode": false,
|
||||||
"isCircular": undefined,
|
"in": undefined,
|
||||||
"isPrimitive": true,
|
"kind": "field",
|
||||||
"nullable": false,
|
"name": "type",
|
||||||
"options": "<<<filtered>>>",
|
"required": true,
|
||||||
"pattern": undefined,
|
"schema": SchemaModel {
|
||||||
"pointer": "#/components/schemas/Dog/properties/type",
|
"activeOneOf": 0,
|
||||||
"rawSchema": Object {
|
"constraints": Array [],
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "",
|
||||||
|
"displayFormat": undefined,
|
||||||
|
"displayType": "string",
|
||||||
|
"enum": Array [],
|
||||||
|
"example": undefined,
|
||||||
|
"externalDocs": undefined,
|
||||||
|
"format": undefined,
|
||||||
|
"isCircular": undefined,
|
||||||
|
"isPrimitive": true,
|
||||||
|
"nullable": false,
|
||||||
|
"options": "<<<filtered>>>",
|
||||||
|
"pattern": undefined,
|
||||||
|
"pointer": "#/components/schemas/Dog/properties/type",
|
||||||
|
"rawSchema": Object {
|
||||||
|
"default": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"readOnly": false,
|
||||||
|
"schema": Object {
|
||||||
|
"default": undefined,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"title": "",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"typePrefix": "",
|
||||||
|
"writeOnly": false,
|
||||||
},
|
},
|
||||||
"readOnly": false,
|
}
|
||||||
"schema": Object {
|
|
||||||
"default": undefined,
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
"title": "",
|
|
||||||
"type": "string",
|
|
||||||
"typePrefix": "",
|
|
||||||
"writeOnly": false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
isLast={true}
|
||||||
isLast={true}
|
key="type"
|
||||||
key="type"
|
renderDiscriminatorSwitch={[Function]}
|
||||||
renderDiscriminatorSwitch={[Function]}
|
showExamples={false}
|
||||||
showExamples={false}
|
/>
|
||||||
/>
|
</tbody>
|
||||||
</tbody>
|
</styled.table>
|
||||||
</styled.table>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('Models', () => {
|
||||||
const spec = require('../fixtures/discriminator.json');
|
const spec = require('../fixtures/discriminator.json');
|
||||||
parser = new OpenAPIParser(spec, undefined, opts);
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
const schema = new SchemaModel(parser, spec.components.schemas.Foo, '', opts);
|
const schema = new SchemaModel(parser, spec.components.schemas.Foo, '', opts);
|
||||||
expect(schema.oneOf).toHaveLength(1);
|
expect(schema.oneOf).toHaveLength(0);
|
||||||
expect(schema.discriminatorProp).toEqual('type');
|
expect(schema.discriminatorProp).toEqual('type');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import * as Sampler from 'openapi-sampler';
|
import * as Sampler from 'openapi-sampler';
|
||||||
|
|
||||||
import { OpenAPIMediaType } from '../../types';
|
import { OpenAPIMediaType } from '../../types';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
|
||||||
import { SchemaModel } from './Schema';
|
|
||||||
|
|
||||||
import { isJsonLike, mapValues } from '../../utils';
|
import { isJsonLike, mapValues } from '../../utils';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { ExampleModel } from './Example';
|
import { ExampleModel } from './Example';
|
||||||
|
import { SchemaModel } from './Schema';
|
||||||
|
|
||||||
export class MediaTypeModel {
|
export class MediaTypeModel {
|
||||||
examples?: { [name: string]: ExampleModel };
|
examples?: { [name: string]: ExampleModel };
|
||||||
|
@ -54,7 +52,7 @@ export class MediaTypeModel {
|
||||||
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
|
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
|
||||||
skipWriteOnly: !this.isRequestType,
|
skipWriteOnly: !this.isRequestType,
|
||||||
};
|
};
|
||||||
if (this.schema && this.schema.oneOf) {
|
if (this.schema && this.schema.oneOf && this.schema.oneOf.length > 0) {
|
||||||
this.examples = {};
|
this.examples = {};
|
||||||
for (const subSchema of this.schema.oneOf) {
|
for (const subSchema of this.schema.oneOf) {
|
||||||
const sample = Sampler.sample(subSchema.rawSchema, samplerOptions, parser.spec);
|
const sample = Sampler.sample(subSchema.rawSchema, samplerOptions, parser.spec);
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import { action, observable } from 'mobx';
|
import { action, observable } from 'mobx';
|
||||||
|
|
||||||
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
|
|
||||||
|
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
|
||||||
import { FieldModel } from './Field';
|
|
||||||
|
|
||||||
import { MergedOpenAPISchema } from '../';
|
import { MergedOpenAPISchema } from '../';
|
||||||
|
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
|
||||||
import {
|
import {
|
||||||
detectType,
|
detectType,
|
||||||
extractExtensions,
|
extractExtensions,
|
||||||
|
@ -18,8 +12,10 @@ import {
|
||||||
sortByField,
|
sortByField,
|
||||||
sortByRequired,
|
sortByRequired,
|
||||||
} from '../../utils/';
|
} from '../../utils/';
|
||||||
|
|
||||||
import { l } from '../Labels';
|
import { l } from '../Labels';
|
||||||
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
|
import { FieldModel } from './Field';
|
||||||
|
|
||||||
// TODO: refactor this model, maybe use getters instead of copying all the values
|
// TODO: refactor this model, maybe use getters instead of copying all the values
|
||||||
export class SchemaModel {
|
export class SchemaModel {
|
||||||
|
@ -225,7 +221,7 @@ export class SchemaModel {
|
||||||
const discriminator = getDiscriminator(schema)!;
|
const discriminator = getDiscriminator(schema)!;
|
||||||
this.discriminatorProp = discriminator.propertyName;
|
this.discriminatorProp = discriminator.propertyName;
|
||||||
const implicitInversedMapping = parser.findDerived([
|
const implicitInversedMapping = parser.findDerived([
|
||||||
...(schema.parentRefs || []),
|
...[], //...(schema.parentRefs || []), // including parentRefs in all cases is wrong (it is correct for a class with subclasses, but not for one, which only has a superclass)
|
||||||
this.pointer,
|
this.pointer,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -312,6 +308,8 @@ export class SchemaModel {
|
||||||
innerSchema.title = name;
|
innerSchema.title = name;
|
||||||
return innerSchema;
|
return innerSchema;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.fields = buildFields(parser, schema, this.pointer, this.options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user