mirror of
https://github.com/Redocly/redoc.git
synced 2025-02-12 16:00:33 +03:00
Merge branch 'master' into update/toggleMenuItem
This commit is contained in:
commit
bfd703d2da
11
.travis.yml
11
.travis.yml
|
@ -1,7 +1,11 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '8'
|
- '10'
|
||||||
cache: yarn
|
cache:
|
||||||
|
yarn: true
|
||||||
|
directories:
|
||||||
|
# we also need to cache folder with Cypress binary
|
||||||
|
- ~/.cache
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- GH_REF: github.com/Redocly/redoc.git
|
- GH_REF: github.com/Redocly/redoc.git
|
||||||
|
@ -14,6 +18,9 @@ env:
|
||||||
- secure: SEqTg6WoGPPpcWzJ03ZfcSBb3nZ2Mdhug0ec2PszuzYO3libCb9usiqi+jils9z6qyXsL6ecz8HYazDGOUepnubhIpI5otLgfn9XiapjMT06Bj//AjbKpH7eu3TJSpJMzoRHZrKIE1y9ZKIBqKwl9Xs7ko+1oa+MLhrLuxXkoi0JqRB5UzkQtJRDoxVNjysnLQn+hsfnm+yuqPHZd2+Loy++q//WHuf9bwJrlkXn2ICYQIX5oQGlxNO6ui+OZklb0YknvyO5GdQeoKaHYru3MMKKCIS6I7AG9wLmPs5Ou3T0Ia0Xx4/7xazs0rH4NCVpIceSYc3v6evR37pp8MsFTC3BzjL1V3slTnmitC1KSNM8ndGRUg1nsCBkJysnR3HpX6SHuCH+UzOuMxEjwiPdSRnzJPEbTHa1HqMfTkTJMbm4zhp7W4/ozX4TtjUB0ql6NoQE2n0Z3aYgR2C78TmzaPQun8EgredWnCID1FedyexaNcw4HyZ2rXlcvG3rBzSwLHH5PePT9skyqy6KtIaL0MlAP556ilgUeyCZfCNdTmzCvPDZuqaeLRezWDdsKnRfTkxIW80QWlmZ6sW0hynJV5JN2Oghk9Tr+QzgV4ZF68FHwoU9YXCTyX4w5iTYq/GjvfTBqB3VSGPOz3PwU7r47tmaYzPj+I44zqktgxyuxDo=
|
- secure: SEqTg6WoGPPpcWzJ03ZfcSBb3nZ2Mdhug0ec2PszuzYO3libCb9usiqi+jils9z6qyXsL6ecz8HYazDGOUepnubhIpI5otLgfn9XiapjMT06Bj//AjbKpH7eu3TJSpJMzoRHZrKIE1y9ZKIBqKwl9Xs7ko+1oa+MLhrLuxXkoi0JqRB5UzkQtJRDoxVNjysnLQn+hsfnm+yuqPHZd2+Loy++q//WHuf9bwJrlkXn2ICYQIX5oQGlxNO6ui+OZklb0YknvyO5GdQeoKaHYru3MMKKCIS6I7AG9wLmPs5Ou3T0Ia0Xx4/7xazs0rH4NCVpIceSYc3v6evR37pp8MsFTC3BzjL1V3slTnmitC1KSNM8ndGRUg1nsCBkJysnR3HpX6SHuCH+UzOuMxEjwiPdSRnzJPEbTHa1HqMfTkTJMbm4zhp7W4/ozX4TtjUB0ql6NoQE2n0Z3aYgR2C78TmzaPQun8EgredWnCID1FedyexaNcw4HyZ2rXlcvG3rBzSwLHH5PePT9skyqy6KtIaL0MlAP556ilgUeyCZfCNdTmzCvPDZuqaeLRezWDdsKnRfTkxIW80QWlmZ6sW0hynJV5JN2Oghk9Tr+QzgV4ZF68FHwoU9YXCTyX4w5iTYq/GjvfTBqB3VSGPOz3PwU7r47tmaYzPj+I44zqktgxyuxDo=
|
||||||
addons:
|
addons:
|
||||||
chrome: stable
|
chrome: stable
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libgconf-2-4 # for cypress
|
||||||
before_script: npm run bundle
|
before_script: npm run bundle
|
||||||
script: npm test && ([ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm
|
script: npm test && ([ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm
|
||||||
run e2e)
|
run e2e)
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
||||||
|
# [2.0.0-rc.10](https://github.com/Redocly/redoc/compare/v2.0.0-rc.9...v2.0.0-rc.10) (2019-07-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* broken headings with single quote ([51d3b9b](https://github.com/Redocly/redoc/commit/51d3b9b)), closes [#955](https://github.com/Redocly/redoc/issues/955)
|
||||||
|
* fix fields table overflow if deeply nested with long title ([12b7057](https://github.com/Redocly/redoc/commit/12b7057))
|
||||||
|
* hide empty example when it is not defined ([4bd499f](https://github.com/Redocly/redoc/commit/4bd499f))
|
||||||
|
* markdown in examples descriptions + minor ui tweaks ([f52d9e8](https://github.com/Redocly/redoc/commit/f52d9e8))
|
||||||
|
* organize response examples in dropdown and display description ([995e557](https://github.com/Redocly/redoc/commit/995e557))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [2.0.0-rc.9](https://github.com/Redocly/redoc/compare/v2.0.0-rc.8-1...v2.0.0-rc.9) (2019-06-27)
|
# [2.0.0-rc.9](https://github.com/Redocly/redoc/compare/v2.0.0-rc.8-1...v2.0.0-rc.9) (2019-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -243,6 +243,7 @@ You can use all of the following options with standalone version on <redoc> tag
|
||||||
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button.
|
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button.
|
||||||
* `disableSearch` - disable search indexing and search box
|
* `disableSearch` - disable search indexing and search box
|
||||||
* `onlyRequiredInSamples` - shows only required fields in request samples.
|
* `onlyRequiredInSamples` - shows only required fields in request samples.
|
||||||
|
* `jsonSampleExpandLevel` - set the default expand level for JSON payload samples (responses and request body). Special value 'all' expands all levels. The default value is `2`.
|
||||||
* `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Redocly/redoc/blob/master/src/theme.ts)
|
* `theme` - ReDoc theme. Not documented yet. For details check source code: [theme.ts](https://github.com/Redocly/redoc/blob/master/src/theme.ts)
|
||||||
|
|
||||||
## Advanced usage of standalone version
|
## Advanced usage of standalone version
|
||||||
|
|
|
@ -156,7 +156,9 @@ async function serve(port: number, pathToSpec: string, options: Options = {}) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (request.url === '/') {
|
} else if (request.url === '/') {
|
||||||
respondWithGzip(pageHTML, request, response);
|
respondWithGzip(pageHTML, request, response, {
|
||||||
|
'Content-Type': 'text/html',
|
||||||
|
});
|
||||||
} else if (request.url === '/spec.json') {
|
} else if (request.url === '/spec.json') {
|
||||||
const specStr = JSON.stringify(spec, null, 2);
|
const specStr = JSON.stringify(spec, null, 2);
|
||||||
respondWithGzip(specStr, request, response, {
|
respondWithGzip(specStr, request, response, {
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
{
|
{
|
||||||
"name": "redoc-cli",
|
"name": "redoc-cli",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "ReDoc's Command Line Interface",
|
"description": "ReDoc's Command Line Interface",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": "index.js",
|
"bin": "index.js",
|
||||||
"repository": "https://github.com/Redocly/redoc",
|
"repository": "https://github.com/Redocly/redoc",
|
||||||
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^2.0.4",
|
"chokidar": "^3.0.2",
|
||||||
"handlebars": "^4.0.11",
|
"handlebars": "^4.1.2",
|
||||||
"isarray": "^2.0.4",
|
"isarray": "^2.0.4",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mobx": "^4.2.0",
|
"mobx": "^4.2.0",
|
||||||
"node-libs-browser": "^2.2.0",
|
"node-libs-browser": "^2.2.1",
|
||||||
"react": "^16.8.4",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.4",
|
"react-dom": "^16.8.6",
|
||||||
"redoc": "^2.0.0-rc.8-1",
|
"redoc": "^2.0.0-rc.10",
|
||||||
"styled-components": "^4.1.3",
|
"styled-components": "^4.3.2",
|
||||||
"tslib": "^1.9.3",
|
"tslib": "^1.10.0",
|
||||||
"yargs": "^12.0.5"
|
"yargs": "^13.2.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ci-publish": "ci-publish"
|
"ci-publish": "ci-publish"
|
||||||
|
@ -28,7 +31,7 @@
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chokidar": "^1.7.5",
|
"@types/chokidar": "^2.1.3",
|
||||||
"@types/handlebars": "^4.0.39",
|
"@types/handlebars": "^4.0.39",
|
||||||
"@types/mkdirp": "^0.5.2",
|
"@types/mkdirp": "^0.5.2",
|
||||||
"ci-publish": "^1.3.1"
|
"ci-publish": "^1.3.1"
|
||||||
|
|
1222
cli/yarn.lock
1222
cli/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -38,7 +38,7 @@ info:
|
||||||
OAuth2 - an open protocol to allow secure authorization in a simple
|
OAuth2 - an open protocol to allow secure authorization in a simple
|
||||||
and standard method from web, mobile and desktop applications.
|
and standard method from web, mobile and desktop applications.
|
||||||
|
|
||||||
<security-definitions />
|
<SecurityDefinitions />
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
title: Swagger Petstore
|
title: Swagger Petstore
|
||||||
|
@ -63,6 +63,14 @@ tags:
|
||||||
description: Access to Petstore orders
|
description: Access to Petstore orders
|
||||||
- name: user
|
- name: user
|
||||||
description: Operations about user
|
description: Operations about user
|
||||||
|
- name: pet_model
|
||||||
|
x-displayName: The Pet Model
|
||||||
|
description: |
|
||||||
|
<ObjectDescription schemaRef="#/components/schemas/Pet" />
|
||||||
|
- name: store_model
|
||||||
|
x-displayName: The Order Model
|
||||||
|
description: |
|
||||||
|
<ObjectDescription schemaRef="#/components/schemas/Order" exampleRef="#/components/examples/Order" showReadOnly={true} showWriteOnly={true} />
|
||||||
x-tagGroups:
|
x-tagGroups:
|
||||||
- name: General
|
- name: General
|
||||||
tags:
|
tags:
|
||||||
|
@ -71,6 +79,10 @@ x-tagGroups:
|
||||||
- name: User Management
|
- name: User Management
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
- name: Models
|
||||||
|
tags:
|
||||||
|
- pet_model
|
||||||
|
- store_model
|
||||||
paths:
|
paths:
|
||||||
/pet:
|
/pet:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -754,6 +766,11 @@ components:
|
||||||
description: Indicates whenever order was completed or not
|
description: Indicates whenever order was completed or not
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
readOnly: true
|
||||||
|
rqeuestId:
|
||||||
|
description: Unique Request Id
|
||||||
|
type: string
|
||||||
|
writeOnly: true
|
||||||
xml:
|
xml:
|
||||||
name: Order
|
name: Order
|
||||||
Pet:
|
Pet:
|
||||||
|
@ -926,3 +943,10 @@ components:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
name: api_key
|
name: api_key
|
||||||
in: header
|
in: header
|
||||||
|
examples:
|
||||||
|
Order:
|
||||||
|
value:
|
||||||
|
quantity: 1,
|
||||||
|
shipDate: 2018-10-19T16:46:45Z,
|
||||||
|
status: placed,
|
||||||
|
complete: false
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe('Menu', () => {
|
||||||
it('should have valid items count', () => {
|
it('should have valid items count', () => {
|
||||||
cy.get('.menu-content')
|
cy.get('.menu-content')
|
||||||
.find('li')
|
.find('li')
|
||||||
.should('have.length', 6 + (2 + 8 + 4) + (1 + 8));
|
.should('have.length', 6 + (2 + 8 + 1 + 4 + 2) + (1 + 8));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync active menu items while scroll', () => {
|
it('should sync active menu items while scroll', () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-rc.9",
|
"version": "2.0.0-rc.10",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"start": "webpack-dev-server --mode=development --env.playground --hot --config demo/webpack.config.ts",
|
"start": "webpack-dev-server --mode=development --env.playground --hot --config demo/webpack.config.ts",
|
||||||
"start:prod": "webpack-dev-server --env.playground --mode=production --config demo/webpack.config.ts",
|
"start:prod": "webpack-dev-server --env.playground --mode=production --config demo/webpack.config.ts",
|
||||||
"start:benchmark": "webpack-dev-server --mode=production --env.bench --config demo/webpack.config.ts",
|
"start:benchmark": "webpack-dev-server --mode=production --env.bench --config demo/webpack.config.ts",
|
||||||
"test": "npm run lint && npm run unit && npm run bundlesize && npm run license-check",
|
"test": "npm run lint && npm run unit && npm run license-check",
|
||||||
"unit": "jest --coverage",
|
"unit": "jest --coverage",
|
||||||
"e2e": "cypress run",
|
"e2e": "cypress run",
|
||||||
"e2e-ci": "cypress run --record",
|
"e2e-ci": "cypress run --record",
|
||||||
|
|
|
@ -5,11 +5,7 @@ import { ClipboardService } from '../services/ClipboardService';
|
||||||
|
|
||||||
export interface CopyButtonWrapperProps {
|
export interface CopyButtonWrapperProps {
|
||||||
data: any;
|
data: any;
|
||||||
children: (
|
children: (props: { renderCopyButton: () => React.ReactNode }) => React.ReactNode;
|
||||||
props: {
|
|
||||||
renderCopyButton: (() => React.ReactNode);
|
|
||||||
},
|
|
||||||
) => React.ReactNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CopyButtonWrapper extends React.PureComponent<
|
export class CopyButtonWrapper extends React.PureComponent<
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const StyledDropdown = styled(Dropdown)`
|
||||||
display: block;
|
display: block;
|
||||||
height: 0;
|
height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.35em;
|
right: 0.6em;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -0.125em;
|
margin-top: -0.125em;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
|
@ -32,6 +32,7 @@ export const TypeName = styled(FieldLabel)`
|
||||||
|
|
||||||
export const TypeTitle = styled(FieldLabel)`
|
export const TypeTitle = styled(FieldLabel)`
|
||||||
color: ${props => props.theme.schema.typeTitleColor};
|
color: ${props => props.theme.schema.typeTitleColor};
|
||||||
|
word-break: break-word;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TypeFormat = TypeName;
|
export const TypeFormat = TypeName;
|
||||||
|
|
|
@ -44,8 +44,7 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const website =
|
const website =
|
||||||
(info.contact &&
|
(info.contact && info.contact.url && (
|
||||||
info.contact.url && (
|
|
||||||
<InfoSpan>
|
<InfoSpan>
|
||||||
URL: <a href={info.contact.url}>{info.contact.url}</a>
|
URL: <a href={info.contact.url}>{info.contact.url}</a>
|
||||||
</InfoSpan>
|
</InfoSpan>
|
||||||
|
@ -53,8 +52,7 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const email =
|
const email =
|
||||||
(info.contact &&
|
(info.contact && info.contact.email && (
|
||||||
info.contact.email && (
|
|
||||||
<InfoSpan>
|
<InfoSpan>
|
||||||
{info.contact.name || 'E-mail'}:{' '}
|
{info.contact.name || 'E-mail'}:{' '}
|
||||||
<a href={'mailto:' + info.contact.email}>{info.contact.email}</a>
|
<a href={'mailto:' + info.contact.email}>{info.contact.email}</a>
|
||||||
|
@ -70,11 +68,7 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
)) ||
|
)) ||
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const version =
|
const version = (info.version && <span>({info.version})</span>) || null;
|
||||||
(info.version && (
|
|
||||||
<span>({info.version})</span>
|
|
||||||
)) ||
|
|
||||||
null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Markdown } from '../Markdown/Markdown';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
||||||
|
|
||||||
import { getBasePath } from '../../utils';
|
import { expandDefaultServerVariables, getBasePath } from '../../utils';
|
||||||
import {
|
import {
|
||||||
EndpointInfo,
|
EndpointInfo,
|
||||||
HttpVerb,
|
HttpVerb,
|
||||||
|
@ -60,21 +60,24 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
/>
|
/>
|
||||||
</EndpointInfo>
|
</EndpointInfo>
|
||||||
<ServersOverlay expanded={expanded}>
|
<ServersOverlay expanded={expanded}>
|
||||||
{operation.servers.map(server => (
|
{operation.servers.map(server => {
|
||||||
<ServerItem key={server.url}>
|
const normalizedUrl = expandDefaultServerVariables(server.url, server.variables);
|
||||||
|
return (
|
||||||
|
<ServerItem key={normalizedUrl}>
|
||||||
<Markdown source={server.description || ''} compact={true} />
|
<Markdown source={server.description || ''} compact={true} />
|
||||||
<SelectOnClick>
|
<SelectOnClick>
|
||||||
<ServerUrl>
|
<ServerUrl>
|
||||||
<span>
|
<span>
|
||||||
{hideHostname || options.hideHostname
|
{hideHostname || options.hideHostname
|
||||||
? getBasePath(server.url)
|
? getBasePath(normalizedUrl)
|
||||||
: server.url}
|
: normalizedUrl}
|
||||||
</span>
|
</span>
|
||||||
{operation.path}
|
{operation.path}
|
||||||
</ServerUrl>
|
</ServerUrl>
|
||||||
</SelectOnClick>
|
</SelectOnClick>
|
||||||
</ServerItem>
|
</ServerItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</ServersOverlay>
|
</ServersOverlay>
|
||||||
</OperationEndpointWrap>
|
</OperationEndpointWrap>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as React from 'react';
|
||||||
import { ExampleValue, FieldLabel } from '../../common-elements/fields';
|
import { ExampleValue, FieldLabel } from '../../common-elements/fields';
|
||||||
|
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
export interface EnumValuesProps {
|
export interface EnumValuesProps {
|
||||||
values: string[];
|
values: string[];
|
||||||
|
@ -9,8 +10,10 @@ export interface EnumValuesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
render() {
|
render() {
|
||||||
const { values, type } = this.props;
|
const { values, type } = this.props;
|
||||||
|
const { enumSkipQuotes } = this.context;
|
||||||
if (!values.length) {
|
if (!values.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -21,11 +24,14 @@ export class EnumValues extends React.PureComponent<EnumValuesProps> {
|
||||||
{type === 'array' ? l('enumArray') : ''}{' '}
|
{type === 'array' ? l('enumArray') : ''}{' '}
|
||||||
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
||||||
</FieldLabel>
|
</FieldLabel>
|
||||||
{values.map((value, idx) => (
|
{values.map((value, idx) => {
|
||||||
|
const exampleValue = enumSkipQuotes ? value : JSON.stringify(value);
|
||||||
|
return (
|
||||||
<React.Fragment key={idx}>
|
<React.Fragment key={idx}>
|
||||||
<ExampleValue>{JSON.stringify(value)}</ExampleValue>{' '}
|
<ExampleValue>{exampleValue}</ExampleValue>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,7 @@ export class Field extends React.Component<FieldProps> {
|
||||||
<FieldDetails {...this.props} />
|
<FieldDetails {...this.props} />
|
||||||
</PropertyDetailsCell>
|
</PropertyDetailsCell>
|
||||||
</tr>
|
</tr>
|
||||||
{field.expanded &&
|
{field.expanded && withSubSchema && (
|
||||||
withSubSchema && (
|
|
||||||
<tr key={field.name + 'inner'}>
|
<tr key={field.name + 'inner'}>
|
||||||
<PropertyCellWithInner colSpan={2}>
|
<PropertyCellWithInner colSpan={2}>
|
||||||
<InnerPropertiesWrap>
|
<InnerPropertiesWrap>
|
||||||
|
|
|
@ -17,10 +17,7 @@ export class FieldDetail extends React.PureComponent<FieldDetailProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FieldLabel> {this.props.label} </FieldLabel>{' '}
|
<FieldLabel> {this.props.label} </FieldLabel> <ExampleValue>{value}</ExampleValue>
|
||||||
<ExampleValue>
|
|
||||||
{value}
|
|
||||||
</ExampleValue>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,13 @@ import { FieldDetail } from './FieldDetail';
|
||||||
import { Badge } from '../../common-elements/';
|
import { Badge } from '../../common-elements/';
|
||||||
|
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
|
||||||
export class FieldDetails extends React.PureComponent<FieldProps> {
|
export class FieldDetails extends React.PureComponent<FieldProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
render() {
|
render() {
|
||||||
const { showExamples, field, renderDiscriminatorSwitch } = this.props;
|
const { showExamples, field, renderDiscriminatorSwitch } = this.props;
|
||||||
|
const { enumSkipQuotes } = this.context;
|
||||||
|
|
||||||
const { schema, description, example, deprecated } = field;
|
const { schema, description, example, deprecated } = field;
|
||||||
|
|
||||||
|
@ -33,7 +36,8 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
|
||||||
if (showExamples) {
|
if (showExamples) {
|
||||||
const label = l('example') + ':';
|
const label = l('example') + ':';
|
||||||
if (field.in && field.style) {
|
if (field.in && field.style) {
|
||||||
const serializedValue = serializeParameterValue(field, example);
|
const serializedValue =
|
||||||
|
example !== undefined ? serializeParameterValue(field, example) : undefined;
|
||||||
exampleField = <FieldDetail label={label} value={serializedValue} raw={true} />;
|
exampleField = <FieldDetail label={label} value={serializedValue} raw={true} />;
|
||||||
} else {
|
} else {
|
||||||
exampleField = <FieldDetail label={label} value={example} />;
|
exampleField = <FieldDetail label={label} value={example} />;
|
||||||
|
@ -64,7 +68,7 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
|
||||||
<Badge type="warning"> {l('deprecated')} </Badge>
|
<Badge type="warning"> {l('deprecated')} </Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FieldDetail label={l('default') + ':'} value={schema.default} />
|
<FieldDetail raw={enumSkipQuotes} label={l('default') + ':'} value={schema.default} />
|
||||||
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
|
{!renderDiscriminatorSwitch && <EnumValues type={schema.type} values={schema.enum} />}{' '}
|
||||||
{exampleField}
|
{exampleField}
|
||||||
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
{<Extensions extensions={{ ...field.extensions, ...schema.extensions }} />}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { SampleControls } from '../../common-elements';
|
||||||
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
import { CopyButtonWrapper } from '../../common-elements/CopyButtonWrapper';
|
||||||
import { PrismDiv } from '../../common-elements/PrismDiv';
|
import { PrismDiv } from '../../common-elements/PrismDiv';
|
||||||
import { jsonToHTML } from '../../utils/jsonToHtml';
|
import { jsonToHTML } from '../../utils/jsonToHtml';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { jsonStyles } from './style';
|
import { jsonStyles } from './style';
|
||||||
|
|
||||||
export interface JsonProps {
|
export interface JsonProps {
|
||||||
|
@ -32,12 +33,18 @@ class Json extends React.PureComponent<JsonProps> {
|
||||||
<span onClick={this.expandAll}> Expand all </span>
|
<span onClick={this.expandAll}> Expand all </span>
|
||||||
<span onClick={this.collapseAll}> Collapse all </span>
|
<span onClick={this.collapseAll}> Collapse all </span>
|
||||||
</SampleControls>
|
</SampleControls>
|
||||||
|
<OptionsContext.Consumer>
|
||||||
|
{options => (
|
||||||
<PrismDiv
|
<PrismDiv
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
ref={node => (this.node = node!)}
|
ref={node => (this.node = node!)}
|
||||||
dangerouslySetInnerHTML={{ __html: jsonToHTML(this.props.data) }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</OptionsContext.Consumer>
|
||||||
</JsonViewerWrap>
|
</JsonViewerWrap>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { DropdownProps } from '../../common-elements/dropdown';
|
import { DropdownProps } from '../../common-elements/dropdown';
|
||||||
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
||||||
|
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||||
|
|
||||||
export interface MediaTypeChildProps {
|
export interface MediaTypeChildProps {
|
||||||
schema: SchemaModel;
|
schema: SchemaModel;
|
||||||
|
@ -11,6 +12,8 @@ export interface MediaTypeChildProps {
|
||||||
|
|
||||||
export interface MediaTypesSwitchProps {
|
export interface MediaTypesSwitchProps {
|
||||||
content?: MediaContentModel;
|
content?: MediaContentModel;
|
||||||
|
withLabel?: boolean;
|
||||||
|
|
||||||
renderDropdown: (props: DropdownProps) => JSX.Element;
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
children: (activeMime: MediaTypeModel) => JSX.Element;
|
children: (activeMime: MediaTypeModel) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
@ -37,13 +40,25 @@ export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Wrapper = ({ children }) =>
|
||||||
|
this.props.withLabel ? (
|
||||||
|
<DropdownWrapper>
|
||||||
|
<DropdownLabel>Content type</DropdownLabel>
|
||||||
|
{children}
|
||||||
|
</DropdownWrapper>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Wrapper>
|
||||||
{this.props.renderDropdown({
|
{this.props.renderDropdown({
|
||||||
value: options[activeMimeIdx],
|
value: options[activeMimeIdx],
|
||||||
options,
|
options,
|
||||||
onChange: this.switchMedia,
|
onChange: this.switchMedia,
|
||||||
})}
|
})}
|
||||||
|
</Wrapper>
|
||||||
{this.props.children(content.active)}
|
{this.props.children(content.active)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { SmallTabs, Tab, TabList, TabPanel } from '../../common-elements';
|
import styled from '../../styled-components';
|
||||||
import { MediaTypeModel } from '../../services/models';
|
|
||||||
|
|
||||||
|
import { DropdownProps } from '../../common-elements';
|
||||||
|
import { MediaTypeModel } from '../../services/models';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { Example } from './Example';
|
import { Example } from './Example';
|
||||||
import { NoSampleLabel } from './styled.elements';
|
import { DropdownLabel, DropdownWrapper, NoSampleLabel } from './styled.elements';
|
||||||
|
|
||||||
export interface PayloadSamplesProps {
|
export interface PayloadSamplesProps {
|
||||||
mediaType: MediaTypeModel;
|
mediaType: MediaTypeModel;
|
||||||
|
renderDropdown: (props: DropdownProps) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
|
interface MediaTypeSamplesState {
|
||||||
|
activeIdx: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MediaTypeSamples extends React.Component<PayloadSamplesProps, MediaTypeSamplesState> {
|
||||||
|
state = {
|
||||||
|
activeIdx: 0,
|
||||||
|
};
|
||||||
|
switchMedia = ({ value }) => {
|
||||||
|
this.setState({
|
||||||
|
activeIdx: parseInt(value, 10),
|
||||||
|
});
|
||||||
|
};
|
||||||
render() {
|
render() {
|
||||||
|
const { activeIdx } = this.state;
|
||||||
const examples = this.props.mediaType.examples || {};
|
const examples = this.props.mediaType.examples || {};
|
||||||
const mimeType = this.props.mediaType.name;
|
const mimeType = this.props.mediaType.name;
|
||||||
|
|
||||||
|
@ -21,28 +37,46 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
|
||||||
if (examplesNames.length === 0) {
|
if (examplesNames.length === 0) {
|
||||||
return noSample;
|
return noSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examplesNames.length > 1) {
|
if (examplesNames.length > 1) {
|
||||||
|
const options = examplesNames.map((name, idx) => {
|
||||||
|
return {
|
||||||
|
label: examples[name].summary || name,
|
||||||
|
value: idx.toString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const example = examples[examplesNames[activeIdx]];
|
||||||
|
const description = example.description;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SmallTabs defaultIndex={0}>
|
<SamplesWrapper>
|
||||||
<TabList>
|
<DropdownWrapper>
|
||||||
{examplesNames.map(name => (
|
<DropdownLabel>Example</DropdownLabel>
|
||||||
<Tab key={name}> {examples[name].summary || name} </Tab>
|
{this.props.renderDropdown({
|
||||||
))}
|
value: options[activeIdx],
|
||||||
</TabList>
|
options,
|
||||||
{examplesNames.map(name => (
|
onChange: this.switchMedia,
|
||||||
<TabPanel key={name}>
|
})}
|
||||||
<Example example={examples[name]} mimeType={mimeType} />
|
</DropdownWrapper>
|
||||||
</TabPanel>
|
<div>
|
||||||
))}
|
{description && <Markdown source={description} />}
|
||||||
</SmallTabs>
|
<Example example={example} mimeType={mimeType} />
|
||||||
|
</div>
|
||||||
|
</SamplesWrapper>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const name = examplesNames[0];
|
const example = examples[examplesNames[0]];
|
||||||
return (
|
return (
|
||||||
<div>
|
<SamplesWrapper>
|
||||||
<Example example={examples[name]} mimeType={mimeType} />
|
{example.description && <Markdown source={example.description} />}
|
||||||
</div>
|
<Example example={example} mimeType={mimeType} />
|
||||||
|
</SamplesWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SamplesWrapper = styled.div`
|
||||||
|
margin-top: 15px;
|
||||||
|
`;
|
||||||
|
|
|
@ -2,10 +2,9 @@ import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { MediaTypeSamples } from './MediaTypeSamples';
|
import { MediaTypeSamples } from './MediaTypeSamples';
|
||||||
|
|
||||||
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
|
||||||
|
|
||||||
import { MediaContentModel } from '../../services/models';
|
import { MediaContentModel } from '../../services/models';
|
||||||
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
|
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
||||||
import { InvertedSimpleDropdown, MimeLabel } from './styled.elements';
|
import { InvertedSimpleDropdown, MimeLabel } from './styled.elements';
|
||||||
|
|
||||||
export interface PayloadSamplesProps {
|
export interface PayloadSamplesProps {
|
||||||
|
@ -21,8 +20,14 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown}>
|
<MediaTypesSwitch content={mimeContent} renderDropdown={this.renderDropdown} withLabel={true}>
|
||||||
{mediaType => <MediaTypeSamples key="samples" mediaType={mediaType} />}
|
{mediaType => (
|
||||||
|
<MediaTypeSamples
|
||||||
|
key="samples"
|
||||||
|
mediaType={mediaType}
|
||||||
|
renderDropdown={this.renderDropdown}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</MediaTypesSwitch>
|
</MediaTypesSwitch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,7 @@ export function useExternalExample(example: ExampleModel, mimeType: string) {
|
||||||
|
|
||||||
prevRef.current = example;
|
prevRef.current = example;
|
||||||
|
|
||||||
useEffect(
|
useEffect(() => {
|
||||||
() => {
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
|
@ -26,9 +25,7 @@ export function useExternalExample(example: ExampleModel, mimeType: string) {
|
||||||
};
|
};
|
||||||
|
|
||||||
load();
|
load();
|
||||||
},
|
}, [example, mimeType]);
|
||||||
[example, mimeType],
|
|
||||||
);
|
|
||||||
|
|
||||||
return value.current;
|
return value.current;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,48 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Dropdown from 'react-dropdown';
|
import Dropdown from 'react-dropdown';
|
||||||
|
|
||||||
|
import { transparentize } from 'polished';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
import { StyledDropdown } from '../../common-elements';
|
import { StyledDropdown } from '../../common-elements';
|
||||||
|
|
||||||
export const MimeLabel = styled.div`
|
export const MimeLabel = styled.div`
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.9);
|
padding: 12px;
|
||||||
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
color: rgba(255, 255, 255, 0.8);
|
`;
|
||||||
|
|
||||||
|
export const DropdownLabel = styled.span`
|
||||||
|
font-family: ${({ theme }) => theme.typography.headings.fontFamily};
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: -11px;
|
||||||
|
left: 12px;
|
||||||
|
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
||||||
|
color: ${({ theme }) => transparentize(0.6, theme.rightPanel.textColor)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DropdownWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: 0.929em;
|
font-size: 0.929em;
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.rightPanel.textColor};
|
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
display: block;
|
display: block;
|
||||||
|
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||||
|
.Dropdown-control {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
.Dropdown-control,
|
.Dropdown-control,
|
||||||
.Dropdown-control:hover {
|
.Dropdown-control:hover {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 1.2em 0 0;
|
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
@ -34,6 +53,7 @@ export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||||
}
|
}
|
||||||
.Dropdown-menu {
|
.Dropdown-menu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,7 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
|
||||||
code={code}
|
code={code}
|
||||||
opened={expanded}
|
opened={expanded}
|
||||||
/>
|
/>
|
||||||
{expanded &&
|
{expanded && !empty && (
|
||||||
!empty && (
|
|
||||||
<ResponseDetailsWrap>
|
<ResponseDetailsWrap>
|
||||||
<ResponseDetails response={this.props.response} />
|
<ResponseDetails response={this.props.response} />
|
||||||
</ResponseDetailsWrap>
|
</ResponseDetailsWrap>
|
||||||
|
|
|
@ -34,9 +34,9 @@ export class ObjectSchema extends React.Component<ObjectSchemaProps> {
|
||||||
|
|
||||||
const filteredFields = needFilter
|
const filteredFields = needFilter
|
||||||
? fields.filter(item => {
|
? fields.filter(item => {
|
||||||
return (
|
return !(
|
||||||
(this.props.skipReadOnly && !item.schema.readOnly) ||
|
(this.props.skipReadOnly && item.schema.readOnly) ||
|
||||||
(this.props.skipWriteOnly && !item.schema.writeOnly)
|
(this.props.skipWriteOnly && item.schema.writeOnly)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
: fields;
|
: fields;
|
||||||
|
|
93
src/components/SchemaDefinition/SchemaDefinition.tsx
Normal file
93
src/components/SchemaDefinition/SchemaDefinition.tsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { DarkRightPanel, MiddlePanel, MimeLabel, Row, Section } from '../../common-elements';
|
||||||
|
import { MediaTypeModel, OpenAPIParser, RedocNormalizedOptions } from '../../services';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { OpenAPIMediaType } from '../../types';
|
||||||
|
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
|
||||||
|
import { MediaTypeSamples } from '../PayloadSamples/MediaTypeSamples';
|
||||||
|
import { InvertedSimpleDropdown } from '../PayloadSamples/styled.elements';
|
||||||
|
import { Schema } from '../Schema';
|
||||||
|
|
||||||
|
export interface ObjectDescriptionProps {
|
||||||
|
schemaRef: string;
|
||||||
|
exampleRef?: string;
|
||||||
|
showReadOnly?: boolean;
|
||||||
|
showWriteOnly?: boolean;
|
||||||
|
parser: OpenAPIParser;
|
||||||
|
options: RedocNormalizedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps> {
|
||||||
|
private static getMediaType(schemaRef: string, exampleRef?: string): OpenAPIMediaType {
|
||||||
|
if (!schemaRef) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const info: OpenAPIMediaType = {
|
||||||
|
schema: { $ref: schemaRef },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (exampleRef) {
|
||||||
|
info.examples = { example: { $ref: exampleRef } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _mediaModel: MediaTypeModel;
|
||||||
|
|
||||||
|
private get mediaModel() {
|
||||||
|
const { parser, schemaRef, exampleRef, options } = this.props;
|
||||||
|
if (!this._mediaModel) {
|
||||||
|
this._mediaModel = new MediaTypeModel(
|
||||||
|
parser,
|
||||||
|
'json',
|
||||||
|
false,
|
||||||
|
SchemaDefinition.getMediaType(schemaRef, exampleRef),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._mediaModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { showReadOnly = true, showWriteOnly = false } = this.props;
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<Row>
|
||||||
|
<MiddlePanel>
|
||||||
|
<Schema
|
||||||
|
skipWriteOnly={!showWriteOnly}
|
||||||
|
skipReadOnly={!showReadOnly}
|
||||||
|
schema={this.mediaModel.schema}
|
||||||
|
/>
|
||||||
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel>
|
||||||
|
<MediaSamplesWrap>
|
||||||
|
<MediaTypeSamples renderDropdown={this.renderDropdown} mediaType={this.mediaModel} />
|
||||||
|
</MediaSamplesWrap>
|
||||||
|
</DarkRightPanel>
|
||||||
|
</Row>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderDropdown = props => {
|
||||||
|
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const MediaSamplesWrap = styled.div`
|
||||||
|
background: ${({ theme }) => theme.codeSample.backgroundColor};
|
||||||
|
& > div,
|
||||||
|
& > pre {
|
||||||
|
padding: ${props => props.theme.spacing.unit * 4}px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div > pre {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -57,16 +57,13 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
{item.name}
|
{item.name}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</MenuItemTitle>
|
</MenuItemTitle>
|
||||||
{(item.depth > 0 &&
|
{(item.depth > 0 && item.items.length > 0 && (
|
||||||
item.items.length > 0 && (
|
|
||||||
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
|
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
|
||||||
)) ||
|
)) ||
|
||||||
null}
|
null}
|
||||||
</MenuItemLabel>
|
</MenuItemLabel>
|
||||||
)}
|
)}
|
||||||
{!withoutChildren &&
|
{!withoutChildren && item.items && item.items.length > 0 && (
|
||||||
item.items &&
|
|
||||||
item.items.length > 0 && (
|
|
||||||
<MenuItems
|
<MenuItems
|
||||||
expanded={item.expanded}
|
expanded={item.expanded}
|
||||||
items={item.items}
|
items={item.items}
|
||||||
|
@ -83,7 +80,7 @@ export interface OperationMenuItemContentProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class OperationMenuItemContent extends React.Component<OperationMenuItemContentProps> {
|
export class OperationMenuItemContent extends React.Component<OperationMenuItemContentProps> {
|
||||||
render() {
|
render() {
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -10,8 +10,13 @@ import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOption
|
||||||
import { ScrollService } from './ScrollService';
|
import { ScrollService } from './ScrollService';
|
||||||
import { SearchStore } from './SearchStore';
|
import { SearchStore } from './SearchStore';
|
||||||
|
|
||||||
|
import { SchemaDefinition } from '../components/SchemaDefinition/SchemaDefinition';
|
||||||
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
import { SecurityDefs } from '../components/SecuritySchemes/SecuritySchemes';
|
||||||
import { SECURITY_DEFINITIONS_COMPONENT_NAME } from '../utils/openapi';
|
import {
|
||||||
|
SCHEMA_DEFINITION_JSX_NAME,
|
||||||
|
SECURITY_DEFINITIONS_COMPONENT_NAME,
|
||||||
|
SECURITY_DEFINITIONS_JSX_NAME,
|
||||||
|
} from '../utils/openapi';
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
menu: {
|
menu: {
|
||||||
|
@ -151,5 +156,18 @@ const DEFAULT_OPTIONS: RedocRawOptions = {
|
||||||
securitySchemes: store.spec.securitySchemes,
|
securitySchemes: store.spec.securitySchemes,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
[SECURITY_DEFINITIONS_JSX_NAME]: {
|
||||||
|
component: SecurityDefs,
|
||||||
|
propsSelector: (store: AppStore) => ({
|
||||||
|
securitySchemes: store.spec.securitySchemes,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
[SCHEMA_DEFINITION_JSX_NAME]: {
|
||||||
|
component: SchemaDefinition,
|
||||||
|
propsSelector: (store: AppStore) => ({
|
||||||
|
parser: store.spec.parser,
|
||||||
|
options: store.options,
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as marked from 'marked';
|
import * as marked from 'marked';
|
||||||
|
|
||||||
import { highlight, safeSlugify } from '../utils';
|
import { highlight, safeSlugify, unescapeHTMLChars } from '../utils';
|
||||||
import { AppStore } from './AppStore';
|
import { AppStore } from './AppStore';
|
||||||
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ export class MarkdownRenderer {
|
||||||
container: MarkdownHeading[] = this.headings,
|
container: MarkdownHeading[] = this.headings,
|
||||||
parentId?: string,
|
parentId?: string,
|
||||||
): MarkdownHeading {
|
): MarkdownHeading {
|
||||||
|
name = unescapeHTMLChars(name);
|
||||||
const item = {
|
const item = {
|
||||||
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
||||||
name,
|
name,
|
||||||
|
@ -88,7 +89,7 @@ export class MarkdownRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
attachHeadingsDescriptions(rawText: string) {
|
attachHeadingsDescriptions(rawText: string) {
|
||||||
const buildRegexp = heading => {
|
const buildRegexp = (heading: MarkdownHeading) => {
|
||||||
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`);
|
return new RegExp(`##?\\s+${heading.name.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class MenuBuilder {
|
||||||
|
|
||||||
const items: ContentItemModel[] = [];
|
const items: ContentItemModel[] = [];
|
||||||
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
const tagsMap = MenuBuilder.getTagsWithOperations(spec);
|
||||||
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', options));
|
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, 1, options));
|
||||||
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
||||||
items.push(
|
items.push(
|
||||||
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
|
||||||
|
@ -59,14 +59,16 @@ export class MenuBuilder {
|
||||||
*/
|
*/
|
||||||
static addMarkdownItems(
|
static addMarkdownItems(
|
||||||
description: string,
|
description: string,
|
||||||
|
parent: GroupModel | undefined,
|
||||||
|
initialDepth: number,
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions,
|
||||||
): ContentItemModel[] {
|
): ContentItemModel[] {
|
||||||
const renderer = new MarkdownRenderer(options);
|
const renderer = new MarkdownRenderer(options);
|
||||||
const headings = renderer.extractHeadings(description || '');
|
const headings = renderer.extractHeadings(description || '');
|
||||||
|
|
||||||
const mapHeadingsDeep = (parent, items, depth = 1) =>
|
const mapHeadingsDeep = (_parent, items, depth = 1) =>
|
||||||
items.map(heading => {
|
items.map(heading => {
|
||||||
const group = new GroupModel('section', heading, parent);
|
const group = new GroupModel('section', heading, _parent);
|
||||||
group.depth = depth;
|
group.depth = depth;
|
||||||
if (heading.items) {
|
if (heading.items) {
|
||||||
group.items = mapHeadingsDeep(group, heading.items, depth + 1);
|
group.items = mapHeadingsDeep(group, heading.items, depth + 1);
|
||||||
|
@ -82,7 +84,7 @@ export class MenuBuilder {
|
||||||
return group;
|
return group;
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapHeadingsDeep(undefined, headings);
|
return mapHeadingsDeep(parent, headings, initialDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,15 +146,22 @@ export class MenuBuilder {
|
||||||
}
|
}
|
||||||
const item = new GroupModel('tag', tag, parent);
|
const item = new GroupModel('tag', tag, parent);
|
||||||
item.depth = GROUP_DEPTH + 1;
|
item.depth = GROUP_DEPTH + 1;
|
||||||
item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options);
|
|
||||||
|
|
||||||
// don't put empty tag into content, instead put its operations
|
// don't put empty tag into content, instead put its operations
|
||||||
if (tag.name === '') {
|
if (tag.name === '') {
|
||||||
const items = this.getOperationsItems(parser, undefined, tag, item.depth + 1, options);
|
const items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, item.depth + 1, options),
|
||||||
|
...this.getOperationsItems(parser, undefined, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
res.push(...items);
|
res.push(...items);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.items = [
|
||||||
|
...MenuBuilder.addMarkdownItems(tag.description || '', item, item.depth + 1, options),
|
||||||
|
...this.getOperationsItems(parser, item, tag, item.depth + 1, options),
|
||||||
|
];
|
||||||
|
|
||||||
res.push(item);
|
res.push(item);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -23,12 +23,14 @@ export interface RedocRawOptions {
|
||||||
showExtensions?: boolean | string | string[];
|
showExtensions?: boolean | string | string[];
|
||||||
hideSingleRequestSampleTab?: boolean | string;
|
hideSingleRequestSampleTab?: boolean | string;
|
||||||
menuToggle?: boolean | string;
|
menuToggle?: boolean | string;
|
||||||
|
jsonSampleExpandLevel?: number | string | 'all';
|
||||||
|
|
||||||
unstable_ignoreMimeParameters?: boolean;
|
unstable_ignoreMimeParameters?: boolean;
|
||||||
|
|
||||||
allowedMdComponents?: Dict<MDXComponentMeta>;
|
allowedMdComponents?: Dict<MDXComponentMeta>;
|
||||||
|
|
||||||
labels?: LabelsConfigRaw;
|
labels?: LabelsConfigRaw;
|
||||||
|
enumSkipQuotes?: boolean | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function argValueToBoolean(val?: string | boolean): boolean {
|
function argValueToBoolean(val?: string | boolean): boolean {
|
||||||
|
@ -111,6 +113,16 @@ export class RedocNormalizedOptions {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static normalizeJsonSampleExpandLevel(level?: number | string | 'all'): number {
|
||||||
|
if (level === 'all') {
|
||||||
|
return +Infinity;
|
||||||
|
}
|
||||||
|
if (!isNaN(Number(level))) {
|
||||||
|
return Math.ceil(Number(level));
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
theme: ResolvedThemeInterface;
|
theme: ResolvedThemeInterface;
|
||||||
scrollYOffset: () => number;
|
scrollYOffset: () => number;
|
||||||
hideHostname: boolean;
|
hideHostname: boolean;
|
||||||
|
@ -127,6 +139,8 @@ export class RedocNormalizedOptions {
|
||||||
showExtensions: boolean | string[];
|
showExtensions: boolean | string[];
|
||||||
hideSingleRequestSampleTab: boolean;
|
hideSingleRequestSampleTab: boolean;
|
||||||
menuToggle: boolean;
|
menuToggle: boolean;
|
||||||
|
jsonSampleExpandLevel: number;
|
||||||
|
enumSkipQuotes: boolean;
|
||||||
|
|
||||||
/* tslint:disable-next-line */
|
/* tslint:disable-next-line */
|
||||||
unstable_ignoreMimeParameters: boolean;
|
unstable_ignoreMimeParameters: boolean;
|
||||||
|
@ -159,6 +173,10 @@ export class RedocNormalizedOptions {
|
||||||
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
||||||
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
||||||
this.menuToggle = argValueToBoolean(raw.menuToggle);
|
this.menuToggle = argValueToBoolean(raw.menuToggle);
|
||||||
|
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
||||||
|
raw.jsonSampleExpandLevel,
|
||||||
|
);
|
||||||
|
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
|
||||||
|
|
||||||
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,14 @@ export class GroupModel implements IMenuItem {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
||||||
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
||||||
|
|
||||||
|
// remove sections from markdown, same as in ApiInfo
|
||||||
this.description = tagOrGroup.description || '';
|
this.description = tagOrGroup.description || '';
|
||||||
|
const firstHeadingLinePos = this.description.search(/^##?\s+/m);
|
||||||
|
if (firstHeadingLinePos > -1) {
|
||||||
|
this.description = this.description.substring(0, firstHeadingLinePos);
|
||||||
|
}
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
|
|
||||||
import { OpenAPIParser } from '../../services';
|
import { OpenAPIParser } from '../../services';
|
||||||
import { OpenAPIParameter, OpenAPIParameterLocation, OpenAPIParameterStyle } from '../../types';
|
import { OpenAPIParameter, OpenAPIParameterLocation, OpenAPIParameterStyle } from '../../types';
|
||||||
|
import { expandDefaultServerVariables } from '../openapi';
|
||||||
|
|
||||||
describe('Utils', () => {
|
describe('Utils', () => {
|
||||||
describe('openapi getStatusCode', () => {
|
describe('openapi getStatusCode', () => {
|
||||||
|
@ -297,11 +298,8 @@ describe('Utils', () => {
|
||||||
it('should expand variables', () => {
|
it('should expand variables', () => {
|
||||||
const servers = normalizeServers('', [
|
const servers = normalizeServers('', [
|
||||||
{
|
{
|
||||||
url: '{protocol}{host}{basePath}',
|
url: 'http://{host}{basePath}',
|
||||||
variables: {
|
variables: {
|
||||||
protocol: {
|
|
||||||
default: 'http://',
|
|
||||||
},
|
|
||||||
host: {
|
host: {
|
||||||
default: '127.0.0.1',
|
default: '127.0.0.1',
|
||||||
},
|
},
|
||||||
|
@ -319,9 +317,15 @@ describe('Utils', () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(servers[0].url).toEqual('http://127.0.0.1/path/to/endpoint');
|
expect(expandDefaultServerVariables(servers[0].url, servers[0].variables)).toEqual(
|
||||||
expect(servers[1].url).toEqual('http://127.0.0.2:{port}');
|
'http://127.0.0.1/path/to/endpoint',
|
||||||
expect(servers[2].url).toEqual('http://127.0.0.3');
|
);
|
||||||
|
expect(expandDefaultServerVariables(servers[1].url, servers[1].variables)).toEqual(
|
||||||
|
'http://127.0.0.2:{port}',
|
||||||
|
);
|
||||||
|
expect(expandDefaultServerVariables(servers[2].url, servers[2].variables)).toEqual(
|
||||||
|
'http://127.0.0.3',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -194,3 +194,7 @@ function parseURL(url: string) {
|
||||||
return new URL(url);
|
return new URL(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function unescapeHTMLChars(str: string): string {
|
||||||
|
return str.replace(/&#(\d+);/g, (_m, code) => String.fromCharCode(parseInt(code, 10)));
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
let level = 1;
|
let level = 1;
|
||||||
const COLLAPSE_LEVEL = 2;
|
|
||||||
|
|
||||||
export function jsonToHTML(json) {
|
export function jsonToHTML(json, maxExpandLevel) {
|
||||||
level = 1;
|
level = 1;
|
||||||
let output = '';
|
let output = '';
|
||||||
output += '<div class="redoc-json">';
|
output += '<div class="redoc-json">';
|
||||||
output += valueToHTML(json);
|
output += valueToHTML(json, maxExpandLevel);
|
||||||
output += '</div>';
|
output += '</div>';
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -33,20 +32,20 @@ function punctuation(val) {
|
||||||
return '<span class="token punctuation">' + val + '</span>';
|
return '<span class="token punctuation">' + val + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function valueToHTML(value) {
|
function valueToHTML(value, maxExpandLevel: number) {
|
||||||
const valueType = typeof value;
|
const valueType = typeof value;
|
||||||
let output = '';
|
let output = '';
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
output += decorateWithSpan('null', 'token keyword');
|
output += decorateWithSpan('null', 'token keyword');
|
||||||
} else if (value && value.constructor === Array) {
|
} else if (value && value.constructor === Array) {
|
||||||
level++;
|
level++;
|
||||||
output += arrayToHTML(value);
|
output += arrayToHTML(value, maxExpandLevel);
|
||||||
level--;
|
level--;
|
||||||
} else if (value && value.constructor === Date) {
|
} else if (value && value.constructor === Date) {
|
||||||
output += decorateWithSpan('"' + value.toISOString() + '"', 'token string');
|
output += decorateWithSpan('"' + value.toISOString() + '"', 'token string');
|
||||||
} else if (valueType === 'object') {
|
} else if (valueType === 'object') {
|
||||||
level++;
|
level++;
|
||||||
output += objectToHTML(value);
|
output += objectToHTML(value, maxExpandLevel);
|
||||||
level--;
|
level--;
|
||||||
} else if (valueType === 'number') {
|
} else if (valueType === 'number') {
|
||||||
output += decorateWithSpan(value, 'token number');
|
output += decorateWithSpan(value, 'token number');
|
||||||
|
@ -70,8 +69,8 @@ function valueToHTML(value) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrayToHTML(json) {
|
function arrayToHTML(json, maxExpandLevel: number) {
|
||||||
const collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : '';
|
const collapsed = level > maxExpandLevel ? 'collapsed' : '';
|
||||||
let output = `<div class="collapser"></div>${punctuation(
|
let output = `<div class="collapser"></div>${punctuation(
|
||||||
'[',
|
'[',
|
||||||
)}<span class="ellipsis"></span><ul class="array collapsible">`;
|
)}<span class="ellipsis"></span><ul class="array collapsible">`;
|
||||||
|
@ -80,7 +79,7 @@ function arrayToHTML(json) {
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
hasContents = true;
|
hasContents = true;
|
||||||
output += '<li><div class="hoverable ' + collapsed + '">';
|
output += '<li><div class="hoverable ' + collapsed + '">';
|
||||||
output += valueToHTML(json[i]);
|
output += valueToHTML(json[i], maxExpandLevel);
|
||||||
if (i < length - 1) {
|
if (i < length - 1) {
|
||||||
output += ',';
|
output += ',';
|
||||||
}
|
}
|
||||||
|
@ -93,8 +92,8 @@ function arrayToHTML(json) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function objectToHTML(json) {
|
function objectToHTML(json, maxExpandLevel: number) {
|
||||||
const collapsed = level > COLLAPSE_LEVEL ? 'collapsed' : '';
|
const collapsed = level > maxExpandLevel ? 'collapsed' : '';
|
||||||
const keys = Object.keys(json);
|
const keys = Object.keys(json);
|
||||||
const length = keys.length;
|
const length = keys.length;
|
||||||
let output = `<div class="collapser"></div>${punctuation(
|
let output = `<div class="collapser"></div>${punctuation(
|
||||||
|
@ -106,7 +105,7 @@ function objectToHTML(json) {
|
||||||
hasContents = true;
|
hasContents = true;
|
||||||
output += '<li><div class="hoverable ' + collapsed + '">';
|
output += '<li><div class="hoverable ' + collapsed + '">';
|
||||||
output += '<span class="property token string">"' + htmlEncode(key) + '"</span>: ';
|
output += '<span class="property token string">"' + htmlEncode(key) + '"</span>: ';
|
||||||
output += valueToHTML(json[key]);
|
output += valueToHTML(json[key], maxExpandLevel);
|
||||||
if (i < length - 1) {
|
if (i < length - 1) {
|
||||||
output += punctuation(',');
|
output += punctuation(',');
|
||||||
}
|
}
|
||||||
|
|
|
@ -453,7 +453,7 @@ export function mergeSimilarMediaTypes(types: Dict<OpenAPIMediaType>): Dict<Open
|
||||||
return mergedTypes;
|
return mergedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function expandVariables(url: string, variables: object = {}) {
|
export function expandDefaultServerVariables(url: string, variables: object = {}) {
|
||||||
return url.replace(
|
return url.replace(
|
||||||
/(?:{)(\w+)(?:})/g,
|
/(?:{)(\w+)(?:})/g,
|
||||||
(match, name) => (variables[name] && variables[name].default) || match,
|
(match, name) => (variables[name] && variables[name].default) || match,
|
||||||
|
@ -482,21 +482,23 @@ export function normalizeServers(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeUrl(url: string, variables: object | undefined): string {
|
function normalizeUrl(url: string): string {
|
||||||
url = expandVariables(url, variables);
|
|
||||||
return resolveUrl(baseUrl, url);
|
return resolveUrl(baseUrl, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return servers.map(server => {
|
return servers.map(server => {
|
||||||
return {
|
return {
|
||||||
...server,
|
...server,
|
||||||
url: normalizeUrl(server.url, server.variables),
|
url: normalizeUrl(server.url),
|
||||||
description: server.description || '',
|
description: server.description || '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SECURITY_DEFINITIONS_COMPONENT_NAME = 'security-definitions';
|
export const SECURITY_DEFINITIONS_COMPONENT_NAME = 'security-definitions';
|
||||||
|
export const SECURITY_DEFINITIONS_JSX_NAME = 'SecurityDefinitions';
|
||||||
|
export const SCHEMA_DEFINITION_JSX_NAME = 'ObjectDescription';
|
||||||
|
|
||||||
export let SECURITY_SCHEMES_SECTION_PREFIX = 'section/Authentication/';
|
export let SECURITY_SCHEMES_SECTION_PREFIX = 'section/Authentication/';
|
||||||
export function setSecuritySchemePrefix(prefix: string) {
|
export function setSecuritySchemePrefix(prefix: string) {
|
||||||
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
SECURITY_SCHEMES_SECTION_PREFIX = prefix;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user