mirror of
https://github.com/Redocly/redoc.git
synced 2025-02-20 11:50:32 +03:00
FBI-451: Provide codeSamplesLanguage
as an option
This commit is contained in:
parent
18aadd5497
commit
d1b54be4c5
12
README.md
12
README.md
|
@ -251,6 +251,7 @@ You can use all of the following options with the standalone version of the <red
|
|||
* **path-only**: displays a path in the sidebar navigation item.
|
||||
* **id-only**: displays the operation id with a fallback to the path in the sidebar navigation item.
|
||||
* `showWebhookVerb` - when set to `true`, shows the HTTP request method for webhooks in operations and in the sidebar.
|
||||
* `codeSamplesLanguage` - enables code sample generation for the provided list of languages.
|
||||
|
||||
### `<redoc>` theme object
|
||||
* `spacing`
|
||||
|
@ -324,6 +325,17 @@ You can use all of the following options with the standalone version of the <red
|
|||
* `backgroundColor`: '#263238'
|
||||
* `color`: '#ffffff'
|
||||
|
||||
## Auto generated code samples
|
||||
A new parameter called `codeSamplesLanguages` was added to `options` Object to provide code sample generation. You can pass an array like this to enable all languages supported by the code generation:
|
||||
|
||||
```javascript
|
||||
['json','xml']
|
||||
```
|
||||
|
||||
Where `['json']` is provided by default.
|
||||
|
||||
When the `x-codeSamples` and `x-code-samples` are not set, it will
|
||||
automatically generate the code samples based on the language(s) you set.
|
||||
-----------
|
||||
## Development
|
||||
see [CONTRIBUTING.md](.github/CONTRIBUTING.md)
|
||||
|
|
|
@ -11,6 +11,10 @@ const userUrl = window.location.search.match(/url=(.*)$/);
|
|||
const specUrl =
|
||||
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
|
||||
|
||||
const options: RedocRawOptions = { nativeScrollbars: false, maxDisplayedEnumValues: 3 };
|
||||
const options: RedocRawOptions = {
|
||||
nativeScrollbars: false,
|
||||
maxDisplayedEnumValues: 3,
|
||||
codeSamplesLanguages: ['json', 'xml'],
|
||||
};
|
||||
|
||||
render(<RedocStandalone specUrl={specUrl} options={options} />, document.getElementById('example'));
|
||||
|
|
|
@ -76,6 +76,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -347,6 +350,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -605,6 +611,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -925,6 +934,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -1208,6 +1220,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -1462,6 +1477,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -1741,6 +1759,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
],
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -2050,6 +2071,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -2321,6 +2345,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
@ -2579,6 +2606,9 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
|
|||
"minItems": undefined,
|
||||
"options": RedocNormalizedOptions {
|
||||
"allowedMdComponents": Object {},
|
||||
"codeSamplesLanguages": Array [
|
||||
"json",
|
||||
],
|
||||
"disableSearch": false,
|
||||
"downloadDefinitionUrl": undefined,
|
||||
"downloadFileName": undefined,
|
||||
|
|
|
@ -6,6 +6,8 @@ import { setRedocLabels } from './Labels';
|
|||
import { SideNavStyleEnum } from './types';
|
||||
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
|
||||
|
||||
export type CodeSamplesLanguage = 'json' | 'xml';
|
||||
|
||||
export interface RedocRawOptions {
|
||||
theme?: ThemeInterface;
|
||||
scrollYOffset?: number | string | (() => number);
|
||||
|
@ -56,6 +58,7 @@ export interface RedocRawOptions {
|
|||
hideFab?: boolean;
|
||||
minCharacterLengthToInitSearch?: number;
|
||||
showWebhookVerb?: boolean;
|
||||
codeSamplesLanguages?: CodeSamplesLanguage[];
|
||||
}
|
||||
|
||||
export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
|
||||
|
@ -211,6 +214,16 @@ export class RedocNormalizedOptions {
|
|||
return 10;
|
||||
}
|
||||
|
||||
private static normalizeCodeSamplesLanguages(
|
||||
value?: CodeSamplesLanguage[],
|
||||
): CodeSamplesLanguage[] {
|
||||
if (isArray(value)) {
|
||||
return value.map(lang => lang.toLowerCase()) as CodeSamplesLanguage[];
|
||||
}
|
||||
|
||||
return ['json'];
|
||||
}
|
||||
|
||||
theme: ResolvedThemeInterface;
|
||||
scrollYOffset: () => number;
|
||||
hideHostname: boolean;
|
||||
|
@ -258,6 +271,7 @@ export class RedocNormalizedOptions {
|
|||
showWebhookVerb: boolean;
|
||||
|
||||
nonce?: string;
|
||||
codeSamplesLanguages: CodeSamplesLanguage[];
|
||||
|
||||
constructor(raw: RedocRawOptions, defaults: RedocRawOptions = {}) {
|
||||
raw = { ...defaults, ...raw };
|
||||
|
@ -335,5 +349,8 @@ export class RedocNormalizedOptions {
|
|||
this.hideFab = argValueToBoolean(raw.hideFab);
|
||||
this.minCharacterLengthToInitSearch = argValueToNumber(raw.minCharacterLengthToInitSearch) || 3;
|
||||
this.showWebhookVerb = argValueToBoolean(raw.showWebhookVerb);
|
||||
this.codeSamplesLanguages = RedocNormalizedOptions.normalizeCodeSamplesLanguages(
|
||||
raw.codeSamplesLanguages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { OpenAPIMediaType } from '../../types';
|
|||
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||
import { SchemaModel } from './Schema';
|
||||
|
||||
import { isJsonLike, isXml, mapValues } from '../../utils';
|
||||
import { isXml, mapValues } from '../../utils';
|
||||
import type { OpenAPIParser } from '../OpenAPIParser';
|
||||
import { ExampleModel } from './Example';
|
||||
import { ConfigAccessOptions, FinalExamples, generateXmlExample } from '../../utils/xml';
|
||||
|
@ -34,6 +34,9 @@ export class MediaTypeModel {
|
|||
this.schema = info.schema && new SchemaModel(parser, info.schema, '', options);
|
||||
this.onlyRequiredInSamples = options.onlyRequiredInSamples;
|
||||
this.generatedPayloadSamplesMaxDepth = options.generatedPayloadSamplesMaxDepth;
|
||||
const isCodeGenerationSupported = options.codeSamplesLanguages.some(lang =>
|
||||
name.toLowerCase().includes(lang),
|
||||
);
|
||||
if (info.examples !== undefined) {
|
||||
this.examples = mapValues(
|
||||
info.examples,
|
||||
|
@ -48,7 +51,7 @@ export class MediaTypeModel {
|
|||
info.encoding,
|
||||
),
|
||||
};
|
||||
} else if (isJsonLike(name) || isXml(name)) {
|
||||
} else if (isCodeGenerationSupported) {
|
||||
this.generateExample(parser, info);
|
||||
}
|
||||
}
|
||||
|
@ -78,8 +81,9 @@ export class MediaTypeModel {
|
|||
this.name,
|
||||
info.encoding,
|
||||
);
|
||||
if (isXml(this.name)) {
|
||||
const xmlExamples = this.resolveXmlExample(parser, sample as OpenAPIExample);
|
||||
|
||||
const xmlExamples = this.resolveXmlExample(parser, sample as OpenAPIExample);
|
||||
if (xmlExamples[0]) {
|
||||
this.examples[subSchema.title].value = xmlExamples[0]?.exampleValue;
|
||||
}
|
||||
}
|
||||
|
|
85
src/utils/jsonToXml.ts
Normal file
85
src/utils/jsonToXml.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @license
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Mrinmoy Majumdar
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* json2xml
|
||||
* @example
|
||||
* Schema: {
|
||||
* 'prop1' : 'one',
|
||||
* 'prop2' : 'two',
|
||||
* 'prop3' : [ 'a', 'b', 'c' ],
|
||||
* 'prop4' : {
|
||||
* 'ob1' : 'val-1',
|
||||
* 'ob2' : 'val-2'
|
||||
* }
|
||||
* }
|
||||
* XML:
|
||||
* <root>
|
||||
* <prop1>simple</prop1>
|
||||
* <prop2>
|
||||
* <0>a</0>
|
||||
* <1>b</1>
|
||||
* <2>c</2>
|
||||
* </prop2>
|
||||
* <prop3>
|
||||
* <ob1>val-1</ob1>
|
||||
* <ob2>val-2</ob2>
|
||||
* </prop3>
|
||||
* </root>
|
||||
**/
|
||||
export const json2xml = (obj: any, level: number = 1): string => {
|
||||
const indent = ' '.repeat(level);
|
||||
let xmlText = '';
|
||||
if (level === 1 && typeof obj !== 'object') {
|
||||
return `\n${indent}${obj.toString()}`;
|
||||
}
|
||||
for (const prop in obj) {
|
||||
const tagNameOrProp = obj[prop]?.['::XML_TAG'] || prop;
|
||||
let tagName = '';
|
||||
if (Array.isArray(obj[prop])) {
|
||||
tagName = tagNameOrProp[0]?.['::XML_TAG'] || `${prop}`;
|
||||
} else {
|
||||
tagName = tagNameOrProp;
|
||||
}
|
||||
if (prop.startsWith('::')) {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(obj[prop])) {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(
|
||||
obj[prop],
|
||||
level + 1,
|
||||
)}\n${indent}</${tagName}>`;
|
||||
} else if (typeof obj[prop] === 'object') {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(
|
||||
obj[prop],
|
||||
level + 1,
|
||||
)}\n${indent}</${tagName}>`;
|
||||
} else {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${obj[prop].toString()}</${tagName}>`;
|
||||
}
|
||||
}
|
||||
return xmlText;
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
import { MergedOpenAPISchema } from '../services';
|
||||
import { OpenAPISchema } from '../types';
|
||||
import { json2xml } from './jsonToXml';
|
||||
|
||||
export interface ConfigAccessOptions {
|
||||
includeReadOnly?: boolean;
|
||||
|
@ -22,66 +23,6 @@ export interface FinalExamples {
|
|||
exampleValue: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* json2xml
|
||||
* @example
|
||||
* Schema: {
|
||||
* 'prop1' : 'one',
|
||||
* 'prop2' : 'two',
|
||||
* 'prop3' : [ 'a', 'b', 'c' ],
|
||||
* 'prop4' : {
|
||||
* 'ob1' : 'val-1',
|
||||
* 'ob2' : 'val-2'
|
||||
* }
|
||||
* }
|
||||
* XML:
|
||||
* <root>
|
||||
* <prop1>simple</prop1>
|
||||
* <prop2>
|
||||
* <0>a</0>
|
||||
* <1>b</1>
|
||||
* <2>c</2>
|
||||
* </prop2>
|
||||
* <prop3>
|
||||
* <ob1>val-1</ob1>
|
||||
* <ob2>val-2</ob2>
|
||||
* </prop3>
|
||||
* </root>
|
||||
**/
|
||||
const json2xml = (obj: any, level: number = 1): string => {
|
||||
const indent = ' '.repeat(level);
|
||||
let xmlText = '';
|
||||
if (level === 1 && typeof obj !== 'object') {
|
||||
return `\n${indent}${obj.toString()}`;
|
||||
}
|
||||
for (const prop in obj) {
|
||||
const tagNameOrProp = obj[prop]?.['::XML_TAG'] || prop;
|
||||
let tagName = '';
|
||||
if (Array.isArray(obj[prop])) {
|
||||
tagName = tagNameOrProp[0]?.['::XML_TAG'] || `${prop}`;
|
||||
} else {
|
||||
tagName = tagNameOrProp;
|
||||
}
|
||||
if (prop.startsWith('::')) {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(obj[prop])) {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(
|
||||
obj[prop],
|
||||
level + 1,
|
||||
)}\n${indent}</${tagName}>`;
|
||||
} else if (typeof obj[prop] === 'object') {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${json2xml(
|
||||
obj[prop],
|
||||
level + 1,
|
||||
)}\n${indent}</${tagName}>`;
|
||||
} else {
|
||||
xmlText = `${xmlText}\n${indent}<${tagName}>${obj[prop].toString()}</${tagName}>`;
|
||||
}
|
||||
}
|
||||
return xmlText;
|
||||
};
|
||||
|
||||
const mergePropertyExamples = (
|
||||
obj: { [x: string]: any },
|
||||
propertyName: string,
|
||||
|
|
Loading…
Reference in New Issue
Block a user