mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 06:04:56 +03:00
Update callbacks layout
This commit is contained in:
parent
b1ef91379a
commit
aa5d6c0c8e
|
@ -530,18 +530,19 @@ paths:
|
|||
example: AAA-123-BBB-456
|
||||
callbacks:
|
||||
orderInProgress:
|
||||
'/{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||
'{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||
servers:
|
||||
- url: //petstore.swagger.io/v2
|
||||
description: Default server callback
|
||||
- url: //petstore.swagger.io/sandbox
|
||||
description: Sandbox server callback
|
||||
- url: //callback-url.path-level/v1
|
||||
description: Path level server 1
|
||||
- url: //callback-url.path-level/v2
|
||||
description: Path level server 2
|
||||
post:
|
||||
description: A callback triggered every time an Order is updated status to "inProgress"
|
||||
summary: Order in Progress (Summary)
|
||||
description: A callback triggered every time an Order is updated status to "inProgress" (Description)
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
orderId:
|
||||
|
@ -613,7 +614,12 @@ paths:
|
|||
var_dump($e->getErrors());
|
||||
}
|
||||
put:
|
||||
description: A Put callback triggered
|
||||
description: Order in Progress (Only Description)
|
||||
servers:
|
||||
- url: //callback-url.operation-level/v1
|
||||
description: Operation level server 1 (Path override)
|
||||
- url: //callback-url.operation-level/v2
|
||||
description: Operation level server 2 (Path override)
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
|
@ -656,9 +662,16 @@ paths:
|
|||
type: string
|
||||
example: '123'
|
||||
orderShipped:
|
||||
'/{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||
'{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||
post:
|
||||
description: A callback triggered every time an Order is shipped
|
||||
description: |
|
||||
Very long description
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim id est laborum.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
|
@ -680,8 +693,10 @@ paths:
|
|||
'200':
|
||||
description: Callback successfully processed and no retries will be performed
|
||||
orderDelivered:
|
||||
'/{$request.body#/callbackUrl}?event={$request.body#/eventName}':
|
||||
'http://notificationServer.com?url={$request.body#/callbackUrl}&event={$request.body#/eventName}':
|
||||
post:
|
||||
deprecated: true
|
||||
summary: Order delivered
|
||||
description: A callback triggered every time an Order is delivered to the recipient
|
||||
requestBody:
|
||||
content:
|
||||
|
|
|
@ -6,21 +6,23 @@ import { CallbackDetailsWrap, StyledCallbackTitle } from '../Callbacks/styled.el
|
|||
import { CallbackDetails } from './CallbackDetails';
|
||||
|
||||
@observer
|
||||
// TODO: rename to Callback
|
||||
export class CallbackView extends React.Component<{ callbackOperation: OperationModel }> {
|
||||
toggle = () => {
|
||||
this.props.callbackOperation.toggle();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { name, description, expanded } = this.props.callbackOperation;
|
||||
const { name, expanded, httpVerb, deprecated } = this.props.callbackOperation;
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledCallbackTitle
|
||||
onClick={this.toggle}
|
||||
name={name}
|
||||
description={description}
|
||||
opened={expanded}
|
||||
httpVerb={httpVerb}
|
||||
deprecated={deprecated}
|
||||
/>
|
||||
{expanded && (
|
||||
<CallbackDetailsWrap>
|
||||
|
|
|
@ -3,14 +3,28 @@ import * as React from 'react';
|
|||
import { OperationModel } from '../../services/models';
|
||||
import { OperationItem } from '../ContentItems/ContentItems';
|
||||
import { Endpoint } from '../Endpoint/Endpoint';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import styled from '../../styled-components';
|
||||
|
||||
export class CallbackDetails extends React.PureComponent<{ callbackOperation: OperationModel }> {
|
||||
render() {
|
||||
const description = this.props.callbackOperation.description;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Endpoint operation={this.props.callbackOperation} inverted={true} />
|
||||
{description && (
|
||||
<CallbackDescription>
|
||||
<Markdown compact={true} inline={true} source={description} />
|
||||
</CallbackDescription>
|
||||
)}
|
||||
<Endpoint operation={this.props.callbackOperation} inverted={true} compact={true} />
|
||||
<OperationItem item={this.props.callbackOperation} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CallbackDescription = styled.div`
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
|
|
@ -1,25 +1,46 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { OperationBadge } from '../SideMenu/styled.elements';
|
||||
import { shortenHTTPVerb } from '../../utils/openapi';
|
||||
import styled from '../../styled-components';
|
||||
import { Badge } from '../../common-elements/';
|
||||
import { l } from '../../services/Labels';
|
||||
|
||||
export interface CallbackTitleProps {
|
||||
name: string;
|
||||
description?: string;
|
||||
opened?: boolean;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
httpVerb: string;
|
||||
deprecated?: boolean;
|
||||
}
|
||||
|
||||
export class CallbackTitle extends React.PureComponent<CallbackTitleProps> {
|
||||
render() {
|
||||
const { name, description, opened, className, onClick } = this.props;
|
||||
const { name, opened, className, onClick, httpVerb, deprecated } = this.props;
|
||||
return (
|
||||
<div className={className} onClick={onClick || undefined}>
|
||||
<CallbackTitleWrapper className={className} onClick={onClick || undefined}>
|
||||
<OperationBadgeStyled type={httpVerb}>{shortenHTTPVerb(httpVerb)}</OperationBadgeStyled>
|
||||
<ShelfIcon size={'1.5em'} direction={opened ? 'down' : 'right'} float={'left'} />
|
||||
<strong>{name} </strong>
|
||||
{description && <Markdown compact={true} inline={true} source={description} />}
|
||||
</div>
|
||||
<CallbackNameStyled deprecated={deprecated}>{name}</CallbackNameStyled>
|
||||
{deprecated ? <Badge type="warning"> {l('deprecated')} </Badge> : null}
|
||||
</CallbackTitleWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CallbackTitleWrapper = styled.div`
|
||||
& > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
`;
|
||||
|
||||
const CallbackNameStyled = styled.span<{ deprecated?: boolean }>`
|
||||
text-decoration: ${props => (props.deprecated ? 'line-through' : 'none')};
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const OperationBadgeStyled = styled(OperationBadge)`
|
||||
margin: 0px 5px 0px 0px;
|
||||
`;
|
||||
|
|
|
@ -43,7 +43,7 @@ export class CallbacksSwitch extends React.Component<CallbacksSwitchProps, Callb
|
|||
|
||||
const options = callbacks.map((callback, idx) => {
|
||||
return {
|
||||
label: `[${callback.httpVerb.toUpperCase()}] ${callback.name}`,
|
||||
label: `${callback.httpVerb.toUpperCase()}: ${callback.name}`,
|
||||
value: idx.toString(),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface EndpointProps {
|
|||
|
||||
hideHostname?: boolean;
|
||||
inverted?: boolean;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export interface EndpointState {
|
||||
|
@ -49,7 +50,9 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
|||
{options => (
|
||||
<OperationEndpointWrap>
|
||||
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
||||
<HttpVerb type={operation.httpVerb}> {operation.httpVerb}</HttpVerb>{' '}
|
||||
<HttpVerb type={operation.httpVerb} compact={this.props.compact}>
|
||||
{operation.httpVerb}
|
||||
</HttpVerb>
|
||||
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
|
||||
<ShelfIcon
|
||||
float={'right'}
|
||||
|
|
|
@ -34,14 +34,14 @@ export const EndpointInfo = styled.div<{ expanded?: boolean; inverted?: boolean
|
|||
}
|
||||
`;
|
||||
|
||||
export const HttpVerb = styled.span.attrs((props: { type: string }) => ({
|
||||
export const HttpVerb = styled.span.attrs((props: { type: string; compact?: boolean }) => ({
|
||||
className: `http-verb ${props.type}`,
|
||||
}))<{ type: string }>`
|
||||
font-size: 0.929em;
|
||||
line-height: 20px;
|
||||
background-color: ${(props: any) => props.theme.colors.http[props.type] || '#999999'};
|
||||
}))<{ type: string; compact?: boolean }>`
|
||||
font-size: ${props => (props.compact ? '0.8em' : '0.929em')};
|
||||
line-height: ${props => (props.compact ? '18px' : '20px')};
|
||||
background-color: ${props => props.theme.colors.http[props.type] || '#999999'};
|
||||
color: #ffffff;
|
||||
padding: 3px 10px;
|
||||
padding: ${props => (props.compact ? '2px 8px' : '3px 10px')};
|
||||
text-transform: uppercase;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
margin: 0;
|
||||
|
@ -59,7 +59,6 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
|||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
transition: all 0.25s ease;
|
||||
|
||||
${props => (props.expanded ? '' : 'transform: translateY(-50%) scaleY(0);')}
|
||||
`;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as React from 'react';
|
|||
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
|
||||
import { ShareLink } from '../../common-elements/linkify';
|
||||
import { OperationModel as OperationType } from '../../services/models';
|
||||
import styled from '../../styled-components';
|
||||
import styled, { media } from '../../styled-components';
|
||||
import { CallbacksList } from '../Callbacks';
|
||||
import { CallbackSamples } from '../CallbackSamples/CallbackSamples';
|
||||
import { Endpoint } from '../Endpoint/Endpoint';
|
||||
|
@ -18,22 +18,6 @@ import { ResponsesList } from '../Responses/ResponsesList';
|
|||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||
|
||||
const CallbackMiddlePanel = styled(MiddlePanel)`
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const OperationRow = styled(Row)`
|
||||
backface-visibility: hidden;
|
||||
contain: content;
|
||||
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const Description = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||
`;
|
||||
|
||||
export interface OperationProps {
|
||||
operation: OperationType;
|
||||
}
|
||||
|
@ -70,7 +54,7 @@ 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} />
|
||||
<ResponsesList responses={operation.responses} isCallback={operation.isCallback} />
|
||||
<CallbacksList callbacks={operation.callbacks} />
|
||||
</AdaptiveMiddlePanel>
|
||||
{!operation.isCallback && (
|
||||
|
@ -89,3 +73,22 @@ export class Operation extends React.Component<OperationProps> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CallbackMiddlePanel = styled(MiddlePanel)`
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
${() => media.lessThan('medium', true)`
|
||||
padding: 0
|
||||
`};
|
||||
`;
|
||||
|
||||
const OperationRow = styled(Row)`
|
||||
backface-visibility: hidden;
|
||||
contain: content;
|
||||
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const Description = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||
`;
|
||||
|
|
|
@ -13,11 +13,12 @@ const ResponsesHeader = styled.h3`
|
|||
|
||||
export interface ResponseListProps {
|
||||
responses: ResponseModel[];
|
||||
isCallback?: boolean;
|
||||
}
|
||||
|
||||
export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
||||
render() {
|
||||
const { responses } = this.props;
|
||||
const { responses, isCallback } = this.props;
|
||||
|
||||
if (!responses || responses.length === 0) {
|
||||
return null;
|
||||
|
@ -25,7 +26,7 @@ export class ResponsesList extends React.PureComponent<ResponseListProps> {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<ResponsesHeader> Responses </ResponsesHeader>
|
||||
<ResponsesHeader>{isCallback ? 'Callback responses' : 'Responses'}</ResponsesHeader>
|
||||
{responses.map(response => {
|
||||
return <ResponseView key={response.code} response={response} />;
|
||||
})}
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('Components', () => {
|
|||
|
||||
it('should correctly render CallbackTitle', () => {
|
||||
const callbackTitleViewElement = shallow(
|
||||
<CallbackTitle name={'Test'} className={'.test'} onClick={undefined} />,
|
||||
<CallbackTitle name={'Test'} className={'.test'} onClick={undefined} httpVerb={'get'} />,
|
||||
).getElement();
|
||||
expect(callbackTitleViewElement.props).toBeDefined();
|
||||
expect(callbackTitleViewElement.props.className).toEqual('.test');
|
||||
|
|
|
@ -39,7 +39,6 @@ export class CallbackModel {
|
|||
undefined,
|
||||
options,
|
||||
true,
|
||||
this.name,
|
||||
);
|
||||
|
||||
this.operations.push(operation);
|
||||
|
|
|
@ -86,7 +86,6 @@ export class OperationModel implements IMenuItem {
|
|||
parent: GroupModel | undefined,
|
||||
private options: RedocNormalizedOptions,
|
||||
isCallback: boolean = false,
|
||||
callbackEventName?: string,
|
||||
) {
|
||||
this.pointer = JsonPointer.compile(['paths', operationSpec.pathName, operationSpec.httpVerb]);
|
||||
|
||||
|
@ -112,22 +111,21 @@ export class OperationModel implements IMenuItem {
|
|||
JsonPointer.compile(['paths', operationSpec.pathName]),
|
||||
);
|
||||
|
||||
this.name = getOperationSummary(operationSpec);
|
||||
|
||||
if (this.isCallback) {
|
||||
// NOTE: Use callback's event name as the view label, not the operationID.
|
||||
this.name = callbackEventName || getOperationSummary(operationSpec);
|
||||
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||
// Can be defined individually per-callback in the specification. Defaults to none.
|
||||
this.security = (operationSpec.security || []).map(
|
||||
security => new SecurityRequirementModel(security, parser),
|
||||
);
|
||||
|
||||
// TODO: update getting pathInfo
|
||||
// TODO: update getting pathInfo for overriding servers on path level
|
||||
this.servers = normalizeServers(
|
||||
'',
|
||||
operationSpec.servers || (pathInfo && pathInfo.servers) || [],
|
||||
);
|
||||
} else {
|
||||
this.name = getOperationSummary(operationSpec);
|
||||
this.security = (operationSpec.security || parser.spec.security || []).map(
|
||||
security => new SecurityRequirementModel(security, parser),
|
||||
);
|
||||
|
@ -256,6 +254,7 @@ export class OperationModel implements IMenuItem {
|
|||
|
||||
@memoize
|
||||
get callbacks() {
|
||||
console.log('this.operationSpec.callbacks', this.operationSpec.callbacks);
|
||||
return Object.keys(this.operationSpec.callbacks || []).map(callbackEventName => {
|
||||
return new CallbackModel(
|
||||
this.parser,
|
||||
|
|
Loading…
Reference in New Issue
Block a user