Skip to content

Commit

Permalink
feat: Webhooks option to reverse readOnly/writeOnly properties #1720
Browse files Browse the repository at this point in the history
  • Loading branch information
Andriy Zaleskyy committed Aug 27, 2021
1 parent 683eabb commit 7486e70
Show file tree
Hide file tree
Showing 19 changed files with 178 additions and 61 deletions.
4 changes: 2 additions & 2 deletions demo/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1193,7 +1193,7 @@ x-webhooks:
summary: New pet
description: Information about a new pet in the systems
operationId: newPet
tags:
tags:
- pet
requestBody:
content:
Expand All @@ -1202,4 +1202,4 @@ x-webhooks:
$ref: "#/components/schemas/Pet"
responses:
"200":
description: Return a 200 status to indicate that the data was received successfully
description: Return a 200 status to indicate that the data was received successfully
7 changes: 6 additions & 1 deletion demo/playground/hmr-playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ 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,
reverseEventsReadOnlyProps: true,
reverseEventsWriteOnlyProps: true,
};

render(<RedocStandalone specUrl={specUrl} options={options} />, document.getElementById('example'));
2 changes: 1 addition & 1 deletion demo/playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
<redoc id="example"></redoc>
</body>

</html>
</html>
17 changes: 13 additions & 4 deletions src/components/Callbacks/CallbackDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import { OperationModel } from '../../services/models';
import { OperationModel, ReverseEventsRWOProps } from '../../services/models';
import styled from '../../styled-components';
import { Endpoint } from '../Endpoint/Endpoint';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
Expand All @@ -14,12 +14,13 @@ import { CallbackDetailsWrap } from './styled.elements';

