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)
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 Specification
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.
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
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
# Cross-Origin Resource Sharing
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.
@ -38,8 +38,9 @@ info:
OAuth2 - an open protocol to allow secure authorization in a simple
and standard method from web, mobile and desktop applications.
<security-definitions />
<security-definitions />
x-opentext-other: foo
version: 1.0.0
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'
@ -82,6 +83,7 @@ paths:
type: integer
format: int64
post:
x-opentext-other: bar
tags:
- pet
summary: Add a new pet to the store

View File

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

View File

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

View File

@ -1,19 +1,19 @@
import * as PropTypes from 'prop-types';
import * as React from 'react';
import { ThemeProvider } from '../../styled-components';
import { OptionsProvider } from '../OptionsProvider';
import { ContentItems } from '../../../src/components';
import { AppStore } from '../../services';
import { ThemeProvider } from '../../styled-components';
import { ApiInfo } from '../ApiInfo/';
import { ApiLogo } from '../ApiLogo/ApiLogo';
import { ContentItems } from '../ContentItems/ContentItems';
import { SideMenu } from '../SideMenu/SideMenu';
import { StickyResponsiveSidebar } from '../StickySidebar/StickyResponsiveSidebar';
import { ApiContentWrap, BackgroundStub, RedocWrap } from './styled.elements';
import { OptionsProvider } from '../OptionsProvider';
import { SearchBox } from '../SearchBox/SearchBox';
import { SideMenu } from '../SideMenu/SideMenu';
import { StickyResponsiveSidebar } from '../StickySidebar/StickyResponsiveSidebar';
import { StoreProvider } from '../StoreBuilder';
import { ApiContentWrap, BackgroundStub, RedocWrap } from './styled.elements';
export interface RedocProps {
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;
onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[];
showOtherInfoPanel?: boolean;
unstable_ignoreMimeParameters?: boolean;
@ -120,6 +121,7 @@ export class RedocNormalizedOptions {
disableSearch: boolean;
onlyRequiredInSamples: boolean;
showExtensions: boolean | string[];
showOtherInfoPanel: boolean;
/* tslint:disable-next-line */
unstable_ignoreMimeParameters: boolean;
@ -147,6 +149,7 @@ export class RedocNormalizedOptions {
this.disableSearch = argValueToBoolean(raw.disableSearch);
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
this.showOtherInfoPanel = argValueToBoolean(raw.showOtherInfoPanel);
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();
});
});