support for a specific vendor extension and toggling content panel area by adding additional option to redoc standalone

This commit is contained in:
Jason Ibrahim 2019-03-15 16:43:08 -07:00 committed by Jason Ibrahim
parent 965778d3b0
commit 1eae317893
10 changed files with 114 additions and 67 deletions

View File

@ -17,14 +17,14 @@ info:
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
# OpenAPI Specification # OpenAPI Specification
This API is documented in **OpenAPI format** and is based on This API is documented in **OpenAPI format** and is based on
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team. [Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
# Cross-Origin Resource Sharing # Cross-Origin Resource Sharing
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
And that allows cross-domain communication from the browser. And that allows cross-domain communication from the browser.
@ -38,8 +38,9 @@ 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 /> <security-definitions />
x-opentext-other: foo
version: 1.0.0 version: 1.0.0
title: Swagger Petstore title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/' termsOfService: 'http://swagger.io/terms/'
@ -82,6 +83,7 @@ paths:
type: integer type: integer
format: int64 format: int64
post: post:
x-opentext-other: bar
tags: tags:
- pet - pet
summary: Add a new pet to the store summary: Add a new pet to the store

View File

@ -1,20 +1,16 @@
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import * as React from 'react'; import * as React from 'react';
import { StyledLink } from '../../../src/common-elements'; import { Constants } from '../../../src/services/Constants';
import { DarkRightPanel, StyledLink } from '../../../src/common-elements';
import { AppStore } from '../../services/AppStore'; import { ContentPanel } from '../RightPanelContent/ContentPanel';
import { MiddlePanel, Row, Section } from '../../common-elements/'; import { MiddlePanel, Row, Section } from '../../common-elements/';
import { AppStore } from '../../services/AppStore';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation'; import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown'; import { Markdown } from '../Markdown/Markdown';
import { StyledMarkdownBlock } from '../Markdown/styled.elements'; import { StyledMarkdownBlock } from '../Markdown/styled.elements';
import { import { ApiHeader, DownloadButton, InfoSpan, InfoSpanBox, InfoSpanBoxWrap, } from './styled.elements';
ApiHeader,
DownloadButton,
InfoSpan,
InfoSpanBox,
InfoSpanBoxWrap,
} from './styled.elements';
export interface ApiInfoProps { export interface ApiInfoProps {
store: AppStore; store: AppStore;
@ -29,8 +25,8 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
}; };
render() { render() {
const { store } = this.props; const {store} = this.props;
const { info, externalDocs } = store.spec; const {info, externalDocs} = store.spec;
const hideDownloadButton = store.options.hideDownloadButton; const hideDownloadButton = store.options.hideDownloadButton;
const downloadFilename = info.downloadFileName; const downloadFilename = info.downloadFileName;
@ -105,11 +101,13 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
</InfoSpanBox> </InfoSpanBox>
</InfoSpanBoxWrap> </InfoSpanBoxWrap>
)) || )) ||
null} null}
</StyledMarkdownBlock> </StyledMarkdownBlock>
<Markdown source={store.spec.info.description} /> <Markdown source={store.spec.info.description}/>
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />} {externalDocs && <ExternalDocumentation externalDocs={externalDocs}/>}
</MiddlePanel> </MiddlePanel>
{store.options.showOtherInfoPanel &&
<DarkRightPanel><ContentPanel content={info[Constants.OTX_EXTENSION_KEY]}/></DarkRightPanel>}
</Row> </Row>
</Section> </Section>
); );

View File

