feat: support multiple examples for parameters (#1470)

This commit is contained in:
Roman Hotsiy 2020-11-23 14:00:17 +02:00 committed by GitHub
parent 340a568262
commit d12e410d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 10 deletions

View File

@ -9,6 +9,8 @@ import {
TypePrefix,
TypeTitle,
ToggleButton,
FieldLabel,
ExampleValue,
} from '../../common-elements/fields';
import { serializeParameterValue } from '../../utils/openapi';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
@ -23,6 +25,8 @@ import { Badge } from '../../common-elements/';
import { l } from '../../services/Labels';
import { OptionsContext } from '../OptionsProvider';
import { FieldModel } from '../../services/models/Field';
import styled from '../../styled-components';
const MAX_PATTERN_LENGTH = 45;
@ -44,20 +48,19 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
const { patternShown } = this.state;
const { enumSkipQuotes, hideSchemaTitles } = this.context;
const { schema, description, example, deprecated } = field;
const { schema, description, example, deprecated, examples } = field;
const rawDefault = !!enumSkipQuotes || field.in === 'header'; // having quotes around header field default values is confusing and inappropriate
let exampleField: JSX.Element | null = null;
let renderedExamples: JSX.Element | null = null;
if (showExamples && example !== undefined) {
const label = l('example') + ':';
if (field.in && (field.style || field.serializationMime)) {
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
const serializedValue = decodeURIComponent(serializeParameterValue(field, example));
exampleField = <FieldDetail label={label} value={serializedValue} raw={true} />;
if (showExamples && (example !== undefined || examples !== undefined)) {
if (examples !== undefined) {
renderedExamples = <Examples field={field} />;
} else {
exampleField = <FieldDetail label={label} value={example} />;
const label = l('example') + ':';
const raw = !!field.in;
renderedExamples = <FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />;
}
}
@ -100,7 +103,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
)}
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={schema.default} />
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
{exampleField}
{renderedExamples}
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
<div>
<Markdown compact={true} source={description} />
@ -113,3 +116,40 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
);
}
}
function Examples({ field }: { field: FieldModel }) {
if (!field.examples) {
return null;
}
return (
<>
<FieldLabel> {l('examples')}: </FieldLabel>
<ExamplesList>
{Object.values(field.examples).map((example, idx) => {
return (
<li key={idx}>
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> - {example.summary || example.description}
</li>
);
})}
</ExamplesList>
</>
);
}
function getSerializedValue(field: FieldModel, example: any) {
if (field.in) {
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
return decodeURIComponent(serializeParameterValue(field, example));
} else {
return example;
}
}
const ExamplesList = styled.ul`
margin-top: 1em;
padding-left: 0;
list-style-position: inside;
`;

View File

@ -5,6 +5,7 @@ export interface LabelsConfig {
default: string;
deprecated: string;
example: string;
examples: string;
nullable: string;
recursive: string;
arrayOf: string;
@ -20,6 +21,7 @@ const labels: LabelsConfig = {
default: 'Default',
deprecated: 'Deprecated',
example: 'Example',
examples: 'Examples',
nullable: 'Nullable',
recursive: 'Recursive',
arrayOf: 'Array of ',

View File

@ -11,6 +11,8 @@ import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import { extractExtensions } from '../../utils/openapi';
import { OpenAPIParser } from '../OpenAPIParser';
import { SchemaModel } from './Schema';
import { ExampleModel } from './Example';
import { mapValues } from '../../utils/helpers';
const DEFAULT_SERIALIZATION: Record<
OpenAPIParameterLocation,
@ -46,6 +48,7 @@ export class FieldModel {
required: boolean;
description: string;
example?: string;
examples?: Record<string, ExampleModel>;
deprecated: boolean;
in?: OpenAPIParameterLocation;
kind: string;
@ -81,6 +84,13 @@ export class FieldModel {
info.description === undefined ? this.schema.description || '' : info.description;
this.example = info.example || this.schema.example;
if (info.examples !== undefined) {
this.examples = mapValues(
info.examples,
example => new ExampleModel(parser, example, name, info.encoding),
);
}
if (serializationMime) {
this.serializationMime = serializationMime;
} else if (info.style) {

View File

@ -95,6 +95,7 @@ export interface OpenAPIParameter {
example?: any;
examples?: { [media: string]: Referenced<OpenAPIExample> };
content?: { [media: string]: OpenAPIMediaType };
encoding?: Record<string, OpenAPIEncoding>;
}
export interface OpenAPIExample {