Merge branch 'master' of https://github.com/nutanix/ReDoc into add-validation

This commit is contained in:
Harjeet Singh 2018-06-19 15:37:35 -07:00
commit 4f0bb8c57b
21 changed files with 165 additions and 56 deletions

View File

@ -1,6 +1,6 @@
*
!bundles/*
!typings/*
!typings/**/*
!package.json
!README.md
!LICENSE

View File

@ -15,7 +15,7 @@ env:
addons:
chrome: stable
before_script: npm run bundle
script: npm test && npm run e2e-ci
script: npm test && [ "${TRAVIS_PULL_REQUEST}" = "false" ] && npm run e2e-ci || npm run e2e
after_script: cat ./coverage/lcov.info | coveralls
before_deploy: npm run compile:cli && npm run declarations
deploy:

View File

@ -1,3 +1,26 @@
<a name="2.0.0-alpha.24"></a>
# [2.0.0-alpha.24](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.23...v2.0.0-alpha.24) (2018-06-01)
### Bug Fixes
* temporary downgrade marked as it introduced breaking changes and a few bugs ([902f97a](https://github.com/Rebilly/ReDoc/commit/902f97a))
<a name="2.0.0-alpha.23"></a>
# [2.0.0-alpha.23](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.22...v2.0.0-alpha.23) (2018-05-31)
### Bug Fixes
* **cli:** make positional arguments required and handle errors in serve and bundle manually ([#518](https://github.com/Rebilly/ReDoc/issues/518)) ([370d08a](https://github.com/Rebilly/ReDoc/commit/370d08a))
* fix typings on npm ([d957ad7](https://github.com/Rebilly/ReDoc/commit/d957ad7))
* fix vertical line misaligned in firefox ([bde08f1](https://github.com/Rebilly/ReDoc/commit/bde08f1)), closes [#503](https://github.com/Rebilly/ReDoc/issues/503)
* mergeAllOf takes items into account ([#511](https://github.com/Rebilly/ReDoc/issues/511)) ([47b2177](https://github.com/Rebilly/ReDoc/commit/47b2177))
<a name="2.0.0-alpha.22"></a>
# [2.0.0-alpha.22](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.21...v2.0.0-alpha.22) (2018-05-29)

View File

@ -212,12 +212,13 @@ You can use all of the following options with standalone version on <redoc> tag
## Advanced usage of standalone version
Instead of adding `spec-url` attribute to the `<redoc>` element you can initialize ReDoc via globally exposed `Redoc` object:
```js
Redoc.init(specOrSpecUrl, options, element)
Redoc.init(specOrSpecUrl, options, element, callback?)
```
- `specOrSpecUrl` is either JSON object with specification or an URL to the spec in `JSON` or `YAML` format
- `options` [options object](#redoc-options-object)
- `element` DOM element to put ReDoc into
- `callback` (optional) - callback to be called after Redoc has been fully rendered
```js
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {

View File

@ -32,7 +32,7 @@ const BUNDLES_DIR = dirname(require.resolve('redoc'));
/* tslint:disable-next-line */
YargsParser.command(
'serve [spec]',
'serve <spec>',
'start the server',
yargs => {
yargs.positional('spec', {
@ -60,16 +60,22 @@ YargsParser.command(
return yargs;
},
async argv => {
await serve(argv.port, argv.spec, {
const config = {
ssr: argv.ssr,
watch: argv.watch,
templateFileName: argv.template,
redocOptions: argv.options || {},
});
};
try {
await serve(argv.port, argv.spec, config);
} catch (e) {
handleError(e);
}
},
)
.command(
'bundle [spec]',
'bundle <spec>',
'bundle spec into zero-dependency HTML-file',
yargs => {
yargs.positional('spec', {
@ -99,16 +105,22 @@ YargsParser.command(
return yargs;
},
async argv => {
await bundle(argv.spec, {
const config = {
ssr: true,
output: argv.o,
cdn: argv.cdn,
title: argv.title,
templateFileName: argv.template,
redocOptions: argv.options || {},
});
};
try {
await bundle(argv.spec, config);
} catch (e) {
handleError(e);
}
},
)
)
.demandCommand()
.options('t', {
alias: 'template',
@ -117,10 +129,6 @@ YargsParser.command(
})
.options('options', {
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
})
.fail((message, error) => {
console.log(error.stack);
process.exit(1);
}).argv;
async function serve(port: number, pathToSpec: string, options: Options = {}) {
@ -296,3 +304,8 @@ function isURL(str: string): boolean {
function escapeUnicode(str) {
return str.replace(/\u2028|\u2029/g, m => '\\u202' + (m === '\u2028' ? '8' : '9'));
}
function handleError(error: Error) {
console.error(error.stack);
process.exit(1);
}

4
custom.d.ts vendored
View File

@ -18,10 +18,6 @@ declare module '*.css' {
declare var __REDOC_VERSION__: string;
declare var __REDOC_REVISION__: string;
declare type Dict<T> = {
[key: string]: T;
};
interface Element {
scrollIntoViewIfNeeded(centerIfNeeded?: boolean): void;
}

View File

@ -11,7 +11,7 @@ const demos = [
value: 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/swagger.yaml',
label: 'Google Calendar',
},
{ value: 'https://api.apis.guru/v2/specs/slack.com/1.0.3/swagger.yaml', label: 'Slack' },
{ value: 'https://api.apis.guru/v2/specs/slack.com/1.0.6/swagger.yaml', label: 'Slack' },
{ value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/swagger.yaml', label: 'Zoom.us' },
{
value: 'https://api.apis.guru/v2/specs/graphhopper.com/1.0/swagger.yaml',

View File

@ -200,6 +200,16 @@ Extends OpenAPI [Parameter Object](http://swagger.io/specification/#parameterObj
###### Usage in ReDoc
`x-examples` are rendered in the JSON tab on the right panel of ReDoc.
### Response Object vendor extensions
Extneds OpeanAPI [Response Object](https://swagger.io/specification/#responseObject)
#### x-summary
| Field Name | Type | Description |
| :------------- | :------: | :---------- |
| x-summary | string | a short summary of the response |
###### Usage in ReDoc
If specified, `x-summary` is used as the response button text. Description is rendered under the button.
### Schema Object vendor extensions
Extends OpenAPI [Schema Object](http://swagger.io/specification/#schemaObject)

View File

@ -1,6 +1,6 @@
{
"name": "redoc",
"version": "2.0.0-alpha.22",
"version": "2.0.0-alpha.24",
"description": "ReDoc",
"repository": {
"type": "git",
@ -51,10 +51,10 @@
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD-2-Clause;BSD-3-Clause' --summary"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.40",
"@babel/plugin-syntax-decorators": "^7.0.0-beta.42",
"@babel/plugin-syntax-jsx": "^7.0.0-beta.42",
"@babel/plugin-syntax-typescript": "^7.0.0-beta.42",
"@babel/core": "7.0.0-beta.47",
"@babel/plugin-syntax-decorators": "7.0.0-beta.47",
"@babel/plugin-syntax-jsx": "7.0.0-beta.47",
"@babel/plugin-syntax-typescript": "7.0.0-beta.47",
"@cypress/webpack-preprocessor": "2.0.1",
"@types/dompurify": "^0.0.31",
"@types/enzyme": "^3.1.8",
@ -138,7 +138,7 @@
"json-schema-ref-parser": "^5.0.0",
"lunr": "^2.2.1",
"mark.js": "^8.11.1",
"marked": "^0.4.0",
"marked": "0.3.18",
"mobx": "^4.3.0",
"mobx-react": "^5.0.0",
"openapi-sampler": "1.0.0-beta.12",

View File

@ -117,7 +117,7 @@ export const InnerPropertiesWrap = styled.div`
`;
export const PropertiesTable = styled.table`
border-collapse: collapse;
border-collapse: separate;
border-radius: 3px;
font-size: ${props => props.theme.baseFont.size};

View File

@ -28,12 +28,7 @@ export class FieldDetails extends React.PureComponent<FieldProps> {
<div>
<TypePrefix>{schema.typePrefix}</TypePrefix>
<TypeName>{schema.displayType}</TypeName>
{schema.format && (
<TypeFormat>
{' <'}
{schema.format}>
</TypeFormat>
)}
{schema.format && <TypeFormat> &lt;{schema.format}&gt; </TypeFormat>}
{schema.title && <TypeTitle> ({schema.title}) </TypeTitle>}
<ConstraintsView constraints={schema.constraints} />
{schema.nullable && <NullableLabel> Nullable </NullableLabel>}

View File

@ -21,9 +21,10 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps> {
const sampleView = isJsonLike(mimeType)
? sample => <JsonViewer data={sample} />
: sample =>
(sample && <SourceCodeWithCopy lang={langFromMime(mimeType)} source={sample} />) || {
noSample,
};
(sample !== undefined && (
<SourceCodeWithCopy lang={langFromMime(mimeType)} source={sample} />
)) ||
noSample;
const examplesNames = Object.keys(examples);
if (examplesNames.length === 0) {

View File

@ -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 (
<div>
@ -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 && (
<ResponseDetailsWrap>
{description && <Markdown source={description} />}
<ResponseHeaders headers={headers} />
<MediaTypesSwitch content={content} renderDropdown={this.renderDropdown}>
{({ schema }) => {

View File

@ -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 }> {
) : (
<PerfectScrollbar updateFn={this.saveScrollUpdate}>
<MenuItems items={store.items} onActivate={this.activate} root={true} />
<RedocAttribution>
<a target="_blank" href="https://github.com/Rebilly/ReDoc">
Documentation Powered by ReDoc
</a>
</RedocAttribution>
</PerfectScrollbar>
)
}

View File

@ -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;
}
`;

View File

@ -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);
}

View File

@ -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(

View File

@ -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);
}
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;

View File

@ -1,3 +1,8 @@
export * from './open-api';
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
declare global {
type Dict<T> = {
[key: string]: T;
};
}

View File

@ -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;

View File

@ -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"