@ -1,24 +1,22 @@
import * as React from 'react';
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import * as React from 'react';
import { OperationPanel } from '../RightPanelContent/OperationPanel';
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements'; import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
import { OptionsContext } from '../OptionsProvider';
import { ShareLink } from '../../common-elements/linkify'; import { ShareLink } from '../../common-elements/linkify';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { Parameters } from '../Parameters/Parameters';
import { RequestSamples } from '../RequestSamples/RequestSamples';
import { ResponsesList } from '../Responses/ResponsesList';
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
import { OperationModel as OperationType } from '../../services/models'; import { OperationModel as OperationType } from '../../services/models';
import styled from '../../styled-components'; import styled from '../../styled-components';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Extensions } from '../Fields/Extensions'; import { Extensions } from '../Fields/Extensions';
import { Markdown } from '../Markdown/Markdown';
import { OptionsContext } from '../OptionsProvider';
import { Parameters } from '../Parameters/Parameters';
import { ResponsesList } from '../Responses/ResponsesList';
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
const OperationRow = styled(Row)` const OperationRow = styled(Row)`
backface-visibility: hidden; backface-visibility: hidden;
@ -28,7 +26,7 @@ const OperationRow = styled(Row)`
`; `;
const Description = styled.div` const Description = styled.div`
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px; margin-bottom: ${({theme}) => theme.spacing.unit * 6}px;
`; `;
export interface OperationProps { export interface OperationProps {
@ -38,9 +36,9 @@ export interface OperationProps {
@observer @observer
export class Operation extends React.Component<OperationProps> { export class Operation extends React.Component<OperationProps> {
render() { render() {
const { operation } = this.props; const {operation} = this.props;
const { name: summary, description, deprecated, externalDocs } = operation; const {name: summary, description, deprecated, externalDocs} = operation;
const hasDescription = !!(description || externalDocs); const hasDescription = !!(description || externalDocs);
return ( return (
@ -49,25 +47,23 @@ export class Operation extends React.Component<OperationProps> {
<OperationRow> <OperationRow>
<MiddlePanel> <MiddlePanel>
<H2> <H2>
<ShareLink to={operation.id} /> <ShareLink to={operation.id}/>
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>} {summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
</H2> </H2>
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />} {options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true}/>}
{hasDescription && ( {hasDescription && (
<Description> <Description>
{description !== undefined && <Markdown source={description} />} {description !== undefined && <Markdown source={description}/>}
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />} {externalDocs && <ExternalDocumentation externalDocs={externalDocs}/>}
</Description> </Description>
)} )}
<Extensions extensions={operation.extensions} /> <Extensions extensions={operation.extensions}/>
<SecurityRequirements securities={operation.security} /> <SecurityRequirements securities={operation.security}/>
<Parameters parameters={operation.parameters} body={operation.requestBody} /> <Parameters parameters={operation.parameters} body={operation.requestBody}/>
<ResponsesList responses={operation.responses} /> <ResponsesList responses={operation.responses}/>
</MiddlePanel> </MiddlePanel>
<DarkRightPanel> <DarkRightPanel>
{!options.pathInMiddlePanel && <Endpoint operation={operation} />} <OperationPanel operation={operation} options={options}/>
<RequestSamples operation={operation} />
<ResponseSamples operation={operation} />
</DarkRightPanel> </DarkRightPanel>
</OperationRow> </OperationRow>
)} )}

View File

@ -1,19 +1,19 @@
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { ContentItems } from '../../../src/components';
import { ThemeProvider } from '../../styled-components';
import { OptionsProvider } from '../OptionsProvider';
import { AppStore } from '../../services'; import { AppStore } from '../../services';
import { ThemeProvider } from '../../styled-components';
import { ApiInfo } from '../ApiInfo/'; import { ApiInfo } from '../ApiInfo/';
import { ApiLogo } from '../ApiLogo/ApiLogo'; import { ApiLogo } from '../ApiLogo/ApiLogo';
import { ContentItems } from '../ContentItems/ContentItems'; import { OptionsProvider } from '../OptionsProvider';
import { SideMenu } from '../SideMenu/SideMenu';
import { StickyResponsiveSidebar } from '../StickySidebar/StickyResponsiveSidebar';
import { ApiContentWrap, BackgroundStub, RedocWrap } from './styled.elements';
import { SearchBox } from '../SearchBox/SearchBox'; import { SearchBox } from '../SearchBox/SearchBox';
import { SideMenu } from '../SideMenu/SideMenu';
import { StickyResponsiveSidebar } from '../StickySidebar/StickyResponsiveSidebar';
import { StoreProvider } from '../StoreBuilder'; import { StoreProvider } from '../StoreBuilder';
import { ApiContentWrap, BackgroundStub, RedocWrap } from './styled.elements';
export interface RedocProps { export interface RedocProps {
store: AppStore; store: AppStore;

View File

@ -0,0 +1,24 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { MiddlePanel, Row } from '../../../src/common-elements';
interface ContentPanelProps {
content: string;
}
@observer
export class ContentPanel extends React.Component<ContentPanelProps> {
render() {
return (
<>
<Row>
<MiddlePanel>
<h1>
{this.props.content}
</h1>
</MiddlePanel>
</Row>
</>
);
}
}

View File

@ -0,0 +1,37 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { ResponseSamples } from '../../../src/components';
import { Endpoint } from '../../../src/components/Endpoint/Endpoint';
import { RequestSamples } from '../../../src/components/RequestSamples/RequestSamples';
import { RedocNormalizedOptions } from '../../../src/services';
import { Constants } from '../../../src/services/Constants';
import { OperationModel } from '../../../src/services/models';
import { ContentPanel } from './/ContentPanel';
interface RightPanelContentProps {
operation: OperationModel;
options: RedocNormalizedOptions;
}
@observer
export class OperationPanel extends React.Component<RightPanelContentProps> {
render() {
if (this.shouldShowOtherInfoPanel()) {
return <ContentPanel content={this.props.operation.extensions[Constants.OTX_EXTENSION_KEY]}/>;
} else {
return (
<>
{!this.props.options.pathInMiddlePanel && <Endpoint operation={this.props.operation}/>}
<RequestSamples operation={this.props.operation}/>
<ResponseSamples operation={this.props.operation}/>
</>
);
}
}
private shouldShowOtherInfoPanel() {
return this.props.options.showOtherInfoPanel &&
this.props.options.showExtensions &&
this.props.operation.extensions[Constants.OTX_EXTENSION_KEY];
}
}

View File

@ -0,0 +1,3 @@
export class Constants {
static readonly OTX_EXTENSION_KEY = 'x-opentext-other';
}

View File

@ -20,6 +20,7 @@ export interface RedocRawOptions {
disableSearch?: boolean | string; disableSearch?: boolean | string;
onlyRequiredInSamples?: boolean | string; onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[]; showExtensions?: boolean | string | string[];
showOtherInfoPanel?: boolean;
unstable_ignoreMimeParameters?: boolean; unstable_ignoreMimeParameters?: boolean;
@ -120,6 +121,7 @@ export class RedocNormalizedOptions {
disableSearch: boolean; disableSearch: boolean;
onlyRequiredInSamples: boolean; onlyRequiredInSamples: boolean;
showExtensions: boolean | string[]; showExtensions: boolean | string[];
showOtherInfoPanel: boolean;
/* tslint:disable-next-line */ /* tslint:disable-next-line */
unstable_ignoreMimeParameters: boolean; unstable_ignoreMimeParameters: boolean;
@ -147,6 +149,7 @@ export class RedocNormalizedOptions {
this.disableSearch = argValueToBoolean(raw.disableSearch); this.disableSearch = argValueToBoolean(raw.disableSearch);
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples); this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions); this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
this.showOtherInfoPanel = argValueToBoolean(raw.showOtherInfoPanel);
this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters); this.unstable_ignoreMimeParameters = argValueToBoolean(raw.unstable_ignoreMimeParameters);

View File

@ -1,10 +0,0 @@
interface ServerStub {
}
/**
* This class produces canned responses for a given OpenAPI spec
*/
export class OpenApiServerStub implements ServerStub {
}

View File

@ -1,6 +0,0 @@
describe('ServerStub', () => {
test('TODO IMPLEMENT ME', () => {
const fn = jest.fn();
expect(fn).toHaveBeenCalled();
});
});