Merge branch 'master' into max-sample-depth-option

This commit is contained in:
Mark Theisen 2021-08-31 09:15:22 -05:00 committed by GitHub
commit 974352fe5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 948 additions and 287 deletions

22
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,22 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'type: bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Minimal reproducible OpenAPI snippet(if possible)**
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Describe the problem to be solved**
A clear and concise description of what problem to be solved
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

13
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,13 @@
## What/Why/How?
## Reference
## Testing
## Screenshots (optional)
## Check yourself
- [ ] Code is linted
- [ ] Tested
- [ ] All new/updated code is covered with tests

6
.github/sync.yml vendored Normal file
View File

@ -0,0 +1,6 @@
group:
- files:
- source: docs/
dest: docs/redoc
repos: |
Redocly/docs

45
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: Publish Docker image
on:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to GitHub Packages
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=ghcr.io/redocly/redoc/cli
VERSION=edge
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
elif [[ $GITHUB_REF == refs/heads/* ]]; then
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
elif [[ $GITHUB_REF == refs/pull/* ]]; then
VERSION=pr-${{ github.event.number }}
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [ "${{ github.event_name }}" = "push" ]; then
TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
fi
echo ::set-output name=version::${VERSION}
echo ::set-output name=tags::${TAGS}
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
- name: Push to GitHub Packages
uses: docker/build-push-action@v2
with:
context: ./cli
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.prep.outputs.tags }}

18
.github/workflows/sync.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Sync Files
on:
push:
branches:
- master
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@master
- name: Run GitHub File Sync
uses: Redocly/repo-file-sync-action@master
with:
GH_PAT: ${{ secrets.GH_PAT }}
COMMIT_PREFIX: "sync:"
SKIP_PR: true

View File

@ -1,3 +1,39 @@
# [2.0.0-rc.56](https://github.com/Redocly/redoc/compare/v2.0.0-rc.53...v2.0.0-rc.56) (2021-08-11)
### Bug Fixes
* handle empty object in security array ([#1678](https://github.com/Redocly/redoc/issues/1678)) ([9e1ea70](https://github.com/Redocly/redoc/commit/9e1ea703e56a71567b13d0d22e2d69945a22de4d))
* hideLoading options in redoc standalone ([#1709](https://github.com/Redocly/redoc/issues/1709)) ([6a52a16](https://github.com/Redocly/redoc/commit/6a52a16d5b75a2955da7217c4a264f0fa8e98c89))
* improve openapi 3.1 ([#1700](https://github.com/Redocly/redoc/issues/1700)) ([cd2d6f7](https://github.com/Redocly/redoc/commit/cd2d6f76e87c8385786a9c8e51c0d11c79d9707c))
- show contentEncoding on fields
- crash with OpenAPI 3.1 type as array of strings in requestBody
- nullable label not shown
* nullable object's fields were missing ([#1721](https://github.com/Redocly/redoc/issues/1721)) ([ddf297b](https://github.com/Redocly/redoc/commit/ddf297b11269ef515bd62771912a5609721d5e39))
### Features
* add github action to build docker images and push to ghcr.io on release ([#1614](https://github.com/Redocly/redoc/issues/1614)) ([919a5f0](https://github.com/Redocly/redoc/commit/919a5f02fb94ca869011d5eaf63ee71b61b60150))
* add yaml highlight ([#1684](https://github.com/Redocly/redoc/issues/1684)) ([d724440](https://github.com/Redocly/redoc/commit/d72444008533623c87f238fe8758b1dd518b89eb))
* added localization for some labels ([#1675](https://github.com/Redocly/redoc/issues/1675)) ([ec50858](https://github.com/Redocly/redoc/commit/ec50858ec47af08c5fe553266fe3c209fba97eae))
# [2.0.0-rc.55](https://github.com/Redocly/redoc/compare/v2.0.0-rc.54...v2.0.0-rc.55) (2021-07-01)
### Bug Fixes
* broken linkify ([3df72fb](https://github.com/Redocly/redoc/commit/3df72fb99ff24fb9a551565b7568d96f8614ed6f)), closes [#1655](https://github.com/Redocly/redoc/issues/1655)
* fix accidentally removed onLoaded ([b41a8b4](https://github.com/Redocly/redoc/commit/b41a8b4ac714084dc25de7914fa1f99386e907e2)), closes [#1656](https://github.com/Redocly/redoc/issues/1656)
### Features
* added git folder sync config ([a69f0fb](https://github.com/Redocly/redoc/commit/a69f0fb00986a04c812ab273711e8f3501b98139))
# [2.0.0-rc.54](https://github.com/Redocly/redoc/compare/v2.0.0-rc.53...v2.0.0-rc.54) (2021-06-09) # [2.0.0-rc.54](https://github.com/Redocly/redoc/compare/v2.0.0-rc.53...v2.0.0-rc.54) (2021-06-09)

View File

@ -72,6 +72,7 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d
- [Commbox](https://www.commbox.io/api/) - [Commbox](https://www.commbox.io/api/)
- [APIs.guru](https://apis.guru/api-doc/) - [APIs.guru](https://apis.guru/api-doc/)
- [FastAPI](https://github.com/tiangolo/fastapi) - [FastAPI](https://github.com/tiangolo/fastapi)
- [BoxKnight](https://www.docs.boxknight.com/)
## Deployment ## Deployment

View File

@ -3,20 +3,29 @@
**[ReDoc](https://github.com/Redocly/redoc)'s Command Line Interface** **[ReDoc](https://github.com/Redocly/redoc)'s Command Line Interface**
## Installation ## Installation
You can use redoc cli by installing `redoc-cli` globally or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b).
You can use `redoc-cli` by installing [the package](https://www.npmjs.com/package/redoc-cli) globally,
or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b).
## Usage ## Usage
Two following commands are available: The two following commands are available:
- `redoc-cli serve [spec]` - starts the server with `spec` rendered with ReDoc. Supports SSR mode (`--ssr`) and can watch the spec (`--watch`) - `redoc-cli serve [spec]` - starts the server with `spec` rendered with ReDoc.
- `redoc-cli bundle [spec]` - bundles spec and ReDoc into **zero-dependency** HTML file. Supports a server-side rendering mode (`--ssr`),
and can watch the spec (`--watch`) to automatically reload the page whenever it changes.
- `redoc-cli bundle [spec]` - bundles `spec` and ReDoc into a **zero-dependency** HTML file.
Some examples: Some examples:
- Bundle with main color changed to `orange`: <br> `$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange` - Bundle with the main color changed to `orange`:<br/>
- Serve with `nativeScrollbars` option set to true: <br> `$ redoc-cli serve [spec] --options.nativeScrollbars` `$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange`
- Bundle using custom template (check [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for reference): <br> `$ redoc-cli bundle [spec] -t custom.hbs` - Serve with the `nativeScrollbars` option set to true:<br/>
- Bundle using custom template and add custom `templateOptions`: <br> `$ redoc-cli bundle [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"` `$ redoc-cli serve [spec] --options.nativeScrollbars`
- Bundle using a custom [Handlebars](https://handlebarsjs.com/) template
(check the [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for an example):<br/>
`$ redoc-cli bundle [spec] -t custom.hbs`
- Bundle using a custom template and add custom `templateOptions`:<br/>
`$ redoc-cli bundle [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
For more details run `redoc-cli --help`. For more details, run `redoc-cli --help`.

116
cli/npm-shrinkwrap.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "redoc-cli", "name": "redoc-cli",
"version": "0.12.1", "version": "0.12.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "redoc-cli", "name": "redoc-cli",
"version": "0.12.1", "version": "0.12.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
@ -17,7 +17,7 @@
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"redoc": "2.0.0-rc.54", "redoc": "2.0.0-rc.56",
"styled-components": "^5.3.0", "styled-components": "^5.3.0",
"yargs": "^17.0.1" "yargs": "^17.0.1"
}, },
@ -605,17 +605,6 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
}, },
"node_modules/clipboard": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
"optional": true,
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -782,12 +771,6 @@
"resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz",
"integrity": "sha1-/UPHNelnuAEzBohKVvvmZZlraBc=" "integrity": "sha1-/UPHNelnuAEzBohKVvvmZZlraBc="
}, },
"node_modules/delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"node_modules/des.js": { "node_modules/des.js": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@ -976,15 +959,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"dependencies": {
"delegate": "^3.1.2"
}
},
"node_modules/handlebars": { "node_modules/handlebars": {
"version": "4.7.7", "version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
@ -1602,12 +1576,9 @@
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
}, },
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.23.0", "version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"optionalDependencies": {
"clipboard": "^2.0.0"
}
}, },
"node_modules/process": { "node_modules/process": {
"version": "0.11.10", "version": "0.11.10",
@ -1780,9 +1751,9 @@
} }
}, },
"node_modules/redoc": { "node_modules/redoc": {
"version": "2.0.0-rc.54", "version": "2.0.0-rc.56",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.54.tgz", "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz",
"integrity": "sha512-xwukaWdoktkDAoQuhajekdC54+/lSLwIUqJCNSTVEjeYEuZPq2tFUj9H5SBt8/YSq5UF/zOmDQrXPWMgildQpQ==", "integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.14.0", "@babel/runtime": "^7.14.0",
"@redocly/openapi-core": "^1.0.0-beta.50", "@redocly/openapi-core": "^1.0.0-beta.50",
@ -1802,7 +1773,7 @@
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"perfect-scrollbar": "^1.5.1", "perfect-scrollbar": "^1.5.1",
"polished": "^4.1.3", "polished": "^4.1.3",
"prismjs": "^1.23.0", "prismjs": "^1.24.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"slugify": "~1.4.7", "slugify": "~1.4.7",
@ -1890,12 +1861,6 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"node_modules/select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"node_modules/setimmediate": { "node_modules/setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@ -2122,12 +2087,6 @@
"node": ">=0.6.0" "node": ">=0.6.0"
} }
}, },
"node_modules/tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"node_modules/to-arraybuffer": { "node_modules/to-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@ -2853,17 +2812,6 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
}, },
"clipboard": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
"optional": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"cliui": { "cliui": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -3010,12 +2958,6 @@
"resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz",
"integrity": "sha1-/UPHNelnuAEzBohKVvvmZZlraBc=" "integrity": "sha1-/UPHNelnuAEzBohKVvvmZZlraBc="
}, },
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"des.js": { "des.js": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@ -3169,15 +3111,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
}, },
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"requires": {
"delegate": "^3.1.2"
}
},
"handlebars": { "handlebars": {
"version": "4.7.7", "version": "4.7.7",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
@ -3648,12 +3581,9 @@
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
}, },
"prismjs": { "prismjs": {
"version": "1.23.0", "version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"requires": {
"clipboard": "^2.0.0"
}
}, },
"process": { "process": {
"version": "0.11.10", "version": "0.11.10",
@ -3811,9 +3741,9 @@
} }
}, },
"redoc": { "redoc": {
"version": "2.0.0-rc.54", "version": "2.0.0-rc.56",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.54.tgz", "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.56.tgz",
"integrity": "sha512-xwukaWdoktkDAoQuhajekdC54+/lSLwIUqJCNSTVEjeYEuZPq2tFUj9H5SBt8/YSq5UF/zOmDQrXPWMgildQpQ==", "integrity": "sha512-ir2TtQ2d/1FqZWIoLmUZ3qvAAnO6jg8dt0SV75TanmfCXpEABcElXWH3mtUf6qKlvgDVt40diDCVuSvyPPxkAw==",
"requires": { "requires": {
"@babel/runtime": "^7.14.0", "@babel/runtime": "^7.14.0",
"@redocly/openapi-core": "^1.0.0-beta.50", "@redocly/openapi-core": "^1.0.0-beta.50",
@ -3833,7 +3763,7 @@
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"perfect-scrollbar": "^1.5.1", "perfect-scrollbar": "^1.5.1",
"polished": "^4.1.3", "polished": "^4.1.3",
"prismjs": "^1.23.0", "prismjs": "^1.24.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"slugify": "~1.4.7", "slugify": "~1.4.7",
@ -3892,12 +3822,6 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"setimmediate": { "setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@ -4083,12 +4007,6 @@
"setimmediate": "^1.0.4" "setimmediate": "^1.0.4"
} }
}, },
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"to-arraybuffer": { "to-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "redoc-cli", "name": "redoc-cli",
"version": "0.12.1", "version": "0.12.3",
"description": "ReDoc's Command Line Interface", "description": "ReDoc's Command Line Interface",
"main": "index.js", "main": "index.js",
"bin": "index.js", "bin": "index.js",
@ -19,7 +19,7 @@
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"redoc": "2.0.0-rc.54", "redoc": "2.0.0-rc.56",
"styled-components": "^5.3.0", "styled-components": "^5.3.0",
"yargs": "^17.0.1" "yargs": "^17.0.1"
}, },

View File

@ -16,7 +16,8 @@ RUN npm ci --no-optional --ignore-scripts
# copy only required for the build files # copy only required for the build files
COPY src /build/src COPY src /build/src
COPY webpack.config.ts tsconfig.json custom.d.ts /build/ COPY webpack.config.ts tsconfig.json custom.d.ts /build/
COPY config/webpack-utils.ts /build/config/
COPY typings/styled-patch.d.ts /build/typings/styled-patch.d.ts COPY typings/styled-patch.d.ts /build/typings/styled-patch.d.ts
RUN npm run bundle:standalone RUN npm run bundle:standalone

View File

@ -15,7 +15,7 @@ const demos = [
value: 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/openapi.yaml', value: 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/openapi.yaml',
label: 'Google Calendar', label: 'Google Calendar',
}, },
{ value: 'https://api.apis.guru/v2/specs/slack.com/1.5.0/openapi.yaml', label: 'Slack' }, { value: 'https://api.apis.guru/v2/specs/slack.com/1.7.0/openapi.yaml', label: 'Slack' },
{ value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/openapi.yaml', label: 'Zoom.us' }, { value: 'https://api.apis.guru/v2/specs/zoom.us/2.0.0/openapi.yaml', label: 'Zoom.us' },
{ value: 'https://docs.graphhopper.com/openapi.json', label: 'GraphHopper' }, { value: 'https://docs.graphhopper.com/openapi.json', label: 'GraphHopper' },
]; ];
@ -77,7 +77,7 @@ class DemoApp extends React.Component<
let proxiedUrl = specUrl; let proxiedUrl = specUrl;
if (specUrl !== DEFAULT_SPEC) { if (specUrl !== DEFAULT_SPEC) {
proxiedUrl = cors proxiedUrl = cors
? '\\\\cors.apis.guru/' + urlResolve(window.location.href, specUrl) ? '\\\\cors.redoc.ly/' + urlResolve(window.location.href, specUrl)
: specUrl; : specUrl;
} }
return ( return (

View File

@ -1166,6 +1166,11 @@ components:
description: User status description: User status
type: integer type: integer
format: int32 format: int32
image:
description: User image
type: string
contentEncoding: base64
contentMediaType: image/png
xml: xml:
name: User name: User
requestBodies: requestBodies:

View File

@ -1193,7 +1193,7 @@ x-webhooks:
summary: New pet summary: New pet
description: Information about a new pet in the systems description: Information about a new pet in the systems
operationId: newPet operationId: newPet
tags: tags:
- pet - pet
requestBody: requestBody:
content: content:
@ -1202,4 +1202,4 @@ x-webhooks:
$ref: "#/components/schemas/Pet" $ref: "#/components/schemas/Pet"
responses: responses:
"200": "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

112
docs/quickstart/cli.md Normal file
View File

@ -0,0 +1,112 @@
---
title: Using the Redoc CLI
---
# Using the Redoc CLI
With Redoc's command-line interface you can bundle your OpenAPI definition and API documentation
(made with Redoc) into a zero-dependency HTML file and locally render your
OpenAPI definition with Redoc.
## Step 1 - Install Redoc CLI
You can install the `redoc-cli` package globally using one of the following package managers:
- [npm](https://docs.npmjs.com/about-npm)
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
Or you can install `redoc-cli` using [npx](https://www.freecodecamp.org/news/npm-vs-npx-whats-the-difference/).
### Install Redoc CLI with yarn
To install the `redoc-cli` package globally with yarn:
```bash
yarn global add redoc-cli
```
### Install Redoc with npm
To install the `redoc-cli` package globally with npm:
```bash
npm i -g redoc-cli
```
### Install with `npx`
To install the `redoc-cli` package locally with `npx`, navigate to your project
directory in your terminal, then use the following command:
```bash
npx redoc-cli
```
## Step 2 - Use the CLI
### Redoc CLI commands
The CLI includes the following commands:
- **`redoc-cli serve [spec]`:** Starts a local server with Redoc. You must include the required parameter, spec, which is
a reference to an OpenAPI definition. Options include:
- `--ssr`: Implements a server-side rendering model.
- `--watch`: Automatically reloads the server while you edit your OpenAPI definition.
- `--options`: Customizes your output using [Redoc options](https://redoc.ly/docs/api-reference-docs/configuration/).
To add nested options, use dot notation.
- **`redoc-cli bundle [spec]`:** Bundles `spec` and Redoc into a zero-dependency HTML file. Options include:
- `-t` or `--template`: Uses custom [Handlebars](https://handlebarsjs.com/) templates to render your OpenAPI definition.
- `--templateOptions`: Adds template options you want to pass to your
custom Handlebars template. To add options, use dot notation.
- **`--help`:** Prints help text for the Redoc CLI commands and options.
- **`--version`:** Prints the version of the `redoc-cli` package you have installed.
### Redoc CLI examples
#### Bundle
Bundle with the main color changed to `orange`:
```bash
redoc-cli bundle openapi.yaml --options.theme.colors.primary.main=orange
```
Bundle using a custom Handlebars template and add custom `templateOptions`:
```bash
redoc-cli bundle http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description"
```
Sample Handlebars template:
```handlebars
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8" />
<title>{{title}}</title>
<!-- needed for adaptive design -->
<meta description="{{{templateOptions.metaDescription}}}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
padding: 0;
margin: 0;
}
</style>
{{{redocHead}}}
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
</head>
<body>
{{{redocHTML}}}
</body>
</html>
```
#### Serve
Serve with the `nativeScrollbars` option set to `true`:
```bash
redoc-cli serve openapi/dist.yaml --options.nativeScrollbars
```

39
docs/quickstart/docker.md Normal file
View File

@ -0,0 +1,39 @@
---
title: Using the Redoc Docker image
---
# Using the Redoc Docker image
Redoc is available as a pre-built Docker image in [Docker Hub](https://hub.docker.com/r/redocly/redoc/).
If you have [Docker](https://docs.docker.com/get-docker/) installed, pull the image with the following command:
```docker
docker pull redocly/redoc
```
Then run the image with the following command:
```docker
docker run -p 8080:80 redocly/redoc
```
The preview starts on port 8080, based on the port used in the command,
and can be accessed at `http://localhost:8080`.
To exit the preview, use `control+C`.
By default Redoc starts with a demo Swagger Petstore OpenAPI definition located at
http://petstore.swagger.io/v2/swagger.json. You can update this URL using
the environment variable `SPEC_URL`.
For example:
```bash
docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/redoc
```
## Using a Dockerfile
You can also create a Dockerfile with some predefined environment variables. Check out
a sample [Dockerfile](https://github.com/Redocly/redoc/blob/master/config/docker/Dockerfile)
in our code repo.

214
docs/quickstart/html.md Normal file
View File

@ -0,0 +1,214 @@
---
title: Using the Redoc HTML element
---
# Using the Redoc HTML element
## TL;DR final code example
```html
<!DOCTYPE html>
<html>
<head>
<title>Redoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
Redoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='http://petstore.swagger.io/v2/swagger.json'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
</body>
</html>
```
:::attention Running Redoc locally requires an HTTP server
Loading local OpenAPI definitions is impossible without running a web server because of issues with
[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and
other security reasons.
:::
### Running Redoc locally
If you want to view your Redoc output locally, you can simulate an HTTP server.
#### Using Redocly OpenAPI CLI
Redocly OpenAPI CLI is an open source command-line tool that includes a command
for simulating an HTTP server to provide a preview of your OpenAPI definition locally.
If you have [OpenAPI CLI](https://redoc.ly/docs/cli/#installation-and-usage) installed, `cd` into your
project directory and run the following command:
```bash
openapi preview-docs openapi.yaml
```
By default, without providing a port, the preview starts on port 8080, and can be accessed at `http://localhost:8080`.
To exit the preview, use `control+C`.
#### Using Python
If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your
project directory and run the following command:
```python
python3 -m http.server
```
If you have [Python 2](https://www.python.org/downloads/) installed, `cd` into your
project directory and run the following command:
```python
python -m SimpleHTTPServer 8000
```
The output after entering the command provides the local URL where the preview can be accessed.
To exit the preview, use `control-C`.
#### Using Node.js
If you have [Node.js](https://nodejs.org/en/download/) installed, install `http-server`
using the following npm command:
```bash
npm install -g http-server
```
Then, `cd` into your project directory and run the following command:
```node
http-server
```
The output after entering the command provides the local URL where the preview can be accessed.
To exit the preview, use `control-C`.
## Step 1 - Install Redoc
You can install Redoc using one of the following package managers:
- [npm](https://docs.npmjs.com/about-npm)
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
:::attention Initialize your package manager
If you do not have a `package.json` file in your project directory,
you need to add one by initializing npm or yarn in your project. Use the command `npm init` for npm,
or `yarn init` for yarn. These initialization commands will lead you through the process
of creating a `package.json` file in your project.
For more information, see
[Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file)
in the npm documentation or [Yarn init](https://classic.yarnpkg.com/en/docs/cli/init/)
in the yarn documentation.
:::
### Install Redoc with yarn
After navigating to your project directory in your terminal, use the following command:
```bash
yarn add redoc
```
### Install Redoc with npm
After navigating to your project directory in your terminal, use the following command:
```bash
npm i redoc
```
## Step 2 - Reference the Redoc script
You can reference the Redoc script using either a link to the files hosted on a CDN
or the files located in your `node modules` folder.
### CDN link
To reference the Redoc script with a CDN link:
```html
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
```
### Node modules link
To reference the Redoc script with a node modules link:
```html
<script src="node_modules/redoc/bundles/redoc.standalone.js"> </script>
```
## Step 3 - Add the <redoc> element
You can add the <redoc> element to your HTML page and reference your OpenAPI
definition using the `spec-url` attribute, or you can initialize Redoc using
a globally exposed Redoc object.
### Using the `spec-url` attribute
To add the <redoc> element with the `spec-url` attribute:
```html
<redoc spec-url="url/to/your/spec"></redoc>
```
#### Examples
```html
<redoc spec-url="http://petstore.swagger.io/v2/swagger.json"></redoc>
```
You can also use a local file (JSON or YAML) in your project, for instance:
```html
<redoc spec-url="dist.json"></redoc>
```
### Using a Redoc object
To add the <redoc> element with a globally exposed Redoc object:
```js
Redoc.init(specOrSpecUrl, options, element, callback)
```
- `specOrSpecUrl`: Either a JSON object with the OpenAPI definition or a URL to the
definition in JSON or YAML format.
- `options`: See [options object](https://redoc.ly/docs/api-reference-docs/configuration/) reference.
- `element`: DOM element Redoc will be inserted into.
- `callback`(optional): Callback to be called after Redoc has been fully rendered.
It is also called on errors with `error` as the first argument.
#### Examples
```html
<script>
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
scrollYOffset: 50
}, document.getElementById('redoc-container'))
</script>
```
You can also use a local file (JSON or YAML) in your project, for instance:
```html
<script>
Redoc.init('dist.yaml', {
scrollYOffset: 50
}, document.getElementById('redoc-container'))
</script>
```

44
docs/quickstart/intro.md Normal file
View File

@ -0,0 +1,44 @@
---
title: Redoc quickstart guide
---
# Redoc quickstart guide
This guide includes step-by-step instructions for how to get started using
Redoc to render your OpenAPI definition.
Redoc offers multiple options for rendering your OpenAPI definition.
You should select the option that best fits your needs.
The following options are supported:
- **[Live demo](https://redocly.github.io/redoc/):**
The live demo offers a fast way to see how your OpenAPI will render with Redoc.
- **[HTML element](./html.md):**
Using the HTML element works well for typical website deployments.
- **[React component](./react.md):**
Using the React component is an option for users with a React-based application.
- **[Docker image](./docker.md):**
Using the Docker image works in a container-based deployment.
- **[CLI](./cli.md):**
Using the CLI is an option for users who prefer to use a command-line interface.
## Before you start
You will need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions:
- OpenAPI 3.0
- [Rebilly Users OpenAPI Definition](https://raw.githubusercontent.com/Rebilly/api-definitions/main/openapi/users.yaml)
- [Swagger Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json)
- OpenAPI 2.0
- [Thingful OpenAPI Definition](https://raw.githubusercontent.com/thingful/openapi-spec/master/spec/swagger.yaml)
- [Fitbit Plus OpenAPI Definition](https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml)
For more information on the OpenAPI specification, refer to the [Learning OpenAPI 3](https://redoc.ly/docs/resources/learning-openapi/)
section in the documentation.
## Live demo online
If you want to see how ReDoc will render your OpenAPI definition, you can try it out online at https://redocly.github.io/redoc/.
A version of the Swagger Petstore API is displayed by default. To test it with your own OpenAPI definition, enter the URL for your
definition and select the **TRY IT** button.

78
docs/quickstart/react.md Normal file
View File

@ -0,0 +1,78 @@
---
title: Using the Redoc React component
---
# Using the Redoc React component
## Before you start
Install the following dependencies required by Redoc if you do not already have them installed:
- `react`
- `react-dom`
- `mobx`
- `styled-components`
- `core-js`
If you have npm installed, you can install these dependencies using the following command:
```js
npm i react react-dom mobx styled-components core-js
```
## Step 1 - Import the `RedocStandalone` component
```js
import { RedocStandalone } from 'redoc';
```
## Step 2 - Use the component
You can either link to your OpenAPI definition with a URL, using the following format:
```react
<RedocStandalone specUrl="url/to/your/spec"/>
```
Or you can pass your OpenAPI definition as an object, using the following format:
```js
<RedocStandalone spec={/* spec as an object */}/>
```
## Optional - Pass options
Options can be passed into the RedocStandalone component to alter how it renders.
For example:
```js
<RedocStandalone
specUrl="http://petstore.swagger.io/v2/swagger.json"
options={{
nativeScrollbars: true,
theme: { colors: { primary: { main: '#dd5522' } } },
}}
/>
```
For more information on configuration options, refer to the
[Configuration options for Reference docs](https://redoc.ly/docs/api-reference-docs/configuration/)
section of the documentation. Options available for Redoc are noted,
"Supported in Redoc CE".
## Optional - Specify `onLoaded` callback
You can also specify the `onLoaded` callback, which is called each time Redoc
is fully rendered or when an error occurs (with an error as the first argument).
```js
<RedocStandalone
specUrl="http://petstore.swagger.io/v2/swagger.json"
onLoaded={error => {
if (!error) {
console.log('Yay!');
}
}}
/>
```

13
docs/sidebars.yaml Normal file
View File

@ -0,0 +1,13 @@
redoc:
- group: Quickstart
expanded: false
page: redoc/quickstart/intro.md
pages:
- label: HTML element
page: redoc/quickstart/html.md
- label: React component
page: redoc/quickstart/react.md
- label: Docker image
page: redoc/quickstart/docker.md
- label: Command-line interface
page: redoc/quickstart/cli.md

100
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "redoc", "name": "redoc",
"version": "2.0.0-rc.54", "version": "2.0.0-rc.56",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "2.0.0-rc.54", "version": "2.0.0-rc.56",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.14.0", "@babel/runtime": "^7.14.0",
@ -26,7 +26,7 @@
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"perfect-scrollbar": "^1.5.1", "perfect-scrollbar": "^1.5.1",
"polished": "^4.1.3", "polished": "^4.1.3",
"prismjs": "^1.23.0", "prismjs": "^1.24.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"slugify": "~1.4.7", "slugify": "~1.4.7",
@ -5186,17 +5186,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/clipboard": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
"optional": true,
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -6686,12 +6675,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"node_modules/delegates": { "node_modules/delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -9589,15 +9572,6 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"dependencies": {
"delegate": "^3.1.2"
}
},
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.6", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
@ -15988,12 +15962,9 @@
} }
}, },
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.23.0", "version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"optionalDependencies": {
"clipboard": "^2.0.0"
}
}, },
"node_modules/process": { "node_modules/process": {
"version": "0.11.10", "version": "0.11.10",
@ -17029,12 +17000,6 @@
"url": "https://opencollective.com/webpack" "url": "https://opencollective.com/webpack"
} }
}, },
"node_modules/select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"node_modules/select-hose": { "node_modules/select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -18755,12 +18720,6 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true "dev": true
}, },
"node_modules/tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"node_modules/tmp": { "node_modules/tmp": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@ -25034,17 +24993,6 @@
"string-width": "^4.2.0" "string-width": "^4.2.0"
} }
}, },
"clipboard": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
"optional": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"cliui": { "cliui": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -26202,12 +26150,6 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true "dev": true
}, },
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -28470,15 +28412,6 @@
} }
} }
}, },
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.2.6", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
@ -33297,12 +33230,9 @@
} }
}, },
"prismjs": { "prismjs": {
"version": "1.23.0", "version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"requires": {
"clipboard": "^2.0.0"
}
}, },
"process": { "process": {
"version": "0.11.10", "version": "0.11.10",
@ -34124,12 +34054,6 @@
"ajv-keywords": "^3.5.2" "ajv-keywords": "^3.5.2"
} }
}, },
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"select-hose": { "select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -35533,12 +35457,6 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true "dev": true
}, },
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"tmp": { "tmp": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "redoc", "name": "redoc",
"version": "2.0.0-rc.54", "version": "2.0.0-rc.56",
"description": "ReDoc", "description": "ReDoc",
"repository": { "repository": {
"type": "git", "type": "git",
@ -163,7 +163,7 @@
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"perfect-scrollbar": "^1.5.1", "perfect-scrollbar": "^1.5.1",
"polished": "^4.1.3", "polished": "^4.1.3",
"prismjs": "^1.23.0", "prismjs": "^1.24.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"slugify": "~1.4.7", "slugify": "~1.4.7",

View File

@ -39,9 +39,9 @@ const isModifiedEvent = (event) =>
export function Link(props: { to: string; className?: string; children?: any }) { export function Link(props: { to: string; className?: string; children?: any }) {
const store = React.useContext(StoreContext); const store = React.useContext(StoreContext);
const clickHandler = React.useCallback( const clickHandler = React.useCallback(
(event) => { (event: React.MouseEvent<HTMLAnchorElement>) => {
if (!store) return; if (!store) return;
navigate(store.menu.history, event); navigate(store.menu.history, event, props.to);
}, },
[store], [store],
); );
@ -60,14 +60,14 @@ export function Link(props: { to: string; className?: string; children?: any })
); );
} }
function navigate(history: HistoryService, event) { function navigate(history: HistoryService, event: React.MouseEvent<HTMLAnchorElement>, to: string) {
if ( if (
!event.defaultPrevented && // onClick prevented default !event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks event.button === 0 && // ignore everything but left clicks
!isModifiedEvent(event) // ignore clicks with modifier keys !isModifiedEvent(event) // ignore clicks with modifier keys
) { ) {
event.preventDefault(); event.preventDefault();
history.replace(this.props.to); history.replace(to);
} }
} }

View File

@ -14,6 +14,7 @@ import {
InfoSpanBox, InfoSpanBox,
InfoSpanBoxWrap, InfoSpanBoxWrap,
} from './styled.elements'; } from './styled.elements';
import { l } from '../../services/Labels';
export interface ApiInfoProps { export interface ApiInfoProps {
store: AppStore; store: AppStore;
@ -79,14 +80,14 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
</ApiHeader> </ApiHeader>
{!hideDownloadButton && ( {!hideDownloadButton && (
<p> <p>
Download OpenAPI specification: {l('downloadSpecification')}:
<DownloadButton <DownloadButton
download={downloadFilename || true} download={downloadFilename || true}
target="_blank" target="_blank"
href={downloadLink} href={downloadLink}
onClick={this.handleDownloadClick} onClick={this.handleDownloadClick}
> >
Download {l('download')}
</DownloadButton> </DownloadButton>
</p> </p>
)} )}

View File

@ -59,7 +59,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
} else { } else {
const label = l('example') + ':'; const label = l('example') + ':';
const raw = !!field.in; const raw = !!field.in;
renderedExamples = <FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />; renderedExamples = <FieldDetail label={label} value={getSerializedValue(field, field.example)} raw={raw} />;
} }
} }
@ -76,6 +76,20 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
&gt;{' '} &gt;{' '}
</TypeFormat> </TypeFormat>
)} )}
{schema.contentEncoding && (
<TypeFormat>
{' '}&lt;
{schema.contentEncoding}
&gt;{' '}
</TypeFormat>
)}
{schema.contentMediaType && (
<TypeFormat>
{' '}&lt;
{schema.contentMediaType}
&gt;{' '}
</TypeFormat>
)}
{schema.title && !hideSchemaTitles && <TypeTitle> ({schema.title}) </TypeTitle>} {schema.title && !hideSchemaTitles && <TypeTitle> ({schema.title}) </TypeTitle>}
<ConstraintsView constraints={schema.constraints} /> <ConstraintsView constraints={schema.constraints} />
{schema.pattern && !hideSchemaPattern && ( {schema.pattern && !hideSchemaPattern && (
@ -110,7 +124,7 @@ export class FieldDetails extends React.PureComponent<FieldProps, { patternShown
<ExternalDocumentation externalDocs={schema.externalDocs} compact={true} /> <ExternalDocumentation externalDocs={schema.externalDocs} compact={true} />
)} )}
{(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null} {(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null}
{field.const && (<FieldDetail label={l('const') + ':'} value={field.const}/>) || null} {field.const && (<FieldDetail label={l('const') + ':'} value={field.const} />) || null}
</div> </div>
); );
} }

View File

@ -69,13 +69,14 @@ function DropdownWithinHeader(props) {
export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element { export function BodyContent(props: { content: MediaContentModel; description?: string }): JSX.Element {
const { content, description } = props; const { content, description } = props;
const { isRequestType } = content;
return ( return (
<MediaTypesSwitch content={content} renderDropdown={DropdownWithinHeader}> <MediaTypesSwitch content={content} renderDropdown={DropdownWithinHeader}>
{({ schema }) => { {({ schema }) => {
return ( return (
<> <>
{description !== undefined && <Markdown source={description} />} {description !== undefined && <Markdown source={description} />}
<Schema skipReadOnly={true} key="schema" schema={schema} /> <Schema skipReadOnly={isRequestType} key="schema" schema={schema} />
</> </>
); );
}} }}

View File

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions'; import { argValueToBoolean, RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions';
import { ErrorBoundary } from './ErrorBoundary'; import { ErrorBoundary } from './ErrorBoundary';
import { Loading } from './Loading/Loading'; import { Loading } from './Loading/Loading';
import { Redoc } from './Redoc/Redoc'; import { Redoc } from './Redoc/Redoc';
@ -15,7 +15,7 @@ export interface RedocStandaloneProps {
export const RedocStandalone = function (props: RedocStandaloneProps) { export const RedocStandalone = function (props: RedocStandaloneProps) {
const { spec, specUrl, options = {}, onLoaded } = props; const { spec, specUrl, options = {}, onLoaded } = props;
const hideLoading = options.hideLoading !== undefined; const hideLoading = argValueToBoolean(options.hideLoading, false);
const normalizedOpts = new RedocNormalizedOptions(options); const normalizedOpts = new RedocNormalizedOptions(options);

View File

@ -6,6 +6,7 @@ import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements'; import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
import { OptionsContext } from '../OptionsProvider'; import { OptionsContext } from '../OptionsProvider';
import { l } from '../../services/Labels';
export interface RequestSamplesProps { export interface RequestSamplesProps {
operation: OperationModel; operation: OperationModel;
@ -26,7 +27,7 @@ export class RequestSamples extends React.Component<RequestSamplesProps> {
return ( return (
(hasSamples && ( (hasSamples && (
<div> <div>
<RightPanelHeader> Request samples </RightPanelHeader> <RightPanelHeader> {l('requestSamples')} </RightPanelHeader>
<Tabs defaultIndex={0}> <Tabs defaultIndex={0}>
<TabList hidden={hideTabList}> <TabList hidden={hideTabList}>

View File

@ -5,6 +5,7 @@ import { OperationModel } from '../../services/models';
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements'; import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
import { PayloadSamples } from '../PayloadSamples/PayloadSamples'; import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
import { l } from '../../services/Labels';
export interface ResponseSamplesProps { export interface ResponseSamplesProps {
operation: OperationModel; operation: OperationModel;
@ -23,7 +24,7 @@ export class ResponseSamples extends React.Component<ResponseSamplesProps> {
return ( return (
(responses.length > 0 && ( (responses.length > 0 && (
<div> <div>
<RightPanelHeader> Response samples </RightPanelHeader> <RightPanelHeader> {l('responseSamples')} </RightPanelHeader>
<Tabs defaultIndex={0}> <Tabs defaultIndex={0}>
<TabList> <TabList>

View File

@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { l } from '../../services/Labels';
import { ResponseModel } from '../../services/models'; import { ResponseModel } from '../../services/models';
import styled from '../../styled-components'; import styled from '../../styled-components';
import { ResponseView } from './Response'; import { ResponseView } from './Response';
@ -26,7 +27,7 @@ export class ResponsesList extends React.PureComponent<ResponseListProps> {
return ( return (
<div> <div>
<ResponsesHeader>{isCallback ? 'Callback responses' : 'Responses'}</ResponsesHeader> <ResponsesHeader>{isCallback ? l('callbackResponses') : l('responses')}</ResponsesHeader>
{responses.map(response => { {responses.map(response => {
return <ResponseView key={response.code} response={response} />; return <ResponseView key={response.code} response={response} />;
})} })}

View File

@ -4,7 +4,8 @@ import { Schema, SchemaProps } from './Schema';
import { ArrayClosingLabel, ArrayOpenningLabel } from '../../common-elements'; import { ArrayClosingLabel, ArrayOpenningLabel } from '../../common-elements';
import styled from '../../styled-components'; import styled from '../../styled-components';
import {humanizeConstraints} from "../../utils"; import { humanizeConstraints } from '../../utils';
import { TypeName } from '../../common-elements/fields';
const PaddedSchema = styled.div` const PaddedSchema = styled.div`
padding-left: ${({ theme }) => theme.spacing.unit * 2}px; padding-left: ${({ theme }) => theme.spacing.unit * 2}px;
@ -13,12 +14,20 @@ const PaddedSchema = styled.div`
export class ArraySchema extends React.PureComponent<SchemaProps> { export class ArraySchema extends React.PureComponent<SchemaProps> {
render() { render() {
const itemsSchema = this.props.schema.items!; const itemsSchema = this.props.schema.items!;
const schema = this.props.schema;
const itemConstraintSchema = ( const itemConstraintSchema = (
min: number | undefined = undefined, min: number | undefined = undefined,
max: number | undefined = undefined, max: number | undefined = undefined,
) => ({ type: 'array', minItems: min, maxItems: max }); ) => ({ type: 'array', minItems: min, maxItems: max });
const minMaxItems = humanizeConstraints(itemConstraintSchema(itemsSchema.schema.minItems, itemsSchema.schema.maxItems)); const minMaxItems = humanizeConstraints(itemConstraintSchema(itemsSchema?.schema?.minItems, itemsSchema?.schema?.maxItems));
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
return (<div>
<TypeName>{schema.displayType}</TypeName>
</div>);
}
return ( return (
<div> <div>

View File

@ -63,20 +63,15 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
return <OneOfSchema schema={schema} {...this.props} />; return <OneOfSchema schema={schema} {...this.props} />;
} }
if (type && Array.isArray(type)) { const types = Array.isArray(type) ? type : [type];
if (types.includes('object')) {
if (schema.fields?.length) {
return <ObjectSchema {...(this.props as any)} />;
}
} else if (types.includes('array')) {
return <ArraySchema {...(this.props as any)} />; return <ArraySchema {...(this.props as any)} />;
} }
switch (type) {
case 'object':
if (schema.fields?.length) {
return <ObjectSchema {...(this.props as any)} />;
}
break;
case 'array':
return <ArraySchema {...(this.props as any)} />;
}
// TODO: maybe adjust FieldDetails to accept schema // TODO: maybe adjust FieldDetails to accept schema
const field = ({ const field = ({
schema, schema,

View File

@ -8,8 +8,8 @@ import { SecurityRequirementModel } from '../../services/models/SecurityRequirem
import { linksCss } from '../Markdown/styled.elements'; import { linksCss } from '../Markdown/styled.elements';
const ScopeName = styled.code` const ScopeName = styled.code`
font-size: ${props => props.theme.typography.code.fontSize}; font-size: ${(props) => props.theme.typography.code.fontSize};
font-family: ${props => props.theme.typography.code.fontFamily}; font-family: ${(props) => props.theme.typography.code.fontFamily};
border: 1px solid ${({ theme }) => theme.colors.border.dark}; border: 1px solid ${({ theme }) => theme.colors.border.dark};
margin: 0 3px; margin: 0 3px;
padding: 0.2em; padding: 0.2em;
@ -67,18 +67,22 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
const security = this.props.security; const security = this.props.security;
return ( return (
<SecurityRequirementOrWrap> <SecurityRequirementOrWrap>
{security.schemes.map(scheme => { {security.schemes.length ? (
return ( security.schemes.map((scheme) => {
<SecurityRequirementAndWrap key={scheme.id}> return (
<Link to={scheme.sectionId}>{scheme.id}</Link> <SecurityRequirementAndWrap key={scheme.id}>
{scheme.scopes.length > 0 && ' ('} <Link to={scheme.sectionId}>{scheme.id}</Link>
{scheme.scopes.map(scope => ( {scheme.scopes.length > 0 && ' ('}
<ScopeName key={scope}>{scope}</ScopeName> {scheme.scopes.map((scope) => (
))} <ScopeName key={scope}>{scope}</ScopeName>
{scheme.scopes.length > 0 && ') '} ))}
</SecurityRequirementAndWrap> {scheme.scopes.length > 0 && ') '}
); </SecurityRequirementAndWrap>
})} );
})
) : (
<SecurityRequirementAndWrap>None</SecurityRequirementAndWrap>
)}
</SecurityRequirementOrWrap> </SecurityRequirementOrWrap>
); );
} }
@ -89,7 +93,7 @@ const AuthHeaderColumn = styled.div`
`; `;
const SecuritiesColumn = styled.div` const SecuritiesColumn = styled.div`
width: ${props => props.theme.schema.defaultDetailsWidth}; width: ${(props) => props.theme.schema.defaultDetailsWidth};
${media.lessThan('small')` ${media.lessThan('small')`
margin-top: 10px; margin-top: 10px;
`} `}

View File

@ -58,6 +58,12 @@ export function StoreBuilder(props: StoreBuilderProps) {
} }
}, [resolvedSpec, specUrl, options]); }, [resolvedSpec, specUrl, options]);
React.useEffect(() => {
if (store && onLoaded) {
onLoaded();
}
}, [store, onLoaded])
return children({ return children({
loading: !store, loading: !store,
store, store,

View File

@ -0,0 +1,27 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import { OpenAPIParser } from '../../services';
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
import { SecurityRequirement } from '../SecurityRequirement/SecurityRequirement';
import { RedocNormalizedOptions } from '../../services/RedocNormalizedOptions';
const options = new RedocNormalizedOptions({});
describe('Components', () => {
describe('SecurityRequirement', () => {
describe('SecurityRequirement', () => {
it('should render \'None\' when empty object in security open api', () => {
const parser = new OpenAPIParser({ openapi: '3.0', info: { title: 'test', version: '0' }, paths: {} },
undefined,
options,
);
const securityRequirement = new SecurityRequirementModel({}, parser);
const securityElement = shallow(
<SecurityRequirement key={1} security={securityRequirement} />
).getElement();
expect(securityElement.props.children.type.target).toEqual('span');
expect(securityElement.props.children.props.children).toEqual('None');
});
});
});
});

View File

@ -20,6 +20,8 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"activeOneOf": 0, "activeOneOf": 0,
"const": "", "const": "",
"constraints": Array [], "constraints": Array [],
"contentEncoding": undefined,
"contentMediaType": undefined,
"default": undefined, "default": undefined,
"deprecated": false, "deprecated": false,
"description": "", "description": "",
@ -71,6 +73,8 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"activeOneOf": 0, "activeOneOf": 0,
"const": "", "const": "",
"constraints": Array [], "constraints": Array [],
"contentEncoding": undefined,
"contentMediaType": undefined,
"default": undefined, "default": undefined,
"deprecated": false, "deprecated": false,
"description": "", "description": "",

View File

@ -10,6 +10,12 @@ export interface LabelsConfig {
arrayOf: string; arrayOf: string;
webhook: string; webhook: string;
const: string; const: string;
download: string;
downloadSpecification: string;
responses: string;
callbackResponses: string;
requestSamples: string;
responseSamples: string;
} }
export type LabelsConfigRaw = Partial<LabelsConfig>; export type LabelsConfigRaw = Partial<LabelsConfig>;
@ -26,6 +32,12 @@ const labels: LabelsConfig = {
arrayOf: 'Array of ', arrayOf: 'Array of ',
webhook: 'Event', webhook: 'Event',
const: 'Value', const: 'Value',
download: 'Download',
downloadSpecification: 'Download OpenAPI specification',
responses: 'Responses',
callbackResponses: 'Callback responses',
requestSamples: 'Request samples',
responseSamples: 'Response samples',
}; };
export function setRedocLabels(_labels?: LabelsConfigRaw) { export function setRedocLabels(_labels?: LabelsConfigRaw) {

View File

@ -45,12 +45,12 @@ export interface RedocRawOptions {
generatedPayloadSamplesMaxDepth?: number; generatedPayloadSamplesMaxDepth?: number;
} }
function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean { export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
if (val === undefined) { if (val === undefined) {
return defaultValue || false; return defaultValue || false;
} }
if (typeof val === 'string') { if (typeof val === 'string') {
return val === 'false' ? false : true; return val !== 'false';
} }
return val; return val;
} }

View File

@ -0,0 +1,27 @@
import { RequestBodyModel } from '../../models/RequestBody';
import { OpenAPIParser } from '../../OpenAPIParser';
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
const opts = new RedocNormalizedOptions({});
describe('Models', () => {
describe('RequestBodyModel', () => {
let parser, props;
beforeEach(() => {
parser = new OpenAPIParser({ openapi: '3.0.0' } as any, undefined, opts);
props = {
parser,
infoOrRef: {},
options: opts,
isEvent: false,
};
});
test('should work with default props', () => {
const consoleError = jest.spyOn(global.console, 'error');
const req = new RequestBodyModel(props);
expect(consoleError).not.toHaveBeenCalled();
expect(req).toEqual({ description: '', required: false });
});
});
});

View File

@ -5,30 +5,38 @@ import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
const opts = new RedocNormalizedOptions({}); const opts = new RedocNormalizedOptions({});
describe('Models', () => { describe('Models', () => {
describe('ResponseModel', () => { describe('ResponseModel', () => {
let parser; let parser, props;
beforeEach(() => { beforeEach(() => {
parser = new OpenAPIParser({ openapi: '3.0.0' } as any, undefined, opts); parser = new OpenAPIParser({ openapi: '3.0.0' } as any, undefined, opts);
props = {
parser,
defaultAsError: false,
infoOrRef: {},
options: opts,
isEvent: false,
code: 'default',
};
}); });
test('should calculate response type based on code', () => { test('should calculate response type based on code', () => {
let resp = new ResponseModel(parser, '200', false, {}, opts); let resp = new ResponseModel({...props, code: '200' });
expect(resp.type).toEqual('success'); expect(resp.type).toEqual('success');
resp = new ResponseModel(parser, '120', false, {}, opts); resp = new ResponseModel({...props, code: '120' });
expect(resp.type).toEqual('info'); expect(resp.type).toEqual('info');
resp = new ResponseModel(parser, '301', false, {}, opts); resp = new ResponseModel({...props, code: '301' });
expect(resp.type).toEqual('redirect'); expect(resp.type).toEqual('redirect');
resp = new ResponseModel(parser, '400', false, {}, opts); resp = new ResponseModel({...props, code: '400' });
expect(resp.type).toEqual('error'); expect(resp.type).toEqual('error');
}); });
test('default should be successful by default', () => { test('default should be successful by default', () => {
const resp = new ResponseModel(parser, 'default', false, {}, opts); const resp = new ResponseModel({...props, code: 'default' });
expect(resp.type).toEqual('success'); expect(resp.type).toEqual('success');
}); });
test('default should be error if defaultAsError is true', () => { test('default should be error if defaultAsError is true', () => {
const resp = new ResponseModel(parser, 'default', true, {}, opts); const resp = new ResponseModel({...props, code: 'default', defaultAsError: true });
expect(resp.type).toEqual('error'); expect(resp.type).toEqual('error');
}); });
}); });

View File

@ -53,8 +53,8 @@ export class MediaTypeModel {
generateExample(parser: OpenAPIParser, info: OpenAPIMediaType) { generateExample(parser: OpenAPIParser, info: OpenAPIMediaType) {
const samplerOptions = { const samplerOptions = {
skipReadOnly: this.isRequestType, skipReadOnly: this.isRequestType,
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
skipWriteOnly: !this.isRequestType, skipWriteOnly: !this.isRequestType,
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
maxSampleDepth: this.generatedPayloadSamplesMaxDepth, maxSampleDepth: this.generatedPayloadSamplesMaxDepth,
}; };
if (this.schema && this.schema.oneOf) { if (this.schema && this.schema.oneOf) {

View File

@ -76,6 +76,7 @@ export class OperationModel implements IMenuItem {
extensions: Record<string, any>; extensions: Record<string, any>;
isCallback: boolean; isCallback: boolean;
isWebhook: boolean; isWebhook: boolean;
isEvent: boolean;
constructor( constructor(
private parser: OpenAPIParser, private parser: OpenAPIParser,
@ -98,7 +99,8 @@ export class OperationModel implements IMenuItem {
this.operationId = operationSpec.operationId; this.operationId = operationSpec.operationId;
this.path = operationSpec.pathName; this.path = operationSpec.pathName;
this.isCallback = isCallback; this.isCallback = isCallback;
this.isWebhook = !!operationSpec.isWebhook; this.isWebhook = operationSpec.isWebhook;
this.isEvent = this.isCallback || this.isWebhook;
this.name = getOperationSummary(operationSpec); this.name = getOperationSummary(operationSpec);
@ -171,8 +173,12 @@ export class OperationModel implements IMenuItem {
@memoize @memoize
get requestBody() { get requestBody() {
return ( return (
this.operationSpec.requestBody && this.operationSpec.requestBody && new RequestBodyModel({
new RequestBodyModel(this.parser, this.operationSpec.requestBody, this.options) parser: this.parser,
infoOrRef: this.operationSpec.requestBody,
options: this.options,
isEvent: this.isEvent,
})
); );
} }
@ -240,13 +246,14 @@ export class OperationModel implements IMenuItem {
return isStatusCode(code); return isStatusCode(code);
}) // filter out other props (e.g. x-props) }) // filter out other props (e.g. x-props)
.map((code) => { .map((code) => {
return new ResponseModel( return new ResponseModel({
this.parser, parser: this.parser,
code, code,
hasSuccessResponses, defaultAsError: hasSuccessResponses,
this.operationSpec.responses[code], infoOrRef: this.operationSpec.responses[code],
this.options, options: this.options,
); isEvent: this.isEvent,
});
}); });
} }

View File

@ -4,22 +4,27 @@ import { OpenAPIParser } from '../OpenAPIParser';
import { RedocNormalizedOptions } from '../RedocNormalizedOptions'; import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import { MediaContentModel } from './MediaContent'; import { MediaContentModel } from './MediaContent';
type RequestBodyProps = {
parser: OpenAPIParser;
infoOrRef: Referenced<OpenAPIRequestBody>;
options: RedocNormalizedOptions;
isEvent: boolean;
}
export class RequestBodyModel { export class RequestBodyModel {
description: string; description: string;
required: boolean; required: boolean;
content?: MediaContentModel; content?: MediaContentModel;
constructor( constructor(props: RequestBodyProps) {
parser: OpenAPIParser, const { parser, infoOrRef, options, isEvent } = props;
infoOrRef: Referenced<OpenAPIRequestBody>, const isRequest = isEvent ? false : true;
options: RedocNormalizedOptions,
) {
const info = parser.deref(infoOrRef); const info = parser.deref(infoOrRef);
this.description = info.description || ''; this.description = info.description || '';
this.required = !!info.required; this.required = !!info.required;
parser.exitRef(infoOrRef); parser.exitRef(infoOrRef);
if (info.content !== undefined) { if (info.content !== undefined) {
this.content = new MediaContentModel(parser, info.content, true, options); this.content = new MediaContentModel(parser, info.content, isRequest, options);
} }
} }
} }

View File

@ -8,6 +8,15 @@ import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import { FieldModel } from './Field'; import { FieldModel } from './Field';
import { MediaContentModel } from './MediaContent'; import { MediaContentModel } from './MediaContent';
type ResponseProps = {
parser: OpenAPIParser,
code: string,
defaultAsError: boolean,
infoOrRef: Referenced<OpenAPIResponse>,
options: RedocNormalizedOptions,
isEvent: boolean,
}
export class ResponseModel { export class ResponseModel {
@observable @observable
expanded: boolean = false; expanded: boolean = false;
@ -19,13 +28,9 @@ export class ResponseModel {
type: string; type: string;
headers: FieldModel[] = []; headers: FieldModel[] = [];
constructor( constructor(props: ResponseProps) {
parser: OpenAPIParser, const { parser, code, defaultAsError, infoOrRef, options, isEvent } = props;
code: string, const isRequest = isEvent ? true : false;
defaultAsError: boolean,
infoOrRef: Referenced<OpenAPIResponse>,
options: RedocNormalizedOptions,
) {
makeObservable(this); makeObservable(this);
this.expanded = options.expandResponses === 'all' || options.expandResponses[code]; this.expanded = options.expandResponses === 'all' || options.expandResponses[code];
@ -34,7 +39,7 @@ export class ResponseModel {
parser.exitRef(infoOrRef); parser.exitRef(infoOrRef);
this.code = code; this.code = code;
if (info.content !== undefined) { if (info.content !== undefined) {
this.content = new MediaContentModel(parser, info.content, false, options); this.content = new MediaContentModel(parser, info.content, isRequest, options);
} }
if (info['x-summary'] !== undefined) { if (info['x-summary'] !== undefined) {

View File

@ -61,6 +61,8 @@ export class SchemaModel {
schema: MergedOpenAPISchema; schema: MergedOpenAPISchema;
extensions?: Record<string, any>; extensions?: Record<string, any>;
const: any; const: any;
contentEncoding?: string;
contentMediaType?: string;
/** /**
* @param isChild if schema discriminator Child * @param isChild if schema discriminator Child
@ -98,6 +100,10 @@ export class SchemaModel {
this.activeOneOf = idx; this.activeOneOf = idx;
} }
hasType(type: string) {
return this.type === type || (Array.isArray(this.type) && this.type.includes(type));
}
init(parser: OpenAPIParser, isChild: boolean) { init(parser: OpenAPIParser, isChild: boolean) {
const schema = this.schema; const schema = this.schema;
this.isCircular = schema['x-circular-ref']; this.isCircular = schema['x-circular-ref'];
@ -120,10 +126,14 @@ export class SchemaModel {
this.readOnly = !!schema.readOnly; this.readOnly = !!schema.readOnly;
this.writeOnly = !!schema.writeOnly; this.writeOnly = !!schema.writeOnly;
this.const = schema.const || ''; this.const = schema.const || '';
this.contentEncoding = schema.contentEncoding;
this.contentMediaType = schema.contentMediaType;
if (!!schema.nullable) { if (!!schema.nullable || schema['x-nullable']) {
if (Array.isArray(this.type) && !this.type.includes('null')) { if (Array.isArray(this.type) && !this.type.some((value) => value === null || value === 'null')) {
this.type = [...this.type, 'null']; this.type = [...this.type, 'null'];
} else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) {
this.type = [this.type, 'null'];
} }
} }
@ -164,9 +174,9 @@ export class SchemaModel {
return; return;
} }
if (this.type === 'object') { if (this.hasType('object')) {
this.fields = buildFields(parser, schema, this.pointer, this.options); this.fields = buildFields(parser, schema, this.pointer, this.options);
} else if ((this.type === 'array' || Array.isArray(this.type)) && schema.items) { } else if (this.hasType('array') && schema.items) {
this.items = new SchemaModel(parser, schema.items, this.pointer + '/items', this.options); this.items = new SchemaModel(parser, schema.items, this.pointer + '/items', this.options);
this.displayType = pluralizeType(this.items.displayType); this.displayType = pluralizeType(this.items.displayType);
this.displayFormat = this.items.format; this.displayFormat = this.items.format;

View File

@ -147,6 +147,8 @@ export interface OpenAPISchema {
enum?: any[]; enum?: any[];
example?: any; example?: any;
const?: string; const?: string;
contentEncoding?: string;
contentMediaType?: string;
} }
export interface OpenAPIDiscriminator { export interface OpenAPIDiscriminator {

View File

@ -2187,6 +2187,12 @@ Object {
"id": Object { "id": Object {
"$ref": "#/components/schemas/Id", "$ref": "#/components/schemas/Id",
}, },
"image": Object {
"contentEncoding": "base64",
"contentMediaType": "image/png",
"description": "User image",
"type": "string",
},
"lastName": Object { "lastName": Object {
"description": "User last name", "description": "User last name",
"example": "Smith", "example": "Smith",

View File

@ -19,6 +19,7 @@ import 'prismjs/components/prism-ruby.js';
import 'prismjs/components/prism-scala.js'; import 'prismjs/components/prism-scala.js';
import 'prismjs/components/prism-sql.js'; import 'prismjs/components/prism-sql.js';
import 'prismjs/components/prism-swift.js'; import 'prismjs/components/prism-swift.js';
import 'prismjs/components/prism-yaml.js';
const DEFAULT_LANG = 'clike'; const DEFAULT_LANG = 'clike';

View File

@ -83,6 +83,8 @@ const schemaKeywordTypes = {
maxLength: 'string', maxLength: 'string',
minLength: 'string', minLength: 'string',
pattern: 'string', pattern: 'string',
contentEncoding: 'string',
contentMediaType: 'string',
items: 'array', items: 'array',
maxItems: 'array', maxItems: 'array',