export interface CallbackDetailsProps {
operation: OperationModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

@observer
export class CallbackDetails extends React.Component<CallbackDetailsProps> {
render() {
const { operation } = this.props;
const { operation, reverseEventsReadWriteOnly } = this.props;
const { description, externalDocs } = operation;
const hasDescription = !!(description || externalDocs);

Expand All @@ -34,8 +35,16 @@ export class CallbackDetails extends React.Component<CallbackDetailsProps> {
<Endpoint operation={this.props.operation} inverted={true} compact={true} />
<Extensions extensions={operation.extensions} />
<SecurityRequirements securities={operation.security} />
<Parameters parameters={operation.parameters} body={operation.requestBody} />
<ResponsesList responses={operation.responses} isCallback={operation.isCallback} />
<Parameters
parameters={operation.parameters}
body={operation.requestBody}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
<ResponsesList
responses={operation.responses}
isCallback={operation.isCallback}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
</CallbackDetailsWrap>
);
}
Expand Down
17 changes: 14 additions & 3 deletions src/components/Callbacks/CallbackOperation.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import { OperationModel } from '../../services/models';
import { OperationModel, ReverseEventsRWOProps } from '../../services/models';
import { StyledCallbackTitle } from './styled.elements';
import { CallbackDetails } from './CallbackDetails';

export interface CallbackOperationProps {
callbackOperation: OperationModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

@observer
export class CallbackOperation extends React.Component<{ callbackOperation: OperationModel }> {
export class CallbackOperation extends React.Component<CallbackOperationProps> {
toggle = () => {
this.props.callbackOperation.toggle();
};

render() {
const { name, expanded, httpVerb, deprecated } = this.props.callbackOperation;
const { reverseEventsReadWriteOnly } = this.props;

return (
<>
Expand All @@ -23,7 +29,12 @@ export class CallbackOperation extends React.Component<{ callbackOperation: Oper
httpVerb={httpVerb}
deprecated={deprecated}
/>
{expanded && <CallbackDetails operation={this.props.callbackOperation} />}
{expanded &&
<CallbackDetails
operation={this.props.callbackOperation}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
}
</>
);
}
Expand Down
11 changes: 8 additions & 3 deletions src/components/Callbacks/CallbacksList.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import * as React from 'react';

import { CallbackModel } from '../../services/models';
import { CallbackModel, ReverseEventsRWOProps } from '../../services/models';
import styled from '../../styled-components';
import { CallbackOperation } from './CallbackOperation';

export interface CallbacksListProps {
callbacks: CallbackModel[];
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

export class CallbacksList extends React.PureComponent<CallbacksListProps> {
render() {
const { callbacks } = this.props;
const { callbacks, reverseEventsReadWriteOnly } = this.props;

if (!callbacks || callbacks.length === 0) {
return null;
Expand All @@ -22,7 +23,11 @@ export class CallbacksList extends React.PureComponent<CallbacksListProps> {
{callbacks.map(callback => {
return callback.operations.map((operation, index) => {
return (
<CallbackOperation key={`${callback.name}_${index}`} callbackOperation={operation} />
<CallbackOperation
key={`${callback.name}_${index}`}
callbackOperation={operation}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
);
});
})}
Expand Down
22 changes: 16 additions & 6 deletions src/components/Operation/Operation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react';

import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
import { ShareLink } from '../../common-elements/linkify';
import { OperationModel } from '../../services/models';
import { OperationModel, ReverseEventsRWOProps } from '../../services/models';
import styled from '../../styled-components';
import { CallbacksList } from '../Callbacks';
import { CallbackSamples } from '../CallbackSamples/CallbackSamples';
Expand All @@ -30,14 +30,14 @@ const Description = styled.div`

export interface OperationProps {
operation: OperationModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

@observer
export class Operation extends React.Component<OperationProps> {
render() {
const { operation } = this.props;

const { name: summary, description, deprecated, externalDocs, isWebhook } = operation;
const { name: summary, description, deprecated, externalDocs, isWebhook, reverseEventsReadWriteOnly = {} } = operation;
const hasDescription = !!(description || externalDocs);

return (
Expand All @@ -61,9 +61,19 @@ export class Operation extends React.Component<OperationProps> {
)}
<Extensions extensions={operation.extensions} />
<SecurityRequirements securities={operation.security} />
<Parameters parameters={operation.parameters} body={operation.requestBody} />
<ResponsesList responses={operation.responses} />
<CallbacksList callbacks={operation.callbacks} />
<Parameters
parameters={operation.parameters}
body={operation.requestBody}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
<ResponsesList
responses={operation.responses}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
<CallbacksList
callbacks={operation.callbacks}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
</MiddlePanel>
<DarkRightPanel>
{!options.pathInMiddlePanel && !isWebhook && <Endpoint operation={operation} />}
Expand Down
19 changes: 14 additions & 5 deletions src/components/Parameters/Parameters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ParametersGroup } from './ParametersGroup';

import { UnderlinedHeader } from '../../common-elements';

import { MediaContentModel } from '../../services';
import { MediaContentModel, ReverseEventsRWOProps } from '../../services';
import { FieldModel, RequestBodyModel } from '../../services/models';
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
import { Schema } from '../Schema';
Expand All @@ -21,6 +21,7 @@ function safePush(obj, prop, item) {
export interface ParametersProps {
parameters?: FieldModel[];
body?: RequestBodyModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

const PARAM_PLACES = ['path', 'query', 'cookie', 'header'];
Expand All @@ -35,7 +36,7 @@ export class Parameters extends React.PureComponent<ParametersProps> {
}

render() {
const { body, parameters = [] } = this.props;
const { body, parameters = [], reverseEventsReadWriteOnly } = this.props;
if (body === undefined && parameters === undefined) {
return null;
}
Expand All @@ -53,7 +54,13 @@ export class Parameters extends React.PureComponent<ParametersProps> {
{paramsPlaces.map(place => (
<ParametersGroup key={place} place={place} parameters={paramsMap[place]} />
))}
{bodyContent && <BodyContent content={bodyContent} description={bodyDescription} />}
{bodyContent &&
<BodyContent
content={bodyContent}
description={bodyDescription}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
}
</>
);
}
Expand All @@ -67,15 +74,17 @@ function DropdownWithinHeader(props) {
);
}

export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element {
export function BodyContent(props: { content: MediaContentModel; description?: string, reverseEventsReadWriteOnly?: ReverseEventsRWOProps }): JSX.Element {
const { content, description } = props;
const { reverseEventsReadOnlyProps, reverseEventsWriteOnlyProps } = props.reverseEventsReadWriteOnly || {};
const skipReadOnly = !reverseEventsReadOnlyProps;
return (
<MediaTypesSwitch content={content} renderDropdown={DropdownWithinHeader}>
{({ schema }) => {
return (
<>
{description !== undefined && <Markdown source={description} />}
<Schema skipReadOnly={true} key="schema" schema={schema} />
<Schema skipReadOnly={skipReadOnly} skipWriteOnly={reverseEventsWriteOnlyProps} key="schema" schema={schema} />
</>
);
}}
Expand Down
18 changes: 14 additions & 4 deletions src/components/Responses/Response.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import { ResponseModel } from '../../services/models';
import { ResponseModel, ReverseEventsRWOProps } from '../../services/models';
import { ResponseDetails } from './ResponseDetails';
import { ResponseDetailsWrap, StyledResponseTitle } from './styled.elements';

export interface ResponseViewProps {
response: ResponseModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

@observer
export class ResponseView extends React.Component<{ response: ResponseModel }> {
export class ResponseView extends React.Component<ResponseViewProps> {
toggle = () => {
this.props.response.toggle();
};

render() {
const { headers, type, summary, description, code, expanded, content } = this.props.response;
const {
reverseEventsReadWriteOnly, response: { headers, type, summary, description, code, expanded, content }
} = this.props;
const mimes =
content === undefined ? [] : content.mediaTypes.filter(mime => mime.schema !== undefined);

Expand All @@ -30,7 +37,10 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
/>
{expanded && !empty && (
<ResponseDetailsWrap>
<ResponseDetails response={this.props.response} />
<ResponseDetails
response={this.props.response}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>
</ResponseDetailsWrap>
)}
</div>
Expand Down
14 changes: 10 additions & 4 deletions src/components/Responses/ResponseDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';

import { ResponseModel } from '../../services/models';
import { ResponseModel, ReverseEventsRWOProps } from '../../services/models';

import { UnderlinedHeader } from '../../common-elements';
import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
Expand All @@ -10,16 +10,22 @@ import { Schema } from '../Schema';
import { Markdown } from '../Markdown/Markdown';
import { ResponseHeaders } from './ResponseHeaders';

export class ResponseDetails extends React.PureComponent<{ response: ResponseModel }> {
export interface ResponseDetailsProps {
response: ResponseModel;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

export class ResponseDetails extends React.PureComponent<ResponseDetailsProps> {
render() {
const { description, headers, content } = this.props.response;
const { reverseEventsReadWriteOnly = {}, response: { description, headers, content } } = this.props;
const skipWriteOnly = !reverseEventsReadWriteOnly.reverseEventsWriteOnlyProps;
return (
<>
{description && <Markdown source={description} />}
<ResponseHeaders headers={headers} />
<MediaTypesSwitch content={content} renderDropdown={this.renderDropdown}>
{({ schema }) => {
return <Schema skipWriteOnly={true} key="schema" schema={schema} />;
return <Schema skipWriteOnly={skipWriteOnly} key="schema" schema={schema} />;
}}
</MediaTypesSwitch>
</>
Expand Down
13 changes: 8 additions & 5 deletions src/components/Responses/ResponsesList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { l } from '../../services/Labels';
import { ResponseModel } from '../../services/models';
import { ResponseModel, ReverseEventsRWOProps } from '../../services/models';
import styled from '../../styled-components';
import { ResponseView } from './Response';

Expand All @@ -15,11 +15,12 @@ const ResponsesHeader = styled.h3`
export interface ResponseListProps {
responses: ResponseModel[];
isCallback?: boolean;
reverseEventsReadWriteOnly?: ReverseEventsRWOProps;
}

export class ResponsesList extends React.PureComponent<ResponseListProps> {
render() {
const { responses, isCallback } = this.props;
const { responses, isCallback, reverseEventsReadWriteOnly } = this.props;

if (!responses || responses.length === 0) {
return null;
Expand All @@ -28,9 +29,11 @@ export class ResponsesList extends React.PureComponent<ResponseListProps> {
return (
<div>
<ResponsesHeader>{isCallback ? l('callbackResponses') : l('responses')}</ResponsesHeader>
{responses.map(response => {
return <ResponseView key={response.code} response={response} />;
})}
{responses.map(response => <ResponseView
key={response.code}
response={response}
reverseEventsReadWriteOnly={reverseEventsReadWriteOnly}
/>)}
</div>
);
}
Expand Down
Loading

0 comments on commit 7486e70

Please sign in to comment.