{schema.typePrefix}
{schema.displayType}
- {schema.format && (
-
- {' <'}
- {schema.format}>
-
- )}
+ {schema.format &&
<{schema.format}> }
{schema.title &&
({schema.title}) }
{schema.nullable &&
Nullable }
diff --git a/src/components/PayloadSamples/MediaTypeSamples.tsx b/src/components/PayloadSamples/MediaTypeSamples.tsx
index 2b37e651..9101f2fa 100644
--- a/src/components/PayloadSamples/MediaTypeSamples.tsx
+++ b/src/components/PayloadSamples/MediaTypeSamples.tsx
@@ -21,9 +21,10 @@ export class MediaTypeSamples extends React.Component
{
const sampleView = isJsonLike(mimeType)
? sample =>
: sample =>
- (sample && ) || {
- noSample,
- };
+ (sample !== undefined && (
+
+ )) ||
+ noSample;
const examplesNames = Object.keys(examples);
if (examplesNames.length === 0) {
diff --git a/src/components/Responses/Response.tsx b/src/components/Responses/Response.tsx
index 6f521f4b..2404dce5 100644
--- a/src/components/Responses/Response.tsx
+++ b/src/components/Responses/Response.tsx
@@ -8,6 +8,7 @@ import { DropdownOrLabel } from '../DropdownOrLabel/DropdownOrLabel';
import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
import { Schema } from '../Schema';
+import { Markdown } from '../Markdown/Markdown';
import { ResponseHeaders } from './ResponseHeaders';
import { ResponseDetailsWrap, StyledResponseTitle } from './styled.elements';
@@ -18,11 +19,11 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
};
render() {
- const { headers, type, description, code, expanded, content } = this.props.response;
+ const { headers, type, summary, description, code, expanded, content } = this.props.response;
const mimes =
content === undefined ? [] : content.mediaTypes.filter(mime => mime.schema !== undefined);
- const empty = headers.length === 0 && mimes.length === 0;
+ const empty = headers.length === 0 && mimes.length === 0 && !description;
return (
@@ -30,13 +31,14 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
onClick={this.toggle}
type={type}
empty={empty}
- title={description || ''}
+ title={summary || ''}
code={code}
opened={expanded}
/>
{expanded &&
!empty && (
+ {description && }
{({ schema }) => {
diff --git a/src/components/SideMenu/SideMenu.tsx b/src/components/SideMenu/SideMenu.tsx
index 169c54f3..9aab7310 100644
--- a/src/components/SideMenu/SideMenu.tsx
+++ b/src/components/SideMenu/SideMenu.tsx
@@ -6,6 +6,7 @@ import { IMenuItem, MenuStore } from '../../services/MenuStore';
import { MenuItems } from './MenuItems';
import { PerfectScrollbar } from '../../common-elements/perfect-scrollbar';
+import { RedocAttribution } from './styled.elements';
@observer
export class SideMenu extends React.Component<{ menu: MenuStore }> {
@@ -29,6 +30,11 @@ export class SideMenu extends React.Component<{ menu: MenuStore }> {
) : (
+
+
+ Documentation Powered by ReDoc
+
+
)
}
diff --git a/src/components/SideMenu/styled.elements.ts b/src/components/SideMenu/styled.elements.ts
index ca8d7145..302dca3e 100644
--- a/src/components/SideMenu/styled.elements.ts
+++ b/src/components/SideMenu/styled.elements.ts
@@ -160,3 +160,21 @@ export const MenuItemTitle = withProps<{ width?: string }>(styled.span)`
overflow: hidden;
text-overflow: ellipsis;
`;
+
+export const RedocAttribution = styled.div`
+ font-size: 0.8em;
+ margin-top: ${({ theme }) => `${theme.spacingUnit / 2}px`};
+ padding: ${({ theme }) => `0 ${theme.spacingUnit}px`};
+ text-align: left;
+
+ opacity: 0.7;
+
+ a,
+ a:visited,
+ a:hover {
+ color: ${({ theme }) => theme.colors.text} !important;
+ border-top: 1px solid #e1e1e1;
+ padding-top: 10px;
+ display: block;
+ }
+`;
diff --git a/src/services/OpenAPIParser.ts b/src/services/OpenAPIParser.ts
index cabf56f5..8982bf3d 100644
--- a/src/services/OpenAPIParser.ts
+++ b/src/services/OpenAPIParser.ts
@@ -233,6 +233,15 @@ export class OpenAPIParser {
}
}
+ if (subSchema.items !== undefined) {
+ receiver.items = receiver.items || {};
+ // merge inner properties
+ receiver.items = this.mergeAllOf(
+ { allOf: [receiver.items, subSchema.items] },
+ $ref + '/items',
+ );
+ }
+
if (subSchema.required !== undefined) {
receiver.required = (receiver.required || []).concat(subSchema.required);
}
diff --git a/src/services/models/Operation.ts b/src/services/models/Operation.ts
index f6fa04ee..9059d346 100644
--- a/src/services/models/Operation.ts
+++ b/src/services/models/Operation.ts
@@ -10,7 +10,9 @@ import { OpenAPIExternalDocumentation, OpenAPIServer } from '../../types';
import {
getOperationSummary,
+ getStatusCodeType,
isAbsolutePath,
+ isStatusCode,
JsonPointer,
mergeParams,
sortByRequired,
@@ -99,10 +101,15 @@ export class OperationModel implements IMenuItem {
let hasSuccessResponses = false;
this.responses = Object.keys(operationSpec.responses || [])
.filter(code => {
- if (parseInt(code, 10) >= 100 && parseInt(code, 10) <= 399) {
+ if (code === 'default') {
+ return true;
+ }
+
+ if (getStatusCodeType(code) === 'success') {
hasSuccessResponses = true;
}
- return isNumeric(code) || code === 'default';
+
+ return isStatusCode(code);
}) // filter out other props (e.g. x-props)
.map(code => {
return new ResponseModel(
diff --git a/src/services/models/Response.ts b/src/services/models/Response.ts
index 3f8937cd..c18f4097 100644
--- a/src/services/models/Response.ts
+++ b/src/services/models/Response.ts
@@ -13,6 +13,7 @@ export class ResponseModel {
content?: MediaContentModel;
code: string;
+ summary: string;
description: string;
type: string;
headers: FieldModel[] = [];
@@ -32,7 +33,15 @@ export class ResponseModel {
if (info.content !== undefined) {
this.content = new MediaContentModel(parser, info.content, false, options);
}
- this.description = info.description || '';
+
+ if (info['x-summary'] !== undefined) {
+ this.summary = info['x-summary'];
+ this.description = info.description || '';
+ } else {
+ this.summary = info.description || '';
+ this.description = '';
+ }
+
this.type = getStatusCodeType(code, defaultAsError);
const headers = info.headers;
diff --git a/src/types/index.d.ts b/src/types/index.d.ts
index c02a721c..8103c2ef 100644
--- a/src/types/index.d.ts
+++ b/src/types/index.d.ts
@@ -1,3 +1,8 @@
export * from './open-api';
export type Omit = Pick>;
+declare global {
+ type Dict = {
+ [key: string]: T;
+ };
+}
diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts
index e5ef8872..a5aeec9a 100644
--- a/src/utils/openapi.ts
+++ b/src/utils/openapi.ts
@@ -6,21 +6,35 @@ import {
OpenAPISchema,
Referenced,
} from '../types';
+import { isNumeric } from './helpers';
-export function getStatusCodeType(statusCode: string | number, defaultAsError = false): string {
+function isWildcardStatusCode(statusCode: string | number): statusCode is string {
+ return typeof statusCode === 'string' && /\dxx/i.test(statusCode);
+}
+
+export function isStatusCode(statusCode: string) {
+ return statusCode === 'default' || isNumeric(statusCode) || isWildcardStatusCode(statusCode);
+}
+
+export function getStatusCodeType(statusCode: string, defaultAsError = false): string {
if (statusCode === 'default') {
return defaultAsError ? 'error' : 'success';
}
- if (statusCode < 100 || statusCode > 599) {
+ let code = parseInt(statusCode, 10);
+ if (isWildcardStatusCode(statusCode)) {
+ code *= 100; // parseInt('2xx') parses to 2
+ }
+
+ if (code < 100 || code > 599) {
throw new Error('invalid HTTP code');
}
let res = 'success';
- if (statusCode >= 300 && statusCode < 400) {
+ if (code >= 300 && code < 400) {
res = 'redirect';
- } else if (statusCode >= 400) {
+ } else if (code >= 400) {
res = 'error';
- } else if (statusCode < 200) {
+ } else if (code < 200) {
res = 'info';
}
return res;
diff --git a/yarn.lock b/yarn.lock
index b6666acf..5288d77e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14,7 +14,7 @@
dependencies:
"@babel/highlight" "7.0.0-beta.46"
-"@babel/core@^7.0.0-beta.40":
+"@babel/core@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-beta.47.tgz#b9c164fb9a1e1083f067c236a9da1d7a7d759271"
dependencies:
@@ -98,19 +98,19 @@
esutils "^2.0.2"
js-tokens "^3.0.0"
-"@babel/plugin-syntax-decorators@^7.0.0-beta.42":
+"@babel/plugin-syntax-decorators@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.0.0-beta.47.tgz#a42f10fcd651940bc475d93b3ac23432b4a8a293"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
-"@babel/plugin-syntax-jsx@^7.0.0-beta.42":
+"@babel/plugin-syntax-jsx@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.0.0-beta.47.tgz#f3849d94288695d724bd205b4f6c3c99e4ec24a4"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
-"@babel/plugin-syntax-typescript@^7.0.0-beta.42":
+"@babel/plugin-syntax-typescript@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.0.0-beta.47.tgz#108d4c83ff48ddcb8f0532252a9892e805ddc64c"
dependencies:
@@ -6542,9 +6542,9 @@ mark.js@^8.11.1:
version "8.11.1"
resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5"
-marked@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/marked/-/marked-0.4.0.tgz#9ad2c2a7a1791f10a852e0112f77b571dce10c66"
+marked@0.3.18:
+ version "0.3.18"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.18.tgz#3ef058cd926101849b92a7a7c15db18c7fc76b2f"
math-expression-evaluator@^1.2.14:
version "1.2.17"