mirror of
https://github.com/Redocly/redoc.git
synced 2025-04-22 17:52:00 +03:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
00bc6edfc4 | ||
|
45476135ad | ||
|
05a04c85ed | ||
|
2db293bfb2 | ||
|
1b4126fde4 | ||
|
1fa13270a1 | ||
|
7cedd59826 | ||
|
cab07bad3b | ||
|
1243095926 | ||
|
6fa5a2a57a | ||
|
3a748022be | ||
|
53a6afc596 | ||
|
ae1ae79901 | ||
|
153ec7a0b7 | ||
|
c765b34ef5 | ||
|
0e2d595ef7 | ||
|
8caddaf0eb | ||
|
981e4a84fb | ||
|
59ee73fefa | ||
|
d614d2d022 | ||
|
85b622fc58 | ||
|
639fd2c32c | ||
|
aa0879ca02 | ||
|
c3fce4e2b2 | ||
|
11912f5d91 | ||
|
1593d01936 | ||
|
4736c54edd | ||
|
64f18779e5 | ||
|
1cceed4b47 | ||
|
c0c68206de | ||
|
d193dd2627 | ||
|
c04b4c8fec | ||
|
c0203be045 | ||
|
c813eeac04 | ||
|
60d131b0a9 | ||
|
a7607eafdd | ||
|
e0666776e8 | ||
|
d0543bb116 | ||
|
3658b6def4 | ||
|
c664dd0d56 | ||
|
72dd57d457 | ||
|
31d88a184b | ||
|
50c7f60fb6 | ||
|
a661320625 | ||
|
b0d03d0206 | ||
|
2b72dc0e90 | ||
|
ff91768879 | ||
|
20a923b485 | ||
|
edac97236d | ||
|
9af774e443 | ||
|
1f11f597c4 | ||
|
3f3f9551ee | ||
|
8c391497bf | ||
|
4fd22f6d74 | ||
|
76cecf054c | ||
|
21b961dffa | ||
|
0db1e9872d | ||
|
e9f9799e89 | ||
|
8c04c02ab0 | ||
|
7b2931dc91 |
6
.github/sync.yml
vendored
6
.github/sync.yml
vendored
|
@ -1,6 +0,0 @@
|
||||||
group:
|
|
||||||
- files:
|
|
||||||
- source: docs/
|
|
||||||
dest: docs/redoc
|
|
||||||
repos: |
|
|
||||||
Redocly/docs
|
|
37
.github/workflows/docs-tests.yaml
vendored
Normal file
37
.github/workflows/docs-tests.yaml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
name: Documentation tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
markdownlint:
|
||||||
|
name: markdownlint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: DavidAnson/markdownlint-cli2-action@v15
|
||||||
|
with:
|
||||||
|
config: .markdownlint.yaml
|
||||||
|
globs: |
|
||||||
|
docs/**/*.md
|
||||||
|
README.md
|
||||||
|
|
||||||
|
vale:
|
||||||
|
name: vale action
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: errata-ai/vale-action@reviewdog
|
||||||
|
with:
|
||||||
|
files: '["README.md", "docs"]'
|
||||||
|
filter_mode: file
|
||||||
|
|
||||||
|
linkcheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Markup Link Checker (mlc)
|
||||||
|
uses: becheran/mlc@v0.16.1
|
||||||
|
with:
|
||||||
|
args: ./docs
|
37
.github/workflows/publish.yml
vendored
37
.github/workflows/publish.yml
vendored
|
@ -2,8 +2,7 @@ name: Publish
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
branches: [main]
|
||||||
- v[0-9]*.[0-9]*.[0-9]*
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bundle:
|
bundle:
|
||||||
|
@ -22,7 +21,7 @@ jobs:
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run bundle
|
- run: npm run bundle
|
||||||
- name: Store bundle artifact
|
- name: Store bundle artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles
|
||||||
path: bundles
|
path: bundles
|
||||||
|
@ -40,14 +39,32 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- name: Download bundled artifact
|
- name: Download bundled artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles
|
||||||
path: bundles
|
path: bundles
|
||||||
- run: npm run e2e
|
- run: npm run e2e
|
||||||
|
check-version:
|
||||||
|
name: Check Version
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [bundle, unit-tests, e2e-tests]
|
||||||
|
outputs:
|
||||||
|
changed: ${{ steps.check.outputs.changed }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
- name: Check if version has been updated
|
||||||
|
id: check
|
||||||
|
uses: EndBug/version-check@v2.0.1
|
||||||
|
with:
|
||||||
|
file-url: https://cdn.jsdelivr.net/npm/redoc/package.json
|
||||||
|
static-checking: localIsNew
|
||||||
publish:
|
publish:
|
||||||
name: Publish to NPM
|
name: Publish to NPM
|
||||||
needs: [bundle, unit-tests, e2e-tests]
|
needs: [check-version]
|
||||||
|
if: needs.check-version.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
@ -56,7 +73,7 @@ jobs:
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Download bundled artifacts
|
- name: Download bundled artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: bundles
|
name: bundles
|
||||||
path: bundles
|
path: bundles
|
||||||
|
@ -77,7 +94,8 @@ jobs:
|
||||||
|
|
||||||
publish-cdn:
|
publish-cdn:
|
||||||
name: Publish to CDN
|
name: Publish to CDN
|
||||||
needs: [bundle, unit-tests, e2e-tests]
|
needs: [check-version]
|
||||||
|
if: needs.check-version.outputs.changed == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
|
@ -89,14 +107,15 @@ jobs:
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
- name: Download all artifact
|
- name: Download all artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Publish to S3
|
- name: Publish to S3
|
||||||
run: npm run publish-cdn
|
run: npm run publish-cdn
|
||||||
|
|
||||||
invalidate-cache:
|
invalidate-cache:
|
||||||
name: Clear cache
|
name: Clear cache
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [publish, publish-cdn]
|
needs: [check-version, publish, publish-cdn]
|
||||||
|
if: needs.check-version.outputs.changed == 'true'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
18
.github/workflows/sync.yml
vendored
18
.github/workflows/sync.yml
vendored
|
@ -1,18 +0,0 @@
|
||||||
name: Sync Files
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Run GitHub File Sync
|
|
||||||
uses: Redocly/repo-file-sync-action@main
|
|
||||||
with:
|
|
||||||
GH_PAT: ${{ secrets.GH_PAT }}
|
|
||||||
COMMIT_PREFIX: 'sync:'
|
|
||||||
SKIP_PR: true
|
|
16
.github/workflows/vale.yaml
vendored
16
.github/workflows/vale.yaml
vendored
|
@ -1,16 +0,0 @@
|
||||||
name: Docs lint
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
vale:
|
|
||||||
name: vale action
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: errata-ai/vale-action@reviewdog
|
|
||||||
with:
|
|
||||||
files: '["README.md", "docs"]'
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
54
.markdownlint.yaml
Normal file
54
.markdownlint.yaml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
# Default rules: https://github.com/github/super-linter/blob/master/TEMPLATES/.markdown-lint.yml
|
||||||
|
|
||||||
|
# Rules by id
|
||||||
|
|
||||||
|
# Unordered list style
|
||||||
|
MD004: false
|
||||||
|
|
||||||
|
# Unordered list indentation
|
||||||
|
MD007:
|
||||||
|
indent: 2
|
||||||
|
|
||||||
|
MD013:
|
||||||
|
# TODO: Consider to decrease allowed line length
|
||||||
|
line_length: 800
|
||||||
|
tables: false
|
||||||
|
|
||||||
|
## Allow same headers in siblings
|
||||||
|
MD024:
|
||||||
|
siblings_only: true
|
||||||
|
|
||||||
|
# Multiple top level headings in the same document
|
||||||
|
MD025:
|
||||||
|
front_matter_title: ''
|
||||||
|
|
||||||
|
# Trailing punctuation in heading
|
||||||
|
MD026:
|
||||||
|
punctuation: '.,;:。,;:'
|
||||||
|
|
||||||
|
# Ordered list item prefix
|
||||||
|
MD029: false
|
||||||
|
|
||||||
|
# Unordered lists inside of ordered lists
|
||||||
|
MD030: false
|
||||||
|
|
||||||
|
# Inline HTML
|
||||||
|
MD033: false
|
||||||
|
|
||||||
|
# No bare urls
|
||||||
|
MD034: false
|
||||||
|
|
||||||
|
# Emphasis used instead of a heading
|
||||||
|
MD036: false
|
||||||
|
|
||||||
|
# Disable "First line in file should be a top level heading"
|
||||||
|
# We use uncommon format to add metadata.
|
||||||
|
# TODO: Consider to use "YAML front matter".
|
||||||
|
MD041: false
|
||||||
|
|
||||||
|
# Rules by tags
|
||||||
|
blank_lines: false
|
||||||
|
|
||||||
|
MD046: false
|
||||||
|
# code-block-style
|
4
.mlc.toml
Normal file
4
.mlc.toml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Ignore these links, we can't check them from this subproject
|
||||||
|
ignore-links=["../*", "/docs/*"]
|
||||||
|
# Path to the root folder used to resolve all relative paths
|
||||||
|
root-dir="./docs"
|
79
CHANGELOG.md
79
CHANGELOG.md
|
@ -1,3 +1,82 @@
|
||||||
|
# [2.5.0](https://github.com/Redocly/redoc/compare/v2.4.0...v2.5.0) (2025-04-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enhance accessibility for menu items with keyboard support ([#2655](https://github.com/Redocly/redoc/issues/2655)) ([2db293b](https://github.com/Redocly/redoc/commit/2db293bfb2973497dd33f31dc99e97f5bb90bbe8))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add keyboard navigation support to JsonViewer component ([#2654](https://github.com/Redocly/redoc/issues/2654)) ([1b4126f](https://github.com/Redocly/redoc/commit/1b4126fde4531387f49c90f52efbd0c0e5f7b6ea))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.4.0](https://github.com/Redocly/redoc/compare/v2.3.0...v2.4.0) (2025-02-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Prototype Pollution Vulnerability Affecting redoc <=2.2.0 ([#2638](https://github.com/Redocly/redoc/issues/2638)) ([153ec7a](https://github.com/Redocly/redoc/commit/153ec7a0b7245639f404c0b038b612ae7377c7db))
|
||||||
|
* unify redoc config ([#2647](https://github.com/Redocly/redoc/issues/2647)) ([53a6afc](https://github.com/Redocly/redoc/commit/53a6afc59624fe4591b0a0f1f20f41c0fbb5f1cf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add supporting react 19 in package.json ([#2652](https://github.com/Redocly/redoc/issues/2652)) ([3a74802](https://github.com/Redocly/redoc/commit/3a748022be3a7dc7f98669e1645dd5cda72f1abc))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.3.0](https://github.com/Redocly/redoc/compare/v2.2.0...v2.3.0) (2025-01-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* displaying json example when showObjectSchemaExamples enabled ([#2635](https://github.com/Redocly/redoc/issues/2635)) ([59ee73f](https://github.com/Redocly/redoc/commit/59ee73fefa8e8edb398940076bdd721fc284caa3))
|
||||||
|
* displaying nested items with type string ([#2634](https://github.com/Redocly/redoc/issues/2634)) ([85b622f](https://github.com/Redocly/redoc/commit/85b622fc581eb96303aeb85056aef36c74ea9f9d))
|
||||||
|
* passing inline parameters after support react 18 for response title ([#2640](https://github.com/Redocly/redoc/issues/2640)) ([d614d2d](https://github.com/Redocly/redoc/commit/d614d2d022df8bd1989cb0eaf76d087b52120d36))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* update pattern styling ([#2196](https://github.com/Redocly/redoc/issues/2196)) ([#2600](https://github.com/Redocly/redoc/issues/2600)) ([aa0879c](https://github.com/Redocly/redoc/commit/aa0879ca0235112918428fdff8f4c48d2c6c4adf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.2.0](https://github.com/Redocly/redoc/compare/v2.1.5...v2.2.0) (2024-10-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* show siblings schema with oneOf ([#2576](https://github.com/Redocly/redoc/issues/2576)) ([60d131b](https://github.com/Redocly/redoc/commit/60d131b0a9dab4710e900323c9ba81160cecf7d8))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support x-badges ([#2605](https://github.com/Redocly/redoc/issues/2605)) ([64f1877](https://github.com/Redocly/redoc/commit/64f18779e5fe7e03f25862463cbc5062e85c867c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.1.5](https://github.com/Redocly/redoc/compare/v2.1.4...v2.1.5) (2024-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update react to 18 and react-tabs to 6 ([#2547](https://github.com/Redocly/redoc/issues/2547)) ([c664dd0](https://github.com/Redocly/redoc/commit/c664dd0d56571ce799b8eadd081d86a6b2cdefae))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.1.4](https://github.com/Redocly/redoc/compare/v2.1.3...v2.1.4) (2024-04-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add deprecated css to clickable property name ([#2526](https://github.com/Redocly/redoc/issues/2526)) ([b0d03d0](https://github.com/Redocly/redoc/commit/b0d03d02069c1508447ddebc2f8a3fffa9b03ce5))
|
||||||
|
* use h2/h3 for headings instead of h1/h2 for better seo ([#2514](https://github.com/Redocly/redoc/issues/2514)) ([2b72dc0](https://github.com/Redocly/redoc/commit/2b72dc0e90f759a8ee2e47691c844e7f05928a24))
|
||||||
|
* security vulnerability ([#2445](https://github.com/Redocly/redoc/pull/2445)) ([1f11f5](https://github.com/Redocly/redoc/commit/1f11f597c4f10ddd601db247f5034052b6ca689f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.1.3](https://github.com/Redocly/redoc/compare/v2.1.2...v2.1.3) (2023-10-24)
|
## [2.1.3](https://github.com/Redocly/redoc/compare/v2.1.2...v2.1.3) (2023-10-24)
|
||||||
|
|
||||||
|
|
||||||
|
|
18
README.md
18
README.md
|
@ -1,11 +1,11 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img alt="Redoc logo" src="https://raw.githubusercontent.com/Redocly/redoc/main//docs/images/redoc.png" width="400px" />
|
<img alt="Redoc logo" src="https://raw.githubusercontent.com/Redocly/redoc/main//docs/images/redoc.png" width="400px" />
|
||||||
|
|
||||||
# Generate interactive API documentation from OpenAPI definitions
|
# Generate beautiful API documentation from OpenAPI
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/main/LICENSE)
|
[](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/main/LICENSE)
|
||||||
|
|
||||||
[](https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc)
|
[](https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,13 +34,11 @@ enter the URL for your definition and select **TRY IT**.
|
||||||
|
|
||||||
- Responsive three-panel design with menu/scrolling synchronization
|
- Responsive three-panel design with menu/scrolling synchronization
|
||||||
- Support for OpenAPI 3.1, OpenAPI 3.0, and Swagger 2.0
|
- Support for OpenAPI 3.1, OpenAPI 3.0, and Swagger 2.0
|
||||||
- [Multiple deployment options](https://redocly.com/docs/redoc/)
|
|
||||||
- Interactive interface so your users can try the API immediately
|
|
||||||
- Ability to integrate your API introduction into the side menu
|
- Ability to integrate your API introduction into the side menu
|
||||||
- High-level grouping in side menu with the [`x-tagGroups`](https://redocly.com/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension
|
- High-level grouping in side menu with the [`x-tagGroups`](https://redocly.com/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension
|
||||||
- [Simple integration with `create-react-app`](https://redocly.com/docs/redoc/quickstart/react/)
|
- [Simple integration with `create-react-app`](https://redocly.com/docs/redoc/quickstart/react/)
|
||||||
- Code samples support (with vendor extension) <br>
|
- Code samples support (with vendor extension) <br>
|
||||||

|

|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -50,7 +48,7 @@ Redoc is provided as a CLI tool (also distributed as a Docker image), HTML tag,
|
||||||
|
|
||||||
If you have Node installed, quickly generate documentation using `npx`:
|
If you have Node installed, quickly generate documentation using `npx`:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npx @redocly/cli build-docs openapi.yaml
|
npx @redocly/cli build-docs openapi.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -73,9 +71,9 @@ Add your own `spec-url` to the `<redoc>` tag; this attribute can also be a local
|
||||||
|
|
||||||
### More usage options
|
### More usage options
|
||||||
|
|
||||||
Check out the [deployment documentation](./deploment/index/md) for more options, and detailed documentation for each.
|
Check out the [deployment documentation](./docs/deployment/intro.md) for more options, and detailed documentation for each.
|
||||||
|
|
||||||
## Redoc vs. Reference
|
## Redoc vs. Redocly API Reference
|
||||||
|
|
||||||
Redoc is Redocly's community-edition product. Looking for something more?
|
Redoc is Redocly's community-edition product. Looking for something more?
|
||||||
We also offer [hosted API reference documentation](https://redocly.com/docs/api-registry/guides/api-registry-quickstart/)
|
We also offer [hosted API reference documentation](https://redocly.com/docs/api-registry/guides/api-registry-quickstart/)
|
||||||
|
@ -104,6 +102,7 @@ A sample of the organizations using Redocly tools in the wild:
|
||||||
- [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/)
|
||||||
- [BoxKnight](https://www.docs.boxknight.com/)
|
- [BoxKnight](https://www.docs.boxknight.com/)
|
||||||
|
- [Quaderno API](https://developers.quaderno.io/api)
|
||||||
|
|
||||||
_Pull requests to add your own API page to the list are welcome_
|
_Pull requests to add your own API page to the list are welcome_
|
||||||
|
|
||||||
|
@ -117,15 +116,14 @@ Redoc uses the following [specification extensions](https://redocly.com/docs/api
|
||||||
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
||||||
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for tags that refer to non-navigation properties like Pagination, Rate-Limits, etc
|
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for tags that refer to non-navigation properties like Pagination, Rate-Limits, etc
|
||||||
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
|
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
|
||||||
|
* [`x-badges`](docs/redoc-vendor-extensions.md#x-badges) - specify operation badges
|
||||||
* [`x-examples`](docs/redoc-vendor-extensions.md#x-examples) - specify JSON example for requests
|
* [`x-examples`](docs/redoc-vendor-extensions.md#x-examples) - specify JSON example for requests
|
||||||
* [`x-nullable`](docs/redoc-vendor-extensions.md#x-nullable) - mark schema param as a nullable
|
* [`x-nullable`](docs/redoc-vendor-extensions.md#x-nullable) - mark schema param as a nullable
|
||||||
* [`x-displayName`](docs/redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories
|
* [`x-displayName`](docs/redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories
|
||||||
* [`x-tagGroups`](docs/redoc-vendor-extensions.md#x-tagGroups) - group tags by categories in the side menu
|
* [`x-tagGroups`](docs/redoc-vendor-extensions.md#x-tagGroups) - group tags by categories in the side menu
|
||||||
* [`x-servers`](docs/redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
* [`x-servers`](docs/redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
||||||
* [`x-ignoredHeaderParameters`](docs/redoc-vendor-extensions.md#x-ignoredHeaderParameters) - ability to specify header parameter names to ignore
|
|
||||||
* [`x-additionalPropertiesName`](docs/redoc-vendor-extensions.md#x-additionalPropertiesName) - ability to supply a descriptive name for the additional property keys
|
* [`x-additionalPropertiesName`](docs/redoc-vendor-extensions.md#x-additionalPropertiesName) - ability to supply a descriptive name for the additional property keys
|
||||||
* [`x-summary`](docs/redoc-vendor-extensions.md#x-summary) - for Response object, use as the response button text, with description rendered under the button
|
* [`x-summary`](docs/redoc-vendor-extensions.md#x-summary) - for Response object, use as the response button text, with description rendered under the button
|
||||||
* [`x-extendedDiscriminator`](docs/redoc-vendor-extensions.md#x-extendedDiscriminator) - in Schemas, uses this to solve name-clash issues with the standard discriminator
|
|
||||||
* [`x-explicitMappingOnly`](docs/redoc-vendor-extensions.md#x-explicitMappingOnly) - in Schemas, display a more descriptive property name in objects with additionalProperties when viewing the property list with an object
|
* [`x-explicitMappingOnly`](docs/redoc-vendor-extensions.md#x-explicitMappingOnly) - in Schemas, display a more descriptive property name in objects with additionalProperties when viewing the property list with an object
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# npm i -g http-server
|
# npm i -g http-server
|
||||||
# http-server -p 8000 --cors
|
# http-server -p 8000 --cors
|
||||||
|
|
||||||
FROM node:12-alpine
|
FROM node:18-alpine
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache git
|
RUN apk update && apk add --no-cache git
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ RUN apk update && apk add --no-cache git
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY package.json package-lock.json /build/
|
COPY package.json package-lock.json /build/
|
||||||
RUN npm ci --no-optional --ignore-scripts
|
RUN npm ci --no-optional --ignore-scripts
|
||||||
|
RUN npm explore esbuild -- npm run postinstall
|
||||||
|
|
||||||
# copy only required for the build files
|
# copy only required for the build files
|
||||||
COPY src /build/src
|
COPY src /build/src
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>ReDoc Interactive Demo</title>
|
<title>Redoc Interactive Demo</title>
|
||||||
<meta name="description" content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation" />
|
<meta
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
name="description"
|
||||||
|
content="Redoc Interactive Demo. OpenAPI-generated API Reference Documentation"
|
||||||
|
/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
<meta property="og:title" content="ReDoc Interactive Demo">
|
<meta property="og:title" content="Redoc Interactive Demo" />
|
||||||
<meta property="og:description" content="ReDoc Interactive Demo. OpenAPI/Swagger-generated API Reference Documentation">
|
<meta
|
||||||
<meta property="og:image" content="https://user-images.githubusercontent.com/3975738/37729752-8a9ea38a-2d46-11e8-8438-42ed26bf1751.png">
|
property="og:description"
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
content="Redoc Interactive Demo. OpenAPI-generated API Reference Documentation"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="https://user-images.githubusercontent.com/3975738/37729752-8a9ea38a-2d46-11e8-8438-42ed26bf1751.png"
|
||||||
|
/>
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
@ -22,18 +30,28 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
<link
|
||||||
</head>
|
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="container"> </div>
|
<div id="container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function (i, s, o, g, r, a, m) {
|
(function (i, s, o, g, r, a, m) {
|
||||||
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
|
i['GoogleAnalyticsObject'] = r;
|
||||||
(i[r].q = i[r].q || []).push(arguments)
|
(i[r] =
|
||||||
}, i[r].l = 1 * new Date(); a = s.createElement(o),
|
i[r] ||
|
||||||
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
|
function () {
|
||||||
|
(i[r].q = i[r].q || []).push(arguments);
|
||||||
|
}),
|
||||||
|
(i[r].l = 1 * new Date());
|
||||||
|
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
|
||||||
|
a.async = 1;
|
||||||
|
a.src = g;
|
||||||
|
m.parentNode.insertBefore(a, m);
|
||||||
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
||||||
|
|
||||||
if (window.location.host === 'rebilly.github.io') {
|
if (window.location.host === 'rebilly.github.io') {
|
||||||
|
@ -41,6 +59,5 @@
|
||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,15 +1,16 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { RedocStandalone } from '../src';
|
import { RedocStandalone } from '../src';
|
||||||
import ComboBox from './ComboBox';
|
import ComboBox from './ComboBox';
|
||||||
import FileInput from './components/FileInput';
|
import FileInput from './components/FileInput';
|
||||||
|
|
||||||
const DEFAULT_SPEC = 'openapi.yaml';
|
const DEFAULT_SPEC = 'museum.yaml';
|
||||||
const NEW_VERSION_SPEC = 'openapi-3-1.yaml';
|
const NEW_VERSION_PETSTORE = 'openapi-3-1.yaml';
|
||||||
|
|
||||||
const demos = [
|
const demos = [
|
||||||
{ value: NEW_VERSION_SPEC, label: 'Petstore OpenAPI 3.1' },
|
{ value: DEFAULT_SPEC, label: 'Museum API' },
|
||||||
|
{ value: NEW_VERSION_PETSTORE, label: 'Petstore OpenAPI 3.1' },
|
||||||
{ value: 'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml', label: 'Instagram' },
|
{ value: 'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml', label: 'Instagram' },
|
||||||
{
|
{
|
||||||
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',
|
||||||
|
@ -54,7 +55,7 @@ class DemoApp extends React.Component<
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange = (url: string) => {
|
handleChange = (url: string) => {
|
||||||
if (url === NEW_VERSION_SPEC) {
|
if (url === NEW_VERSION_PETSTORE) {
|
||||||
this.setState({ cors: false });
|
this.setState({ cors: false });
|
||||||
0;
|
0;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,7 @@ class DemoApp extends React.Component<
|
||||||
<RedocStandalone
|
<RedocStandalone
|
||||||
spec={this.state.spec}
|
spec={this.state.spec}
|
||||||
specUrl={proxiedUrl}
|
specUrl={proxiedUrl}
|
||||||
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
|
options={{ scrollYOffset: 'nav', sanitize: true }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -178,7 +179,9 @@ const Logo = styled.img`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
render(<DemoApp />, document.getElementById('container'));
|
const container = document.getElementById('container');
|
||||||
|
const root = createRoot(container!);
|
||||||
|
root.render(<DemoApp />);
|
||||||
|
|
||||||
/* ====== Helpers ====== */
|
/* ====== Helpers ====== */
|
||||||
function updateQueryStringParameter(uri, key, value) {
|
function updateQueryStringParameter(uri, key, value) {
|
||||||
|
|
BIN
demo/museum-logo.png
Normal file
BIN
demo/museum-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
787
demo/museum.yaml
Normal file
787
demo/museum.yaml
Normal file
|
@ -0,0 +1,787 @@
|
||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: Redocly Museum API
|
||||||
|
description: An imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.
|
||||||
|
version: 1.0.0
|
||||||
|
contact:
|
||||||
|
email: team@redocly.com
|
||||||
|
url: 'https://redocly.com/docs/cli/'
|
||||||
|
x-logo:
|
||||||
|
url: 'https://redocly.github.io/redoc/museum-logo.png'
|
||||||
|
altText: Museum logo
|
||||||
|
license:
|
||||||
|
name: MIT
|
||||||
|
url: 'https://opensource.org/license/mit/ '
|
||||||
|
servers:
|
||||||
|
- url: 'https://api.fake-museum-example.com/v1'
|
||||||
|
paths:
|
||||||
|
/museum-hours:
|
||||||
|
get:
|
||||||
|
summary: Get museum hours
|
||||||
|
description: Get upcoming museum operating hours
|
||||||
|
operationId: getMuseumHours
|
||||||
|
tags:
|
||||||
|
- Operations
|
||||||
|
x-badges:
|
||||||
|
- name: 'Beta'
|
||||||
|
position: before
|
||||||
|
color: purple
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/StartDate'
|
||||||
|
- $ref: '#/components/parameters/PaginationPage'
|
||||||
|
- $ref: '#/components/parameters/PaginationLimit'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GetMuseumHoursResponse'
|
||||||
|
examples:
|
||||||
|
default:
|
||||||
|
summary: Museum opening hours
|
||||||
|
value:
|
||||||
|
- date: '2023-09-11'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-12'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-13'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-17'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
closed:
|
||||||
|
summary: The museum is closed
|
||||||
|
value: []
|
||||||
|
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
/special-events:
|
||||||
|
post:
|
||||||
|
security: []
|
||||||
|
operationId: CreateSpecialEvent
|
||||||
|
summary: Create special event
|
||||||
|
tags:
|
||||||
|
- Events
|
||||||
|
x-badges:
|
||||||
|
- name: 'Alpha'
|
||||||
|
color: purple
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateSpecialEventRequest'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/CreateSpecialEventRequestExample'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SpecialEventResponse'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/CreateSpecialEventResponseExample'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
get:
|
||||||
|
summary: List special events
|
||||||
|
description: Return a list of upcoming special events at the museum.
|
||||||
|
security: []
|
||||||
|
operationId: listSpecialEvents
|
||||||
|
x-badges:
|
||||||
|
- name: 'Gamma'
|
||||||
|
tags:
|
||||||
|
- Events
|
||||||
|
parameters:
|
||||||
|
- name: startDate
|
||||||
|
in: query
|
||||||
|
description: The starting date to retrieve future operating hours from. Defaults to today's date.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2023-02-23
|
||||||
|
- name: endDate
|
||||||
|
in: query
|
||||||
|
description: The end of a date range to retrieve special events for. Defaults to 7 days after `startDate`.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2023-04-18
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
description: The page number to retrieve.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
example: 2
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
description: The number of days per page.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 10
|
||||||
|
maximum: 30
|
||||||
|
example: 15
|
||||||
|
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ListSpecialEventsResponse'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/ListSpecialEventsResponseExample'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
/special-events/{eventId}:
|
||||||
|
get:
|
||||||
|
summary: Get special event
|
||||||
|
description: Get details about a special event.
|
||||||
|
operationId: getSpecialEvent
|
||||||
|
tags:
|
||||||
|
- Events
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/EventId'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SpecialEventResponse'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/GetSpecialEventResponseExample'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
patch:
|
||||||
|
summary: Update special event
|
||||||
|
description: Update the details of a special event
|
||||||
|
operationId: updateSpecialEvent
|
||||||
|
tags:
|
||||||
|
- Events
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/EventId'
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UpdateSpecialEventRequest'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/UpdateSpecialEventRequestExample'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SpecialEventResponse'
|
||||||
|
examples:
|
||||||
|
default_example:
|
||||||
|
$ref: '#/components/examples/UpdateSpecialEventResponseExample'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
delete:
|
||||||
|
summary: Delete special event
|
||||||
|
description: Delete a special event from the collection. Allows museum to cancel planned events.
|
||||||
|
operationId: deleteSpecialEvent
|
||||||
|
tags:
|
||||||
|
- Events
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/EventId'
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Success - no content
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'401':
|
||||||
|
description: Unauthorized
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
/tickets:
|
||||||
|
post:
|
||||||
|
summary: Buy museum tickets
|
||||||
|
description: Purchase museum tickets for general entry or special events.
|
||||||
|
operationId: buyMuseumTickets
|
||||||
|
tags:
|
||||||
|
- Tickets
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/BuyMuseumTicketsRequest'
|
||||||
|
examples:
|
||||||
|
general_entry:
|
||||||
|
$ref: '#/components/examples/BuyGeneralTicketsRequestExample'
|
||||||
|
event_entry:
|
||||||
|
$ref: '#/components/examples/BuyEventTicketsRequestExample'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/BuyMuseumTicketsResponse'
|
||||||
|
examples:
|
||||||
|
general_entry:
|
||||||
|
$ref: '#/components/examples/BuyGeneralTicketsResponseExample'
|
||||||
|
event_entry:
|
||||||
|
$ref: '#/components/examples/BuyEventTicketsResponseExample'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
/tickets/{ticketId}/qr:
|
||||||
|
get:
|
||||||
|
summary: Get ticket QR code
|
||||||
|
description: Return an image of your ticket with scannable QR code. Used for event entry.
|
||||||
|
operationId: getTicketCode
|
||||||
|
tags:
|
||||||
|
- Tickets
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TicketId'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Scannable event ticket in image format
|
||||||
|
content:
|
||||||
|
image/png:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GetTicketCodeResponse'
|
||||||
|
'400':
|
||||||
|
description: Bad request
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SpecialEvent:
|
||||||
|
description: Request payload for creating new special events at the museum.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Name of the special event
|
||||||
|
type: string
|
||||||
|
example: Fossil lecture
|
||||||
|
location:
|
||||||
|
description: Location where the special event is held
|
||||||
|
type: string
|
||||||
|
example: Lecture theatre
|
||||||
|
eventDescription:
|
||||||
|
description: Description of the special event
|
||||||
|
type: string
|
||||||
|
example: Our panel of experts will share their favorite fossils and explain why they are so great.
|
||||||
|
dates:
|
||||||
|
description: List of planned dates for the special event
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2024-03-29
|
||||||
|
price:
|
||||||
|
description: Price of a ticket for the special event
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
example: 12.50
|
||||||
|
|
||||||
|
TicketType:
|
||||||
|
description: Type of ticket being purchased. Use `general` for regular museum entry and `event` for tickets to special events.
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- event
|
||||||
|
- general
|
||||||
|
x-enumDescriptions:
|
||||||
|
event: Special event ticket
|
||||||
|
general: General museum entry ticket
|
||||||
|
example: event
|
||||||
|
Date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2023-10-29
|
||||||
|
Email:
|
||||||
|
description: Email address for ticket purchaser.
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
example: museum-lover@example.com
|
||||||
|
Phone:
|
||||||
|
description: Phone number for the ticket purchaser (optional).
|
||||||
|
type: string
|
||||||
|
example: +1(234)-567-8910
|
||||||
|
BuyMuseumTicketsRequest:
|
||||||
|
description: Request payload used for purchasing museum tickets.
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ticketType:
|
||||||
|
$ref: '#/components/schemas/TicketType'
|
||||||
|
eventId:
|
||||||
|
description: Unique identifier for a special event. Required if purchasing tickets for the museum's special events.
|
||||||
|
$ref: '#/components/schemas/EventId'
|
||||||
|
ticketDate:
|
||||||
|
description: Date that the ticket is valid for.
|
||||||
|
$ref: '#/components/schemas/Date'
|
||||||
|
email:
|
||||||
|
$ref: '#/components/schemas/Email'
|
||||||
|
phone:
|
||||||
|
$ref: '#/components/schemas/Phone'
|
||||||
|
required:
|
||||||
|
- ticketType
|
||||||
|
- ticketDate
|
||||||
|
- email
|
||||||
|
TicketMessage:
|
||||||
|
description: Confirmation message after a ticket purchase.
|
||||||
|
type: string
|
||||||
|
example: Museum general entry ticket purchased
|
||||||
|
TicketId:
|
||||||
|
description: Unique identifier for museum ticket. Generated when purchased.
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c
|
||||||
|
TicketConfirmation:
|
||||||
|
description: Unique confirmation code used to verify ticket purchase.
|
||||||
|
type: string
|
||||||
|
example: ticket-event-a98c8f-7eb12
|
||||||
|
BuyMuseumTicketsResponse:
|
||||||
|
description: Details for a museum ticket after a successful purchase.
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
$ref: '#/components/schemas/TicketMessage'
|
||||||
|
eventName:
|
||||||
|
$ref: '#/components/schemas/EventName'
|
||||||
|
ticketId:
|
||||||
|
$ref: '#/components/schemas/TicketId'
|
||||||
|
ticketType:
|
||||||
|
$ref: '#/components/schemas/TicketType'
|
||||||
|
ticketDate:
|
||||||
|
description: Date the ticket is valid for.
|
||||||
|
$ref: '#/components/schemas/Date'
|
||||||
|
confirmationCode:
|
||||||
|
$ref: '#/components/schemas/TicketConfirmation'
|
||||||
|
required:
|
||||||
|
- message
|
||||||
|
- ticketId
|
||||||
|
- ticketType
|
||||||
|
- ticketDate
|
||||||
|
- confirmationCode
|
||||||
|
GetTicketCodeResponse:
|
||||||
|
description: An image of a ticket with a QR code used for museum or event entry.
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
GetMuseumHoursResponse:
|
||||||
|
description: List of museum operating hours for consecutive days.
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/MuseumDailyHours'
|
||||||
|
MuseumDailyHours:
|
||||||
|
description: Daily operating hours for the museum.
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
date:
|
||||||
|
description: Date the operating hours apply to.
|
||||||
|
$ref: '#/components/schemas/Date'
|
||||||
|
example: 2024-12-31
|
||||||
|
timeOpen:
|
||||||
|
type: string
|
||||||
|
pattern: '^([01]\d|2[0-3]):?([0-5]\d)$'
|
||||||
|
description: Time the museum opens on a specific date. Uses 24 hour time format (`HH:mm`).
|
||||||
|
example: 09:00
|
||||||
|
timeClose:
|
||||||
|
description: Time the museum closes on a specific date. Uses 24 hour time format (`HH:mm`).
|
||||||
|
type: string
|
||||||
|
pattern: '^([01]\d|2[0-3]):?([0-5]\d)$'
|
||||||
|
example: 18:00
|
||||||
|
required:
|
||||||
|
- date
|
||||||
|
- timeOpen
|
||||||
|
- timeClose
|
||||||
|
EventId:
|
||||||
|
description: Identifier for a special event.
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: 3be6453c-03eb-4357-ae5a-984a0e574a54
|
||||||
|
EventName:
|
||||||
|
type: string
|
||||||
|
description: Name of the special event
|
||||||
|
example: Pirate Coding Workshop
|
||||||
|
EventLocation:
|
||||||
|
type: string
|
||||||
|
description: Location where the special event is held
|
||||||
|
example: Computer Room
|
||||||
|
EventDescription:
|
||||||
|
type: string
|
||||||
|
description: Description of the special event
|
||||||
|
example: Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).
|
||||||
|
EventDates:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Date'
|
||||||
|
description: List of planned dates for the special event
|
||||||
|
EventPrice:
|
||||||
|
description: Price of a ticket for the special event
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
example: 25
|
||||||
|
CreateSpecialEventRequest:
|
||||||
|
description: Request payload for creating new special events at the museum.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
$ref: '#/components/schemas/EventName'
|
||||||
|
location:
|
||||||
|
$ref: '#/components/schemas/EventLocation'
|
||||||
|
eventDescription:
|
||||||
|
$ref: '#/components/schemas/EventDescription'
|
||||||
|
dates:
|
||||||
|
$ref: '#/components/schemas/EventDates'
|
||||||
|
price:
|
||||||
|
$ref: '#/components/schemas/EventPrice'
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- location
|
||||||
|
- eventDescription
|
||||||
|
- dates
|
||||||
|
- price
|
||||||
|
UpdateSpecialEventRequest:
|
||||||
|
description: Request payload for updating an existing special event. Only included fields are updated in the event.
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
$ref: '#/components/schemas/EventName'
|
||||||
|
location:
|
||||||
|
$ref: '#/components/schemas/EventLocation'
|
||||||
|
eventDescription:
|
||||||
|
$ref: '#/components/schemas/EventDescription'
|
||||||
|
dates:
|
||||||
|
$ref: '#/components/schemas/EventDates'
|
||||||
|
price:
|
||||||
|
$ref: '#/components/schemas/EventPrice'
|
||||||
|
ListSpecialEventsResponse:
|
||||||
|
description: A list of upcoming special events
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/SpecialEventResponse'
|
||||||
|
SpecialEventResponse:
|
||||||
|
description: Information about a special event.
|
||||||
|
properties:
|
||||||
|
eventId:
|
||||||
|
$ref: '#/components/schemas/EventId'
|
||||||
|
name:
|
||||||
|
$ref: '#/components/schemas/EventName'
|
||||||
|
location:
|
||||||
|
$ref: '#/components/schemas/EventLocation'
|
||||||
|
eventDescription:
|
||||||
|
$ref: '#/components/schemas/EventDescription'
|
||||||
|
dates:
|
||||||
|
$ref: '#/components/schemas/EventDates'
|
||||||
|
price:
|
||||||
|
$ref: '#/components/schemas/EventPrice'
|
||||||
|
required:
|
||||||
|
- eventId
|
||||||
|
- name
|
||||||
|
- location
|
||||||
|
- eventDescription
|
||||||
|
- dates
|
||||||
|
- price
|
||||||
|
securitySchemes:
|
||||||
|
MuseumPlaceholderAuth:
|
||||||
|
type: http
|
||||||
|
scheme: basic
|
||||||
|
examples:
|
||||||
|
BuyGeneralTicketsRequestExample:
|
||||||
|
summary: General entry ticket
|
||||||
|
value:
|
||||||
|
ticketType: general
|
||||||
|
ticketDate: 2023-09-07
|
||||||
|
email: todd@example.com
|
||||||
|
BuyEventTicketsRequestExample:
|
||||||
|
summary: Special event ticket
|
||||||
|
value:
|
||||||
|
ticketType: general
|
||||||
|
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||||
|
ticketDate: 2023-09-05
|
||||||
|
email: todd@example.com
|
||||||
|
BuyGeneralTicketsResponseExample:
|
||||||
|
summary: General entry ticket
|
||||||
|
value:
|
||||||
|
message: Museum general entry ticket purchased
|
||||||
|
ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a
|
||||||
|
ticketType: general
|
||||||
|
ticketDate: 2023-09-07
|
||||||
|
confirmationCode: ticket-general-e5e5c6-dce78
|
||||||
|
BuyEventTicketsResponseExample:
|
||||||
|
summary: Special event ticket
|
||||||
|
value:
|
||||||
|
message: Museum special event ticket purchased
|
||||||
|
ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9
|
||||||
|
eventName: Mermaid Treasure Identification and Analysis
|
||||||
|
ticketType: event
|
||||||
|
ticketDate: 2023-09-05
|
||||||
|
confirmationCode: ticket-event-9c55eg-8v82a
|
||||||
|
CreateSpecialEventRequestExample:
|
||||||
|
summary: Create special event
|
||||||
|
value:
|
||||||
|
name: Mermaid Treasure Identification and Analysis
|
||||||
|
location: Under the seaaa 🦀 🎶 🌊.
|
||||||
|
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||||
|
dates:
|
||||||
|
- 2023-09-05
|
||||||
|
- 2023-09-08
|
||||||
|
price: 0
|
||||||
|
CreateSpecialEventResponseExample:
|
||||||
|
summary: Special event created
|
||||||
|
value:
|
||||||
|
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||||
|
name: Mermaid Treasure Identification and Analysis
|
||||||
|
location: Under the seaaa 🦀 🎶 🌊.
|
||||||
|
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||||
|
dates:
|
||||||
|
- 2023-09-05
|
||||||
|
- 2023-09-08
|
||||||
|
price: 30
|
||||||
|
GetSpecialEventResponseExample:
|
||||||
|
summary: Get special event
|
||||||
|
value:
|
||||||
|
eventId: 6744a0da-4121-49cd-8479-f8cc20526495
|
||||||
|
name: Time Traveler Tea Party
|
||||||
|
location: Temporal Tearoom
|
||||||
|
eventDescription: Sip tea with important historical figures.
|
||||||
|
dates:
|
||||||
|
- 2023-11-18
|
||||||
|
- 2023-11-25
|
||||||
|
- 2023-12-02
|
||||||
|
price: 60
|
||||||
|
ListSpecialEventsResponseExample:
|
||||||
|
summary: List of special events
|
||||||
|
value:
|
||||||
|
- eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97
|
||||||
|
name: Sasquatch Ballet
|
||||||
|
location: Seattle... probably
|
||||||
|
eventDescription: They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.
|
||||||
|
dates:
|
||||||
|
- 2023-12-15
|
||||||
|
- 2023-12-22
|
||||||
|
price: 40
|
||||||
|
- eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483
|
||||||
|
name: Solar Telescope Demonstration
|
||||||
|
location: Far from the sun.
|
||||||
|
eventDescription: Look at the sun without going blind!
|
||||||
|
dates:
|
||||||
|
- 2023-09-07
|
||||||
|
- 2023-09-14
|
||||||
|
price: 50
|
||||||
|
- eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb
|
||||||
|
name: Cook like a Caveman
|
||||||
|
location: Fire Pit on East side
|
||||||
|
eventDescription: Learn to cook on an open flame.
|
||||||
|
dates:
|
||||||
|
- 2023-11-10
|
||||||
|
- 2023-11-17
|
||||||
|
- 2023-11-24
|
||||||
|
price: 5
|
||||||
|
- eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910
|
||||||
|
name: Underwater Basket Weaving
|
||||||
|
location: Rec Center Pool next door.
|
||||||
|
eventDescription: Learn to weave baskets underwater.
|
||||||
|
dates:
|
||||||
|
- 2023-09-12
|
||||||
|
- 2023-09-15
|
||||||
|
price: 15
|
||||||
|
- eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||||
|
name: Mermaid Treasure Identification and Analysis
|
||||||
|
location: Room Sea-12
|
||||||
|
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.
|
||||||
|
dates:
|
||||||
|
- 2023-09-05
|
||||||
|
- 2023-09-08
|
||||||
|
price: 30
|
||||||
|
- eventId: 6744a0da-4121-49cd-8479-f8cc20526495
|
||||||
|
name: Time Traveler Tea Party
|
||||||
|
location: Temporal Tearoom
|
||||||
|
eventDescription: Sip tea with important historical figures.
|
||||||
|
dates:
|
||||||
|
- 2023-11-18
|
||||||
|
- 2023-11-25
|
||||||
|
- 2023-12-02
|
||||||
|
price: 60
|
||||||
|
- eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54
|
||||||
|
name: Pirate Coding Workshop
|
||||||
|
location: Computer Room
|
||||||
|
eventDescription: Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).
|
||||||
|
dates:
|
||||||
|
- 2023-10-29
|
||||||
|
- 2023-10-30
|
||||||
|
- 2023-10-31
|
||||||
|
price: 45
|
||||||
|
- eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78
|
||||||
|
name: Llama Street Art Through the Ages
|
||||||
|
location: Auditorium
|
||||||
|
eventDescription: Llama street art?! Alpaca my bags -- let's go!
|
||||||
|
dates:
|
||||||
|
- 2023-10-29
|
||||||
|
- 2023-10-30
|
||||||
|
- 2023-10-31
|
||||||
|
price: 45
|
||||||
|
- eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957
|
||||||
|
name: The Great Parrot Debate
|
||||||
|
location: Outdoor Amphitheatre
|
||||||
|
eventDescription: See leading parrot minds discuss important geopolitical issues.
|
||||||
|
dates:
|
||||||
|
- 2023-11-03
|
||||||
|
- 2023-11-10
|
||||||
|
price: 35
|
||||||
|
- eventId: b92d46b7-4c5d-422b-87a5-287767e26f29
|
||||||
|
name: Eat a Bunch of Corn
|
||||||
|
location: Cafeteria
|
||||||
|
eventDescription: We accidentally bought too much corn. Please come eat it.
|
||||||
|
dates:
|
||||||
|
- 2023-11-10
|
||||||
|
- 2023-11-17
|
||||||
|
- 2023-11-24
|
||||||
|
price: 5
|
||||||
|
UpdateSpecialEventRequestExample:
|
||||||
|
summary: Update special event request
|
||||||
|
value:
|
||||||
|
location: On the beach.
|
||||||
|
price: 15
|
||||||
|
UpdateSpecialEventResponseExample:
|
||||||
|
summary: Update special event
|
||||||
|
value:
|
||||||
|
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||||
|
name: Mermaid Treasure Identification and Analysis
|
||||||
|
location: On the beach.
|
||||||
|
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||||
|
dates:
|
||||||
|
- 2023-09-05
|
||||||
|
- 2023-09-08
|
||||||
|
price: 15
|
||||||
|
GetMuseumHours:
|
||||||
|
summary: Museum opening hours
|
||||||
|
value:
|
||||||
|
- date: '2023-09-11'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-12'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-13'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-14'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-15'
|
||||||
|
timeOpen: '10:00'
|
||||||
|
timeClose: '16:00'
|
||||||
|
- date: '2023-09-18'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-19'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-20'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-21'
|
||||||
|
timeOpen: '09:00'
|
||||||
|
timeClose: '18:00'
|
||||||
|
- date: '2023-09-22'
|
||||||
|
timeOpen: '10:00'
|
||||||
|
timeClose: '16:00'
|
||||||
|
ClosedMuseumHours:
|
||||||
|
summary: The museum is closed
|
||||||
|
value: []
|
||||||
|
parameters:
|
||||||
|
PaginationPage:
|
||||||
|
name: page
|
||||||
|
in: query
|
||||||
|
description: The page number to retrieve.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
example: 2
|
||||||
|
PaginationLimit:
|
||||||
|
name: limit
|
||||||
|
in: query
|
||||||
|
description: The number of days per page.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 10
|
||||||
|
maximum: 30
|
||||||
|
example: 15
|
||||||
|
EventId:
|
||||||
|
name: eventId
|
||||||
|
in: path
|
||||||
|
description: An identifier for a special event.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: dad4bce8-f5cb-4078-a211-995864315e39
|
||||||
|
StartDate:
|
||||||
|
name: startDate
|
||||||
|
in: query
|
||||||
|
description: The starting date to retrieve future operating hours from. Defaults to today's date.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2023-02-23
|
||||||
|
EndDate:
|
||||||
|
name: endDate
|
||||||
|
in: query
|
||||||
|
description: The end of a date range to retrieve special events for. Defaults to 7 days after `startDate`.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
example: 2023-04-18
|
||||||
|
TicketId:
|
||||||
|
name: ticketId
|
||||||
|
in: path
|
||||||
|
description: An identifier for a ticket to a museum event. Used to generate ticket image.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: Operations
|
||||||
|
x-displayName: About the museum
|
||||||
|
description: Operational information about the museum.
|
||||||
|
- name: Events
|
||||||
|
x-displayName: Upcoming events
|
||||||
|
description: Special events hosted by the Museum.
|
||||||
|
- name: Tickets
|
||||||
|
x-displayName: Buy tickets
|
||||||
|
description: Museum tickets for general entrance or special events.
|
||||||
|
|
||||||
|
x-tagGroups:
|
||||||
|
- name: Plan your visit
|
||||||
|
tags:
|
||||||
|
- Operations
|
||||||
|
- Events
|
||||||
|
- name: Purchases
|
||||||
|
tags:
|
||||||
|
- Tickets
|
||||||
|
- name: Entities
|
||||||
|
tags:
|
||||||
|
- Schemas
|
||||||
|
|
||||||
|
security:
|
||||||
|
- MuseumPlaceholderAuth: []
|
|
@ -106,6 +106,10 @@ paths:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- pet
|
- pet
|
||||||
|
x-badges:
|
||||||
|
- name: 'Beta'
|
||||||
|
position: before
|
||||||
|
color: purple
|
||||||
summary: Add a new pet to the store
|
summary: Add a new pet to the store
|
||||||
description: Add new pet to the store inventory.
|
description: Add new pet to the store inventory.
|
||||||
operationId: addPet
|
operationId: addPet
|
||||||
|
@ -150,6 +154,9 @@ paths:
|
||||||
put:
|
put:
|
||||||
tags:
|
tags:
|
||||||
- pet
|
- pet
|
||||||
|
x-badges:
|
||||||
|
- name: 'Alpha'
|
||||||
|
color: purple
|
||||||
summary: Update an existing pet
|
summary: Update an existing pet
|
||||||
description: ''
|
description: ''
|
||||||
operationId: updatePet
|
operationId: updatePet
|
||||||
|
@ -183,6 +190,8 @@ paths:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- pet
|
- pet
|
||||||
|
x-badges:
|
||||||
|
- name: 'Gamma'
|
||||||
summary: Find pet by ID
|
summary: Find pet by ID
|
||||||
description: Returns a single pet
|
description: Returns a single pet
|
||||||
operationId: getPetById
|
operationId: getPetById
|
||||||
|
@ -1074,6 +1083,10 @@ components:
|
||||||
- available
|
- available
|
||||||
- pending
|
- pending
|
||||||
- sold
|
- sold
|
||||||
|
x-enumDescriptions:
|
||||||
|
available: Available status
|
||||||
|
pending: Pending status
|
||||||
|
sold: Sold status
|
||||||
petType:
|
petType:
|
||||||
description: Type of a pet
|
description: Type of a pet
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import type { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
|
import type { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
|
||||||
import RedocStandalone from './hot';
|
import { RedocStandalone } from '../../src';
|
||||||
|
|
||||||
const big = window.location.search.indexOf('big') > -1;
|
const big = window.location.search.indexOf('big') > -1;
|
||||||
const swagger = window.location.search.indexOf('swagger') > -1;
|
const swagger = window.location.search.indexOf('swagger') > -1;
|
||||||
|
@ -9,8 +9,14 @@ const swagger = window.location.search.indexOf('swagger') > -1;
|
||||||
const userUrl = window.location.search.match(/url=(.*)$/);
|
const userUrl = window.location.search.match(/url=(.*)$/);
|
||||||
|
|
||||||
const specUrl =
|
const specUrl =
|
||||||
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
|
(userUrl && userUrl[1]) || (swagger ? 'museum.yaml' : big ? 'big-openapi.json' : 'museum.yaml');
|
||||||
|
|
||||||
const options: RedocRawOptions = { nativeScrollbars: false, maxDisplayedEnumValues: 3 };
|
const options: RedocRawOptions = {
|
||||||
|
nativeScrollbars: false,
|
||||||
|
maxDisplayedEnumValues: 3,
|
||||||
|
schemaDefinitionsTagName: 'schemas',
|
||||||
|
};
|
||||||
|
|
||||||
render(<RedocStandalone specUrl={specUrl} options={options} />, document.getElementById('example'));
|
const container = document.getElementById('example');
|
||||||
|
const root = createRoot(container!);
|
||||||
|
root.render(<RedocStandalone specUrl={specUrl} options={options} />);
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
// eslint-disable-next-line import/no-internal-modules
|
|
||||||
import { hot } from 'react-hot-loader/root';
|
|
||||||
import { RedocStandalone as RedocStandaloneOrig, RedocStandaloneProps } from '../../src';
|
|
||||||
|
|
||||||
const RedocStandalone = function (props: RedocStandaloneProps) {
|
|
||||||
return <RedocStandaloneOrig {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default hot(RedocStandalone);
|
|
|
@ -1,10 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>ReDoc</title>
|
<title>Redoc</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -15,11 +14,13 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
<link
|
||||||
</head>
|
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<redoc id="example"></redoc>
|
<redoc id="example"></redoc>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -14,7 +14,7 @@ function root(filename) {
|
||||||
return resolve(__dirname + '/' + filename);
|
return resolve(__dirname + '/' + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) => ({
|
export default (env: { playground?: boolean; bench?: boolean } = {}) => ({
|
||||||
entry: [
|
entry: [
|
||||||
root('../src/polyfills.ts'),
|
root('../src/polyfills.ts'),
|
||||||
root(
|
root(
|
||||||
|
@ -51,12 +51,6 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
fs: false,
|
fs: false,
|
||||||
os: false,
|
os: false,
|
||||||
},
|
},
|
||||||
alias:
|
|
||||||
mode !== 'production'
|
|
||||||
? {
|
|
||||||
'react-dom': '@hot-loader/react-dom',
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
performance: false,
|
performance: false,
|
||||||
|
@ -121,7 +115,7 @@ export default (env: { playground?: boolean; bench?: boolean } = {}, { mode }) =
|
||||||
webpackIgnore(/json-schema-ref-parser\/lib\/dereference\.js/),
|
webpackIgnore(/json-schema-ref-parser\/lib\/dereference\.js/),
|
||||||
webpackIgnore(/^\.\/SearchWorker\.worker$/),
|
webpackIgnore(/^\.\/SearchWorker\.worker$/),
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: ['demo/openapi.yaml'],
|
patterns: ['demo/museum.yaml'],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
185
docs/config.md
185
docs/config.md
|
@ -6,12 +6,12 @@ Each deployment type has documentation on how to configure options for that type
|
||||||
|
|
||||||
**Versions: 2.x**
|
**Versions: 2.x**
|
||||||
|
|
||||||
:::success Client-side configuration
|
{% admonition type="success" name="Client-side configuration" %}
|
||||||
|
|
||||||
Using Redoc as a standalone (HTML or React) tool, these options must be presented in [kebab case](https://en.wikipedia.org/wiki/Letter_case#Kebab_case).
|
Using Redoc as a standalone (HTML or React) tool, these options must be presented in [kebab case](https://en.wikipedia.org/wiki/Letter_case#Kebab_case).
|
||||||
For example, `scrollYOffset` becomes `scroll-y-offset`, and `expandResponses` becomes `expand-responses`.
|
For example, `scrollYOffset` becomes `scroll-y-offset`, and `expandResponses` becomes `expand-responses`.
|
||||||
|
|
||||||
:::
|
{% /admonition %}
|
||||||
|
|
||||||
## Functional settings
|
## Functional settings
|
||||||
|
|
||||||
|
@ -26,59 +26,19 @@ Sets the minimum amount of characters that need to be typed into the search dial
|
||||||
|
|
||||||
_Default: 3_
|
_Default: 3_
|
||||||
|
|
||||||
### expandDefaultServerVariables
|
### hideDownloadButtons
|
||||||
|
|
||||||
Enables or disables expanding default server variables.
|
|
||||||
|
|
||||||
### expandResponses
|
|
||||||
|
|
||||||
Controls which responses to expand by default. Specify one or more responses by providing their response codes as a comma-separated list without spaces, for example `expandResponses='200,201'`. Special value 'all' expands all responses by default. Be careful: this option can slow down documentation rendering time.
|
|
||||||
|
|
||||||
### expandSingleSchemaField
|
|
||||||
|
|
||||||
Automatically expands the single field in a schema.
|
|
||||||
|
|
||||||
### hideDownloadButton
|
|
||||||
|
|
||||||
Hides the 'Download' button for saving the API definition source file. **This setting does not make the API definition private**; it just hides the button.
|
Hides the 'Download' button for saving the API definition source file. **This setting does not make the API definition private**; it just hides the button.
|
||||||
|
|
||||||
### hideHostname
|
|
||||||
|
|
||||||
If set to `true`, the protocol and hostname are not shown in the operation definition.
|
|
||||||
|
|
||||||
### hideLoading
|
### hideLoading
|
||||||
|
|
||||||
Hides the loading animation. Does not apply to CLI or Workflows-rendered docs.
|
Hides the loading animation. Does not apply to CLI or Workflows-rendered docs.
|
||||||
|
|
||||||
### hideRequestPayloadSample
|
|
||||||
|
|
||||||
Hides request payload examples.
|
|
||||||
|
|
||||||
### hideOneOfDescription
|
|
||||||
|
|
||||||
If set to `true`, the description for `oneOf`/`anyOf` object is not shown in the schema.
|
|
||||||
|
|
||||||
### hideSchemaPattern
|
|
||||||
|
|
||||||
If set to `true`, the pattern is not shown in the schema.
|
|
||||||
|
|
||||||
### hideSchemaTitles
|
### hideSchemaTitles
|
||||||
|
|
||||||
Hides the schema title next to to the type.
|
Hides the schema title next to to the type.
|
||||||
|
|
||||||
### hideSecuritySection
|
### jsonSamplesExpandLevel
|
||||||
|
|
||||||
Hides the Security panel section.
|
|
||||||
|
|
||||||
### hideSingleRequestSampleTab
|
|
||||||
|
|
||||||
Hides the request sample tab for requests with only one sample.
|
|
||||||
|
|
||||||
### htmlTemplate
|
|
||||||
|
|
||||||
Sets the path to the optional HTML file used to modify the layout of the reference docs page.
|
|
||||||
|
|
||||||
### jsonSampleExpandLevel
|
|
||||||
|
|
||||||
Sets the default expand level for JSON payload samples (response and request body). The default value is 2, and the maximum supported value is '+Infinity'. It can also be configured as a string with the special value `all` that expands all levels.
|
Sets the default expand level for JSON payload samples (response and request body). The default value is 2, and the maximum supported value is '+Infinity'. It can also be configured as a string with the special value `all` that expands all levels.
|
||||||
|
|
||||||
|
@ -88,35 +48,17 @@ _Default: 2_
|
||||||
|
|
||||||
Displays only the specified number of enum values. The remaining values are hidden in an expandable area. If not set, all values are displayed.
|
Displays only the specified number of enum values. The remaining values are hidden in an expandable area. If not set, all values are displayed.
|
||||||
|
|
||||||
### menuToggle
|
|
||||||
|
|
||||||
If set to `true`, selecting an expanded item in the sidebar twice collapses it.
|
|
||||||
|
|
||||||
_Default: true_
|
|
||||||
|
|
||||||
### nativeScrollbars
|
|
||||||
|
|
||||||
If set to `true`, the sidebar uses the native scrollbar instead of perfect-scroll. This setting is a scrolling performance optimization for big API definitions.
|
|
||||||
|
|
||||||
### onlyRequiredInSamples
|
### onlyRequiredInSamples
|
||||||
|
|
||||||
Shows only required fields in request samples.
|
Shows only required fields in request samples.
|
||||||
|
|
||||||
### pathInMiddlePanel
|
### sortRequiredPropsFirst
|
||||||
|
|
||||||
Shows the path link and HTTP verb in the middle panel instead of the right panel.
|
|
||||||
|
|
||||||
### payloadSampleIdx
|
|
||||||
|
|
||||||
If set, the payload sample is inserted at the specified index. If there are `N` payload samples and the value configured here is bigger than `N`, the payload sample is inserted last. Indexes start from 0.
|
|
||||||
|
|
||||||
### requiredPropsFirst
|
|
||||||
|
|
||||||
Shows required properties in schemas first, ordered in the same order as in the required array.
|
Shows required properties in schemas first, ordered in the same order as in the required array.
|
||||||
|
|
||||||
### schemaExpansionLevel
|
### schemasExpansionLevel
|
||||||
|
|
||||||
Specifies whether to automatically expand schemas in Reference docs. Set it to `all` to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. For example, `schemaExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
|
Specifies whether to automatically expand schemas in Reference docs. Set it to `all` to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. For example, `schemasExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
|
||||||
|
|
||||||
### scrollYOffset
|
### scrollYOffset
|
||||||
|
|
||||||
|
@ -132,6 +74,111 @@ Note that you can specify the `scrollYOffset` value in any of the following ways
|
||||||
|
|
||||||
Shows specification extensions ('x-' fields). Extensions used by Redoc are ignored. The value can be boolean or an array of strings with names of extensions to display. When used as boolean and set to `true`, all specification extensions are shown.
|
Shows specification extensions ('x-' fields). Extensions used by Redoc are ignored. The value can be boolean or an array of strings with names of extensions to display. When used as boolean and set to `true`, all specification extensions are shown.
|
||||||
|
|
||||||
|
### sanitize
|
||||||
|
|
||||||
|
If set to `true`, the API definition is considered untrusted and all HTML/Markdown is sanitized to prevent XSS.
|
||||||
|
|
||||||
|
### downloadUrls
|
||||||
|
|
||||||
|
Set the URLs used to download the OpenAPI description or other documentation related files from the API documentation page.
|
||||||
|
|
||||||
|
### schemaDefinitionsTagName
|
||||||
|
|
||||||
|
If a value is set, all of the schemas are rendered with the designated tag name. The schemas then render and display in the sidebar navigation (with that associated tag name).
|
||||||
|
|
||||||
|
### generatedSamplesMaxDepth
|
||||||
|
|
||||||
|
The generatedSamplesMaxDepth option controls how many schema levels automatically generated for payload samples. The default is 8 which works well for most APIs, but you can adjust it if necessary for your use case.
|
||||||
|
|
||||||
|
### hidePropertiesPrefix
|
||||||
|
|
||||||
|
In complex data structures or object schemas where properties are nested within parent objects the hidePropertiesPrefix option enables the hiding of parent names for nested properties within the documentation.
|
||||||
|
|
||||||
|
_Default: true_
|
||||||
|
|
||||||
|
## Deprecated Functional settings
|
||||||
|
|
||||||
|
### hideDownloadButton
|
||||||
|
|
||||||
|
Hides the 'Download' button for saving the API definition source file. **This setting does not make the API definition private**; it just hides the button.
|
||||||
|
|
||||||
|
### downloadFileName
|
||||||
|
|
||||||
|
Sets the filename for the downloaded API definition source file.
|
||||||
|
|
||||||
|
### downloadDefinitionUrl
|
||||||
|
|
||||||
|
Sets the URL for the downloaded API definition source file.
|
||||||
|
|
||||||
|
### requiredPropsFirst
|
||||||
|
|
||||||
|
Shows required properties in schemas first, ordered in the same order as in the required array.
|
||||||
|
|
||||||
|
### jsonSampleExpandLevel
|
||||||
|
|
||||||
|
Sets the default expand level for JSON payload samples (response and request body). The default value is 2, and the maximum supported value is '+Infinity'. It can also be configured as a string with the special value `all` that expands all levels.
|
||||||
|
|
||||||
|
_Default: 2_
|
||||||
|
|
||||||
|
### schemaExpansionLevel
|
||||||
|
|
||||||
|
Specifies whether to automatically expand schemas in Reference docs. Set it to `all` to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. For example, `schemaExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
|
||||||
|
|
||||||
|
|
||||||
|
### expandDefaultServerVariables
|
||||||
|
|
||||||
|
Enables or disables expanding default server variables.
|
||||||
|
|
||||||
|
### expandResponses
|
||||||
|
|
||||||
|
Controls which responses to expand by default. Specify one or more responses by providing their response codes as a comma-separated list without spaces, for example `expandResponses='200,201'`. Special value 'all' expands all responses by default. Be careful: this option can slow down documentation rendering time.
|
||||||
|
|
||||||
|
### expandSingleSchemaField
|
||||||
|
|
||||||
|
Automatically expands the single field in a schema.
|
||||||
|
|
||||||
|
### hideHostname
|
||||||
|
|
||||||
|
If set to `true`, the protocol and hostname are not shown in the operation definition.
|
||||||
|
|
||||||
|
### hideRequestPayloadSample
|
||||||
|
|
||||||
|
Hides request payload examples.
|
||||||
|
|
||||||
|
### hideOneOfDescription
|
||||||
|
|
||||||
|
If set to `true`, the description for `oneOf`/`anyOf` object is not shown in the schema.
|
||||||
|
|
||||||
|
### hideSchemaPattern
|
||||||
|
|
||||||
|
If set to `true`, the pattern is not shown in the schema.
|
||||||
|
|
||||||
|
### hideSecuritySection
|
||||||
|
|
||||||
|
Hides the Security panel section.
|
||||||
|
|
||||||
|
### hideSingleRequestSampleTab
|
||||||
|
|
||||||
|
Hides the request sample tab for requests with only one sample.
|
||||||
|
|
||||||
|
### menuToggle
|
||||||
|
|
||||||
|
If set to `true`, selecting an expanded item in the sidebar twice collapses it.
|
||||||
|
|
||||||
|
_Default: true_
|
||||||
|
|
||||||
|
### nativeScrollbars
|
||||||
|
|
||||||
|
If set to `true`, the sidebar uses the native scrollbar instead of perfect-scroll. This setting is a scrolling performance optimization for big API definitions.
|
||||||
|
|
||||||
|
### pathInMiddlePanel
|
||||||
|
|
||||||
|
Shows the path link and HTTP verb in the middle panel instead of the right panel.
|
||||||
|
|
||||||
|
### payloadSampleIdx
|
||||||
|
|
||||||
|
If set, the payload sample is inserted at the specified index. If there are `N` payload samples and the value configured here is bigger than `N`, the payload sample is inserted last. Indexes start from 0.
|
||||||
|
|
||||||
### showObjectSchemaExamples
|
### showObjectSchemaExamples
|
||||||
|
|
||||||
Shows object schema example in the properties; default `false`.
|
Shows object schema example in the properties; default `false`.
|
||||||
|
@ -162,12 +209,12 @@ When set to true, sorts tags in the navigation sidebar and in the middle panel a
|
||||||
|
|
||||||
_Default: false_
|
_Default: false_
|
||||||
|
|
||||||
### untrustedDefinition
|
### untrustedSpec
|
||||||
|
|
||||||
If set to `true`, the API definition is considered untrusted and all HTML/Markdown is sanitized to prevent XSS.
|
If set to `true`, the API definition is considered untrusted and all HTML/Markdown is sanitized to prevent XSS.
|
||||||
|
|
||||||
## Theme settings
|
## Theme settings
|
||||||
|
Change styles for the API documentation page. **Supported in Redoc CE 2.x**.
|
||||||
* `spacing`
|
* `spacing`
|
||||||
* `unit`: 5 # main spacing unit used in autocomputed theme values later
|
* `unit`: 5 # main spacing unit used in autocomputed theme values later
|
||||||
* `sectionHorizontal`: 40 # Horizontal section padding. COMPUTED: spacing.unit * 8
|
* `sectionHorizontal`: 40 # Horizontal section padding. COMPUTED: spacing.unit * 8
|
||||||
|
@ -248,7 +295,7 @@ For more information, refer to [Security definitions injection](./security-defin
|
||||||
|
|
||||||
### OpenAPI specification extensions
|
### OpenAPI specification extensions
|
||||||
|
|
||||||
Redoc uses the following [specification extensions](https://redocly.com/docs/api-reference-docs/spec-extensions/):
|
Redoc uses the following [specification extensions](https://redocly.com/docs-legacy/api-reference-docs/spec-extensions/):
|
||||||
* [`x-logo`](./redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
* [`x-logo`](./redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
||||||
* [`x-traitTag`](./redoc-vendor-extensions.md#x-traittag) - useful for handling out common things like Pagination, Rate-Limits, etc
|
* [`x-traitTag`](./redoc-vendor-extensions.md#x-traittag) - useful for handling out common things like Pagination, Rate-Limits, etc
|
||||||
* [`x-codeSamples`](./redoc-vendor-extensions.md#x-codesamples) - specify operation code samples
|
* [`x-codeSamples`](./redoc-vendor-extensions.md#x-codesamples) - specify operation code samples
|
||||||
|
@ -257,10 +304,8 @@ Redoc uses the following [specification extensions](https://redocly.com/docs/api
|
||||||
* [`x-displayName`](./redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories
|
* [`x-displayName`](./redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories
|
||||||
* [`x-tagGroups`](./redoc-vendor-extensions.md#x-taggroups) - group tags by categories in the side menu
|
* [`x-tagGroups`](./redoc-vendor-extensions.md#x-taggroups) - group tags by categories in the side menu
|
||||||
* [`x-servers`](./redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
* [`x-servers`](./redoc-vendor-extensions.md#x-servers) - ability to specify different servers for API (backported from OpenAPI 3.0)
|
||||||
* [`x-ignoredHeaderParameters`](./redoc-vendor-extensions.md#x-ignoredheaderparameters) - ability to specify header parameter names to ignore
|
|
||||||
* [`x-additionalPropertiesName`](./redoc-vendor-extensions.md#x-additionalpropertiesname) - ability to supply a descriptive name for the additional property keys
|
* [`x-additionalPropertiesName`](./redoc-vendor-extensions.md#x-additionalpropertiesname) - ability to supply a descriptive name for the additional property keys
|
||||||
* [`x-summary`](./redoc-vendor-extensions.md#x-summary) - For Response object, use as the response button text, with description rendered under the button
|
* [`x-summary`](./redoc-vendor-extensions.md#x-summary) - For Response object, use as the response button text, with description rendered under the button
|
||||||
* [`x-extendedDiscriminator`](./redoc-vendor-extensions.md#x-extendeddiscriminator) - In Schemas, uses this to solve name-clash issues with the standard discriminator
|
|
||||||
* [`x-explicitMappingOnly`](./redoc-vendor-extensions.md#x-explicitmappingonly) - In Schemas, display a more descriptive property name in objects with additionalProperties when viewing the property list with an object
|
* [`x-explicitMappingOnly`](./redoc-vendor-extensions.md#x-explicitmappingonly) - In Schemas, display a more descriptive property name in objects with additionalProperties when viewing the property list with an object
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,21 @@ seo:
|
||||||
# How to use the Redocly CLI
|
# How to use the Redocly CLI
|
||||||
|
|
||||||
With Redocly CLI, you can bundle your OpenAPI definition and API documentation
|
With Redocly CLI, you can bundle your OpenAPI definition and API documentation
|
||||||
(made with Redoc) into a zero-dependency HTML file and render it locally.
|
(made with Redoc) into an HTML file and render it locally.
|
||||||
|
|
||||||
## Step 1 - Install Redocly CLI
|
## Step 1 - Install Redocly CLI
|
||||||
|
|
||||||
First, you need to install the `@redocly/cli` package.
|
First, you need to install the `@redocly/cli` package.
|
||||||
|
|
||||||
You can install it [globally](/docs/cli/installation.md#install-globally) using npm or Yarn.
|
You can install it [globally](../../cli/installation.md#install-globally) using npm or Yarn.
|
||||||
|
|
||||||
Or you can install it during [runtime](/docs/cli/installation.md#use-npx-at-runtime) using npx or Docker.
|
Or you can install it during [runtime](../../cli/installation.md#use-npx-at-runtime) using npx or Docker.
|
||||||
|
|
||||||
## Step 2 - Build the HTML file
|
## Step 2 - Build the HTML file
|
||||||
|
|
||||||
The Redocly CLI `build-docs` command builds Redoc into a zero-dependency HTML file.
|
The Redocly CLI `build-docs` command builds Redoc into an HTML file.
|
||||||
|
|
||||||
To build a zero-dependency HTML file using Redocly CLI, enter the following command,
|
To build an HTML file using Redocly CLI, enter the following command,
|
||||||
replacing `apis/openapi.yaml` with your API definition file's name and path:
|
replacing `apis/openapi.yaml` with your API definition file's name and path:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -126,7 +126,7 @@ The main example shows using the CDN:
|
||||||
|
|
||||||
If you would instead prefer to host the depdencies yourself, first install `redoc` using `npm`:
|
If you would instead prefer to host the depdencies yourself, first install `redoc` using `npm`:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
npm install redoc
|
npm install redoc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ The following options are supported:
|
||||||
You need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions:
|
You need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions:
|
||||||
|
|
||||||
- OpenAPI 3.0
|
- OpenAPI 3.0
|
||||||
- [Rebilly Users OpenAPI Definition](https://raw.githubusercontent.com/Rebilly/api-definitions/main/openapi/users.yaml)
|
- [Museum Example API](https://github.com/Redocly/museum-openapi-example/blob/main/openapi.yaml)
|
||||||
- [Swagger Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json)
|
- [Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json)
|
||||||
- OpenAPI 2.0
|
- OpenAPI 2.0
|
||||||
- [Thingful OpenAPI Definition](https://raw.githubusercontent.com/thingful/openapi-spec/master/spec/swagger.yaml)
|
- [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)
|
- [Fitbit Plus OpenAPI Definition](https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml)
|
||||||
|
@ -104,7 +104,7 @@ npm install -g http-server
|
||||||
Then, `cd` into your project directory and run the following command:
|
Then, `cd` into your project directory and run the following command:
|
||||||
|
|
||||||
```node
|
```node
|
||||||
http - server;
|
http-server
|
||||||
```
|
```
|
||||||
|
|
||||||
The output after entering the command provides the local URL where the preview can be accessed.
|
The output after entering the command provides the local URL where the preview can be accessed.
|
||||||
|
|
|
@ -28,7 +28,7 @@ Redoc is provided as a CLI tool (also distributed as a Docker image), HTML tag,
|
||||||
|
|
||||||
If you have Node installed, quickly generate documentation using `npx`:
|
If you have Node installed, quickly generate documentation using `npx`:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
npx @redocly/cli build-docs openapi.yaml
|
npx @redocly/cli build-docs openapi.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ theme:
|
||||||
openapi:
|
openapi:
|
||||||
disableSearch: true
|
disableSearch: true
|
||||||
expandResponses: 200,202
|
expandResponses: 200,202
|
||||||
jsonSampleExpandLevel: 1
|
jsonSamplesExpandLevel: 1
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
sidebar:
|
sidebar:
|
||||||
|
@ -84,7 +84,7 @@ theme:
|
||||||
|
|
||||||
Redocly CLI detects a file named `redocly.yaml` in the same directory as you run the command and uses it. See the documentation with a command like this:
|
Redocly CLI detects a file named `redocly.yaml` in the same directory as you run the command and uses it. See the documentation with a command like this:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
redocly preview-docs openapi.yaml
|
redocly preview-docs openapi.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ replace the `spec-url` attribute with the URL or local file address to your defi
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
{% admonition type="attention" name="Redoc requires an HTTP server to run locally" %}
|
{% admonition type="info" name="Redoc requires an HTTP server to run locally" %}
|
||||||
Loading local OpenAPI definitions is impossible without running a web server because of issues with
|
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
|
[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and
|
||||||
other security reasons. Refer to [Running Redoc locally](./deployment/intro.md#how-to-run-redoc-locally) for more information.
|
other security reasons. Refer to [Running Redoc locally](./deployment/intro.md#how-to-run-redoc-locally) for more information.
|
||||||
|
|
|
@ -10,9 +10,6 @@ You can use the following [vendor extensions](https://redocly.com/docs/openapi-v
|
||||||
- [Tag Group Object](#tag-group-object)
|
- [Tag Group Object](#tag-group-object)
|
||||||
- [Fixed fields](#fixed-fields)
|
- [Fixed fields](#fixed-fields)
|
||||||
- [x-tagGroups example](#x-taggroups-example)
|
- [x-tagGroups example](#x-taggroups-example)
|
||||||
- [x-ignoredHeaderParameters](#x-ignoredheaderparameters)
|
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-1)
|
|
||||||
- [x-ignoredHeaderParameters example](#x-ignoredheaderparameters-example)
|
|
||||||
- [Info Object](#info-object)
|
- [Info Object](#info-object)
|
||||||
- [x-logo](#x-logo)
|
- [x-logo](#x-logo)
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-2)
|
- [How to use with Redoc](#how-to-use-with-redoc-2)
|
||||||
|
@ -39,42 +36,43 @@ You can use the following [vendor extensions](https://redocly.com/docs/openapi-v
|
||||||
- [Schema Object](#schema-object)
|
- [Schema Object](#schema-object)
|
||||||
- [x-nullable](#x-nullable)
|
- [x-nullable](#x-nullable)
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-7)
|
- [How to use with Redoc](#how-to-use-with-redoc-7)
|
||||||
- [x-extendedDiscriminator](#x-extendeddiscriminator)
|
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-8)
|
|
||||||
- [x-extendedDiscriminator example](#x-extendeddiscriminator-example)
|
|
||||||
- [x-additionalPropertiesName](#x-additionalpropertiesname)
|
- [x-additionalPropertiesName](#x-additionalpropertiesname)
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-9)
|
- [How to use with Redoc](#how-to-use-with-redoc-9)
|
||||||
- [x-additionalPropertiesName example](#x-additionalpropertiesname-example)
|
- [x-additionalPropertiesName example](#x-additionalpropertiesname-example)
|
||||||
- [x-explicitMappingOnly](#x-explicitmappingonly)
|
- [x-explicitMappingOnly](#x-explicitmappingonly)
|
||||||
- [How to use with Redoc](#how-to-use-with-redoc-10)
|
- [How to use with Redoc](#how-to-use-with-redoc-10)
|
||||||
- [x-explicitMappingOnly example](#x-explicitmappingonly-example)
|
- [x-explicitMappingOnly example](#x-explicitmappingonly-example)
|
||||||
|
- [x-enumDescriptions](#x-enumdescriptions)
|
||||||
|
- [How to use with Redoc](#how-to-use-with-redoc-11)
|
||||||
|
- [x-enumDescriptions example](#x-enumdescriptions-example)
|
||||||
|
|
||||||
### Swagger Object
|
## Swagger Object
|
||||||
Extends the OpenAPI root [OpenAPI Object](https://redocly.com/docs/openapi-visual-reference/openapi/)
|
Extends the OpenAPI root [OpenAPI Object](https://redocly.com/docs/openapi-visual-reference/openapi)
|
||||||
|
|
||||||
#### x-servers
|
### x-servers
|
||||||
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverObject). Currently doesn't support templates.
|
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#server-object). Currently doesn't support templates.
|
||||||
|
|
||||||
#### x-tagGroups
|
### x-tagGroups
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :-----------: | :---------- |
|
| :------------- | :-----------: | :---------- |
|
||||||
| x-tagGroups | [ [Tag Group Object](#tag-group-object) ] | A list of tag groups |
|
| x-tagGroups | [ [Tag Group Object](#tag-group-object) ] | A list of tag groups |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
`x-tagGroups` is used to group tags in the side menu.
|
`x-tagGroups` is used to group tags in the side menu.
|
||||||
Before you use `x-tagGroups`, make sure you **add all tags to a group**, since a tag that is not in a group, **is not displayed** at all!
|
Before you use `x-tagGroups`, make sure you **add all tags to a group**, since a tag that is not in a group, **is not displayed** at all!
|
||||||
|
|
||||||
<a name="tagGroupObject"></a>
|
<a name="tagGroupObject"></a>
|
||||||
|
|
||||||
#### Tag Group Object
|
#### Tag Group Object
|
||||||
Information about tags group
|
Information about tags group
|
||||||
###### Fixed fields
|
##### Fixed fields
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :---------- | :--------: | :---------- |
|
| :---------- | :--------: | :---------- |
|
||||||
| name | string | The group name |
|
| name | string | The group name |
|
||||||
| tags | [ string ] | List of tags to include in this group
|
| tags | [ string ] | List of tags to include in this group |
|
||||||
|
|
||||||
###### x-tagGroups example
|
#### x-tagGroups example
|
||||||
json
|
json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -104,54 +102,33 @@ x-tagGroups:
|
||||||
- Secondary Stats
|
- Secondary Stats
|
||||||
```
|
```
|
||||||
|
|
||||||
#### x-ignoredHeaderParameters
|
## Info Object
|
||||||
|
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
|
||||||
| :-------------------------- | :-----------: | :---------- |
|
|
||||||
| x-ignoredHeaderParameters | [ string ] | A list of ignored headers |
|
|
||||||
|
|
||||||
|
|
||||||
###### How to use with Redoc
|
|
||||||
Use `x-ignoredHeaderParameters` to specify header parameter names which are ignored by Redoc.
|
|
||||||
|
|
||||||
###### x-ignoredHeaderParameters example
|
|
||||||
```yaml
|
|
||||||
swagger: '2.0'
|
|
||||||
info:
|
|
||||||
...
|
|
||||||
tags: [...]
|
|
||||||
x-ignoredHeaderParameters:
|
|
||||||
- Accept
|
|
||||||
- User-Agent
|
|
||||||
- X-Test-Header
|
|
||||||
```
|
|
||||||
|
|
||||||
### Info Object
|
|
||||||
Extends the OpenAPI [Info Object](https://redocly.com/docs/openapi-visual-reference/info/)
|
Extends the OpenAPI [Info Object](https://redocly.com/docs/openapi-visual-reference/info/)
|
||||||
#### x-logo
|
|
||||||
|
### x-logo
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :-----------: | :---------- |
|
| :------------- | :-----------: | :---------- |
|
||||||
| x-logo | [Logo Object](#logo-object) | The information about API logo |
|
| x-logo | [Logo Object](#logo-object) | The information about API logo |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
`x-logo` is used to specify API logo. The corresponding image is displayed just above the side-menu.
|
`x-logo` is used to specify API logo. The corresponding image is displayed just above the side-menu.
|
||||||
|
|
||||||
<a name="logoObject"></a>
|
<a name="logoObject"></a>
|
||||||
|
|
||||||
#### Logo Object
|
#### Logo Object
|
||||||
The information about API logo
|
The information about API logo
|
||||||
|
|
||||||
###### Fixed fields
|
#### Fixed fields
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :-------------- | :------: | :---------- |
|
| :-------------- | :------: | :---------- |
|
||||||
| url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location
|
| url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location |
|
||||||
| backgroundColor | string | background color to be used. MUST be RGB color in [hexadecimal format] (https://en.wikipedia.org/wiki/Web_colors#Hex_triplet)
|
| backgroundColor | string | background color to be used. MUST be RGB color in [hexadecimal format] (https://en.wikipedia.org/wiki/Web_colors#Hex_triplet) |
|
||||||
| altText | string | Text to use for alt tag on the logo. Defaults to 'logo' if nothing is provided.
|
| altText | string | Text to use for alt tag on the logo. Defaults to 'logo' if nothing is provided. |
|
||||||
| href | string | The URL pointing to the contact page. Default to 'info.contact.url' field of the OAS.
|
| href | string | The URL pointing to the contact page. Default to 'info.contact.url' field of the OAS. |
|
||||||
|
|
||||||
|
|
||||||
###### x-logo example
|
#### x-logo example
|
||||||
json
|
json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -177,19 +154,19 @@ info:
|
||||||
altText: "Petstore logo"
|
altText: "Petstore logo"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tag Object
|
## Tag Object
|
||||||
Extends the OpenAPI [Tag Object](https://redocly.com/docs/openapi-visual-reference/tags/)
|
Extends the OpenAPI [Tag Object](https://redocly.com/docs/openapi-visual-reference/tags/)
|
||||||
|
|
||||||
#### x-traitTag
|
### x-traitTag
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) |
|
| x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
Tags that have `x-traitTag` set to `true` are listed in the side-menu but don't have any subitems (operations). It also renders the `description` tag.
|
Tags that have `x-traitTag` set to `true` are listed in the side-menu but don't have any subitems (operations). It also renders the `description` tag.
|
||||||
This is useful for handling out common things like Pagination, Rate-Limits, etc.
|
This is useful for handling out common things like Pagination, Rate-Limits, etc.
|
||||||
|
|
||||||
###### x-traitTag example
|
#### x-traitTag example
|
||||||
json
|
json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -205,28 +182,29 @@ description: Pagination description (can use markdown syntax)
|
||||||
x-traitTag: true
|
x-traitTag: true
|
||||||
```
|
```
|
||||||
|
|
||||||
#### x-displayName
|
### x-displayName
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-displayName | string | Defines the text that is used for this tag in the menu and in section headings |
|
| x-displayName | string | Defines the text that is used for this tag in the menu and in section headings |
|
||||||
|
|
||||||
### Operation Object vendor extensions
|
## Operation Object vendor extensions
|
||||||
Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-reference/operation/)
|
Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-reference/operation/)
|
||||||
|
|
||||||
#### x-codeSamples
|
### x-codeSamples
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-codeSamples | [ [Code Sample Object](#code-sample-object) ] | A list of code samples associated with operation |
|
| x-codeSamples | [ [Code Sample Object](#code-sample-object) ] | A list of code samples associated with operation |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
`x-codeSamples` are rendered on the right panel in Redoc.
|
`x-codeSamples` are rendered on the right panel in Redoc.
|
||||||
|
|
||||||
<a name="codeSampleObject"></a>
|
<a name="codeSampleObject"></a>
|
||||||
#### Code Sample Object
|
|
||||||
|
### Code Sample Object
|
||||||
Operation code sample
|
Operation code sample
|
||||||
|
|
||||||
###### Fixed fields
|
#### Fixed fields
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :---------- | :------: | :----------- |
|
| :---------- | :------: | :----------- |
|
||||||
| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) |
|
| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) |
|
||||||
|
@ -234,7 +212,7 @@ Operation code sample
|
||||||
| source | string | Code sample source code |
|
| source | string | Code sample source code |
|
||||||
|
|
||||||
|
|
||||||
###### Code Sample Object example
|
#### Code Sample Object example
|
||||||
json
|
json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -248,94 +226,46 @@ lang: JavaScript
|
||||||
source: console.log('Hello World');
|
source: console.log('Hello World');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Parameter Object
|
### x-badges
|
||||||
|
| Field Name | Type | Description |
|
||||||
|
| :------------- | :------: | :---------- |
|
||||||
|
| x-badges | [[Badge Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-badges#badge-object)] | A list of badges associated with the operation |
|
||||||
|
|
||||||
|
## Parameter Object
|
||||||
Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/)
|
Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/)
|
||||||
|
|
||||||
#### x-examples
|
### x-examples
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-examples | [Example Object](https://redocly.com/docs/openapi-visual-reference/example/) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` |
|
| x-examples | [Example Object](https://redocly.com/docs/openapi-visual-reference/example/) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
`x-examples` are rendered in the JSON tab on the right panel in Redoc.
|
`x-examples` are rendered in the JSON tab on the right panel in Redoc.
|
||||||
|
|
||||||
### Response Object vendor extensions
|
## Response Object vendor extensions
|
||||||
Extends the OpenAPI [Response Object](https://redocly.com/docs/openapi-visual-reference/response/).
|
Extends the OpenAPI [Response Object](https://redocly.com/docs/openapi-visual-reference/response/).
|
||||||
|
|
||||||
#### x-summary
|
### x-summary
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-summary | string | a short summary of the response |
|
| x-summary | string | a short summary of the response |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
If specified, you can use `x-summary` as the response button text, with description rendered under the button.
|
If specified, you can use `x-summary` as the response button text, with description rendered under the button.
|
||||||
|
|
||||||
### Schema Object
|
## Schema Object
|
||||||
Extends the OpenAPI [Schema Object](https://redocly.com/docs/openapi-visual-reference/schemas/)
|
Extends the OpenAPI [Schema Object](https://redocly.com/docs/openapi-visual-reference/schemas/)
|
||||||
|
|
||||||
#### x-nullable
|
### x-nullable
|
||||||
| Field Name | Type | Description |
|
| Field Name | Type | Description |
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-nullable | boolean | marks schema as a nullable |
|
| x-nullable | boolean | marks schema as a nullable |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
Schemas marked as `x-nullable` are marked in Redoc with the label Nullable.
|
Schemas marked as `x-nullable` are marked in Redoc with the label Nullable.
|
||||||
|
|
||||||
#### x-extendedDiscriminator
|
### x-additionalPropertiesName
|
||||||
**ATTENTION**: This is a Redoc-specific vendor extension, and is not supported by other tools.
|
**Attention**: This is a Redoc-specific vendor extension, and is not supported by other tools.
|
||||||
|
|
||||||
| Field Name | Type | Description |
|
|
||||||
| :------------- | :------: | :---------- |
|
|
||||||
| x-extendedDiscriminator | string | specifies extended discriminator |
|
|
||||||
|
|
||||||
###### How to use with Redoc
|
|
||||||
Redoc uses this vendor extension to solve name-clash issues with the standard `discriminator`.
|
|
||||||
Value of this field specifies the field which is used as an extended discriminator.
|
|
||||||
Redoc displays definition with selectpicker using which user can select value of the `x-extendedDiscriminator`-marked field.
|
|
||||||
Redoc displays the definition derived from the current (using `allOf`) and has `enum` with only one value which is the same as the selected value of the field specified as `x-extendedDiscriminator`.
|
|
||||||
|
|
||||||
###### x-extendedDiscriminator example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
|
|
||||||
Payment:
|
|
||||||
x-extendedDiscriminator: type
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- type
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
CashPayment:
|
|
||||||
allOf:
|
|
||||||
- $ref: "#/definitions/Payment"
|
|
||||||
- properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- cash
|
|
||||||
currency:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
PayPalPayment:
|
|
||||||
allOf:
|
|
||||||
- $ref: "#/definitions/Payment"
|
|
||||||
- properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- paypal
|
|
||||||
userEmail:
|
|
||||||
type: string
|
|
||||||
```
|
|
||||||
|
|
||||||
In the example above, the names of definitions (`PayPalPayment`) are named differently than names in the payload (`paypal`) which is not supported by default `discriminator`.
|
|
||||||
|
|
||||||
#### x-additionalPropertiesName
|
|
||||||
**ATTENTION**: This is a Redoc-specific vendor extension, and is not supported by other tools.
|
|
||||||
|
|
||||||
Extends the `additionalProperties` property of the schema object.
|
Extends the `additionalProperties` property of the schema object.
|
||||||
|
|
||||||
|
@ -343,10 +273,10 @@ Extends the `additionalProperties` property of the schema object.
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-additionalPropertiesName | string | descriptive name of additional properties keys |
|
| x-additionalPropertiesName | string | descriptive name of additional properties keys |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
Redoc uses this extension to display a more descriptive property name in objects with `additionalProperties` when viewing the property list with an `object`.
|
Redoc uses this extension to display a more descriptive property name in objects with `additionalProperties` when viewing the property list with an `object`.
|
||||||
|
|
||||||
###### x-additionalPropertiesName example
|
#### x-additionalPropertiesName example
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
Player:
|
Player:
|
||||||
|
@ -362,8 +292,8 @@ Player:
|
||||||
type: string
|
type: string
|
||||||
```
|
```
|
||||||
|
|
||||||
#### x-explicitMappingOnly
|
### x-explicitMappingOnly
|
||||||
**ATTENTION**: This is Redoc-specific vendor extension, and is not supported by other tools.
|
**Attention**: This is Redoc-specific vendor extension, and is not supported by other tools.
|
||||||
|
|
||||||
Extends the `discriminator` property of the schema object.
|
Extends the `discriminator` property of the schema object.
|
||||||
|
|
||||||
|
@ -371,11 +301,11 @@ Extends the `discriminator` property of the schema object.
|
||||||
| :------------- | :------: | :---------- |
|
| :------------- | :------: | :---------- |
|
||||||
| x-explicitMappingOnly | boolean | limit the discriminator selectpicker to the explicit mappings only |
|
| x-explicitMappingOnly | boolean | limit the discriminator selectpicker to the explicit mappings only |
|
||||||
|
|
||||||
###### How to use with Redoc
|
#### How to use with Redoc
|
||||||
Redoc uses this extension to filter the `discriminator` mappings shown in the selectpicker.
|
Redoc uses this extension to filter the `discriminator` mappings shown in the selectpicker.
|
||||||
When set to `true`, the selectpicker lists only the explicitly defined mappings. When `false`, the default behavior is kept, in other words, explicit and implicit mappings are shown.
|
When set to `true`, the selectpicker lists only the explicitly defined mappings. When `false`, the default behavior is kept, in other words, explicit and implicit mappings are shown.
|
||||||
|
|
||||||
###### x-explicitMappingOnly example
|
#### x-explicitMappingOnly example
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -393,3 +323,31 @@ Pet:
|
||||||
```
|
```
|
||||||
|
|
||||||
Shows in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from the `Pet` class.
|
Shows in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from the `Pet` class.
|
||||||
|
|
||||||
|
### x-enumDescriptions
|
||||||
|
| Field Name | Type | Description |
|
||||||
|
| :------------- | :------: | :---------- |
|
||||||
|
| x-enumDescriptions | [[Enum Description Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-enum-descriptions#enum-description-object)] | A list of the enum values and descriptions to include in the documentation. |
|
||||||
|
|
||||||
|
#### How to use with Redoc
|
||||||
|
The enum (short for "enumeration") fields in OpenAPI allow you to restrict the value of a field to a list of allowed values. These values need to be short and machine-readable, but that can make them harder for humans to parse and work with.
|
||||||
|
|
||||||
|
Add x-enumDescriptions to your OpenAPI description to show a helpful table of enum options and an explanation of what each one means. This field supports Markdown.
|
||||||
|
|
||||||
|
#### x-enumDescriptions example
|
||||||
|
The following example shows a schema with two short-named options, and the x-enumDescriptions entry to list all enum entries and give additional context for each:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
TicketType:
|
||||||
|
description: Type of ticket being purchased. Use `general` for regular museum entry and `event` for tickets to special events.
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- event
|
||||||
|
- general
|
||||||
|
x-enumDescriptions:
|
||||||
|
event: Event Tickets _(timed entry)_
|
||||||
|
general: General Admission
|
||||||
|
example: event
|
||||||
|
```
|
||||||
|
|
|
@ -4,7 +4,7 @@ You can inject the Security Definitions widget anywhere in your specification `d
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
...
|
...
|
||||||
# Authorization
|
## Authorization
|
||||||
|
|
||||||
Some description
|
Some description
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ Some description
|
||||||
The inject instruction is wrapped in an HTML comment,
|
The inject instruction is wrapped in an HTML comment,
|
||||||
so it is **visible only in Redoc** and not visible, for instance, in the SwaggerUI.
|
so it is **visible only in Redoc** and not visible, for instance, in the SwaggerUI.
|
||||||
|
|
||||||
# Default behavior
|
## Default behavior
|
||||||
|
|
||||||
If the injection tag is not found in the description, it is appended to the end
|
If the injection tag is not found in the description, it is appended to the end
|
||||||
of description under the `Authentication` header.
|
of description under the `Authentication` header.
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('Menu', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync active menu items while scroll', () => {
|
it('should sync active menu items while scroll', () => {
|
||||||
cy.contains('h1', 'Introduction')
|
cy.contains('h2', 'Introduction')
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.get('[role=menuitem] > label.active')
|
.get('[role=menuitem] > label.active')
|
||||||
.should('have.text', 'Introduction');
|
.should('have.text', 'Introduction');
|
||||||
|
@ -35,7 +35,7 @@ describe('Menu', () => {
|
||||||
|
|
||||||
cy.contains('h1', 'Swagger Petstore').scrollIntoView().wait(100);
|
cy.contains('h1', 'Swagger Petstore').scrollIntoView().wait(100);
|
||||||
|
|
||||||
cy.contains('h1', 'Introduction')
|
cy.contains('h2', 'Introduction')
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.wait(100)
|
.wait(100)
|
||||||
.get('[role=menuitem] > label.active')
|
.get('[role=menuitem] > label.active')
|
||||||
|
@ -52,6 +52,31 @@ describe('Menu', () => {
|
||||||
cy.location('hash').should('equal', '#schema/Cat');
|
cy.location('hash').should('equal', '#schema/Cat');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should contains badge schema from x-badges', () => {
|
||||||
|
cy.contains('h2', 'Add a new pet to the store').scrollIntoView();
|
||||||
|
|
||||||
|
cy.contains('h2 > span', 'Beta')
|
||||||
|
.scrollIntoView()
|
||||||
|
.wait(100)
|
||||||
|
.get('[role=menuitem] > label.active')
|
||||||
|
.children('span[type="badge"]')
|
||||||
|
.should('have.text', 'Beta');
|
||||||
|
|
||||||
|
cy.contains('h2 > span', 'Alpha')
|
||||||
|
.scrollIntoView()
|
||||||
|
.wait(100)
|
||||||
|
.get('[role=menuitem] > label.active')
|
||||||
|
.children('span[type="badge"]')
|
||||||
|
.should('have.text', 'Alpha');
|
||||||
|
|
||||||
|
cy.contains('h2 > span', 'Gamma')
|
||||||
|
.scrollIntoView()
|
||||||
|
.wait(100)
|
||||||
|
.get('[role=menuitem] > label.active')
|
||||||
|
.children('span[type="badge"]')
|
||||||
|
.should('have.text', 'Gamma');
|
||||||
|
});
|
||||||
|
|
||||||
it('should contains Cat schema in Pet using x-tags', () => {
|
it('should contains Cat schema in Pet using x-tags', () => {
|
||||||
cy.contains('[role=menuitem] > label.-depth1', 'pet').click({ force: true });
|
cy.contains('[role=menuitem] > label.-depth1', 'pet').click({ force: true });
|
||||||
cy.location('hash').should('equal', '#tag/pet');
|
cy.location('hash').should('equal', '#tag/pet');
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
module.exports = function(config) {
|
|
||||||
const testWebpackConfig = require('./build/webpack.test.js');
|
|
||||||
const travis = process.env.TRAVIS;
|
|
||||||
|
|
||||||
config.set({
|
|
||||||
frameworks: ['jasmine', 'sinon', 'should'],
|
|
||||||
preprocessors: {
|
|
||||||
'./tests/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'],
|
|
||||||
},
|
|
||||||
|
|
||||||
coverageReporter: {
|
|
||||||
type: 'in-memory',
|
|
||||||
},
|
|
||||||
|
|
||||||
remapCoverageReporter: {
|
|
||||||
'text-summary': null,
|
|
||||||
'text-lcov': './coverage/lcov.info',
|
|
||||||
html: './coverage/html',
|
|
||||||
},
|
|
||||||
webpack: testWebpackConfig,
|
|
||||||
webpackMiddleware: {
|
|
||||||
stats: 'errors-only',
|
|
||||||
state: true,
|
|
||||||
},
|
|
||||||
client: {
|
|
||||||
chai: {
|
|
||||||
truncateThreshold: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
files: [
|
|
||||||
{ pattern: './tests/spec-bundle.js', watched: false },
|
|
||||||
{ pattern: 'tests/schemas/**/*.json', included: false },
|
|
||||||
{ pattern: 'tests/schemas/**/*.yml', included: false },
|
|
||||||
{ pattern: 'lib/**/*.html', included: false },
|
|
||||||
],
|
|
||||||
|
|
||||||
proxies: {
|
|
||||||
'/tests/schemas': '/base/tests/schemas',
|
|
||||||
'/lib/': '/base/lib/',
|
|
||||||
'/node_modules/': '/base/node_modules/',
|
|
||||||
},
|
|
||||||
colors: true,
|
|
||||||
singleRun: true,
|
|
||||||
reporters: travis
|
|
||||||
? ['mocha', 'coverage', 'remap-coverage', 'coveralls']
|
|
||||||
: ['mocha', 'coverage', 'remap-coverage'],
|
|
||||||
|
|
||||||
browsers: ['ChromeHeadlessNoSandbox'],
|
|
||||||
customLaunchers: {
|
|
||||||
ChromeHeadlessNoSandbox: {
|
|
||||||
base: 'ChromeHeadless',
|
|
||||||
flags: ['--no-sandbox']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
browserNoActivityTimeout: 60000,
|
|
||||||
});
|
|
||||||
};
|
|
12929
package-lock.json
generated
12929
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
74
package.json
74
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.1.3",
|
"version": "2.5.0",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -55,20 +55,20 @@
|
||||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||||
"publish-cdn": "scripts/publish-cdn.sh",
|
"publish-cdn": "scripts/publish-cdn.sh",
|
||||||
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
||||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0;Python-2.0' --summary",
|
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0;CC0-1.0;Python-2.0 ' --summary",
|
||||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"pre-commit": "pretty-quick --staged && npm run lint"
|
"pre-commit": "pretty-quick --staged && npm run lint"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
|
||||||
"@cypress/webpack-preprocessor": "^5.17.1",
|
"@cypress/webpack-preprocessor": "^5.17.1",
|
||||||
"@hot-loader/react-dom": "^17.0.2",
|
"@size-limit/file": "^11.1.4",
|
||||||
"@size-limit/preset-app": "^8.2.6",
|
|
||||||
"@types/chai": "^4.2.18",
|
"@types/chai": "^4.2.18",
|
||||||
"@types/dompurify": "^2.2.2",
|
"@types/dompurify": "^2.2.2",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
"@types/enzyme-to-json": "^1.5.3",
|
"@types/enzyme-to-json": "^1.5.3",
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^29.5.6",
|
||||||
"@types/json-pointer": "^1.0.30",
|
"@types/json-pointer": "^1.0.30",
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/mark.js": "^8.11.5",
|
"@types/mark.js": "^8.11.5",
|
||||||
|
@ -76,57 +76,55 @@
|
||||||
"@types/node": "^15.6.1",
|
"@types/node": "^15.6.1",
|
||||||
"@types/prismjs": "^1.16.5",
|
"@types/prismjs": "^1.16.5",
|
||||||
"@types/prop-types": "^15.7.3",
|
"@types/prop-types": "^15.7.3",
|
||||||
"@types/react": "^17.0.8",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dom": "^17.0.5",
|
"@types/react-dom": "^18.0.0",
|
||||||
"@types/styled-components": "^5.1.1",
|
"@types/styled-components": "^5.1.1",
|
||||||
"@types/tapable": "^2.2.2",
|
"@types/tapable": "^2.2.2",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/webpack-env": "^1.18.0",
|
"@types/webpack-env": "^1.18.0",
|
||||||
"@types/yargs": "^17.0.0",
|
"@types/yargs": "^17.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.26.0",
|
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||||
"@typescript-eslint/parser": "^4.26.0",
|
"@typescript-eslint/parser": "^5.55.0",
|
||||||
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
|
|
||||||
"beautify-benchmark": "^0.2.4",
|
"beautify-benchmark": "^0.2.4",
|
||||||
"conventional-changelog-cli": "^3.0.0",
|
"conventional-changelog-cli": "^3.0.0",
|
||||||
"copy-webpack-plugin": "^9.0.0",
|
"copy-webpack-plugin": "^9.0.0",
|
||||||
"core-js": "^3.13.1",
|
"core-js": "^3.13.1",
|
||||||
"coveralls": "^3.1.1",
|
|
||||||
"css-loader": "^5.2.6",
|
"css-loader": "^5.2.6",
|
||||||
"cypress": "^13.1.0",
|
"cypress": "^13.8.1",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-to-json": "^3.6.2",
|
"enzyme-to-json": "^3.6.2",
|
||||||
"esbuild-loader": "^3.0.1",
|
"esbuild-loader": "^4.3.0",
|
||||||
"eslint": "^7.27.0",
|
"eslint": "^7.27.0",
|
||||||
"eslint-plugin-import": "^2.23.4",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
"eslint-plugin-react": "^7.25.1",
|
"eslint-plugin-react": "^7.34.2",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"fork-ts-checker-webpack-plugin": "^6.2.10",
|
"fork-ts-checker-webpack-plugin": "^6.2.10",
|
||||||
"html-webpack-plugin": "^5.3.1",
|
"html-webpack-plugin": "^5.3.1",
|
||||||
"husky": "^7.0.0",
|
"husky": "^7.0.0",
|
||||||
"jest": "^27.0.3",
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"lodash.noop": "^3.0.1",
|
"lodash.noop": "^3.0.1",
|
||||||
"mobx": "^6.3.2",
|
"mobx": "^6.10.2",
|
||||||
"outdent": "^0.8.0",
|
"outdent": "^0.8.0",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
"pretty-quick": "^3.0.0",
|
"pretty-quick": "^3.0.0",
|
||||||
"raf": "^3.4.1",
|
"raf": "^3.4.1",
|
||||||
"react": "^17.0.2",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.0.0",
|
||||||
"react-hot-loader": "^4.13.0",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"size-limit": "^8.2.6",
|
"size-limit": "^11.1.4",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"ts-jest": "^27.0.2",
|
"ts-jest": "^29.1.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.9.0",
|
||||||
"unfetch": "^4.2.0",
|
"unfetch": "^4.2.0",
|
||||||
"url": "^0.11.1",
|
"url": "^0.11.1",
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.94.0",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"webpack-node-externals": "^3.0.0",
|
"webpack-node-externals": "^3.0.0",
|
||||||
|
@ -135,31 +133,31 @@
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"core-js": "^3.1.4",
|
"core-js": "^3.1.4",
|
||||||
"mobx": "^6.0.4",
|
"mobx": "^6.0.4",
|
||||||
"react": "^16.8.4 || ^17.0.0 || ^18.0.0",
|
"react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0",
|
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
"styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5"
|
"styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@redocly/openapi-core": "^1.0.0-rc.2",
|
"@redocly/openapi-core": "^1.4.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.2",
|
||||||
"decko": "^1.2.0",
|
"decko": "^1.2.0",
|
||||||
"dompurify": "^2.2.8",
|
"dompurify": "^3.2.4",
|
||||||
"eventemitter3": "^4.0.7",
|
"eventemitter3": "^5.0.1",
|
||||||
"json-pointer": "^0.6.2",
|
"json-pointer": "^0.6.2",
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"marked": "^4.0.15",
|
"marked": "^4.3.0",
|
||||||
"mobx-react": "^7.2.0",
|
"mobx-react": "^9.1.1",
|
||||||
"openapi-sampler": "^1.3.1",
|
"openapi-sampler": "^1.5.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"perfect-scrollbar": "^1.5.5",
|
"perfect-scrollbar": "^1.5.5",
|
||||||
"polished": "^4.1.3",
|
"polished": "^4.2.2",
|
||||||
"prismjs": "^1.27.0",
|
"prismjs": "^1.29.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.8.1",
|
||||||
"react-tabs": "^4.3.0",
|
"react-tabs": "^6.0.2",
|
||||||
"slugify": "~1.4.7",
|
"slugify": "~1.4.7",
|
||||||
"stickyfill": "^1.1.1",
|
"stickyfill": "^1.1.1",
|
||||||
"swagger2openapi": "^7.0.6",
|
"swagger2openapi": "^7.0.8",
|
||||||
"url-template": "^2.0.8"
|
"url-template": "^2.0.8"
|
||||||
},
|
},
|
||||||
"size-limit": [
|
"size-limit": [
|
||||||
|
|
|
@ -49,7 +49,7 @@ const Gap = styled.div`
|
||||||
bottom: -20px;
|
bottom: -20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface TooltipProps {
|
export interface TooltipProps extends React.PropsWithChildren<any> {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import { transparentize } from 'polished';
|
import { transparentize } from 'polished';
|
||||||
|
|
||||||
import styled, { extensionsHook, css } from '../styled-components';
|
import styled, { css, extensionsHook } from '../styled-components';
|
||||||
import { PropertyNameCell } from './fields-layout';
|
import { PropertyNameCell } from './fields-layout';
|
||||||
|
import { deprecatedCss } from './mixins';
|
||||||
import { ShelfIcon } from './shelfs';
|
import { ShelfIcon } from './shelfs';
|
||||||
|
|
||||||
export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
||||||
|
&.deprecated {
|
||||||
|
span.property-name {
|
||||||
|
${deprecatedCss}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -90,9 +97,11 @@ export const RecursiveLabel = styled(FieldLabel)`
|
||||||
|
|
||||||
export const PatternLabel = styled(FieldLabel)`
|
export const PatternLabel = styled(FieldLabel)`
|
||||||
color: #0e7c86;
|
color: #0e7c86;
|
||||||
|
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||||
|
font-size: 12px;
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
font-weight: bold;
|
content: ' ';
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,9 @@ export interface PerfectScrollbarProps {
|
||||||
updateFn?: (fn) => void;
|
updateFn?: (fn) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerfectScrollbar extends React.Component<PerfectScrollbarProps> {
|
export class PerfectScrollbar extends React.Component<
|
||||||
|
React.PropsWithChildren<PerfectScrollbarProps>
|
||||||
|
> {
|
||||||
private _container: HTMLElement;
|
private _container: HTMLElement;
|
||||||
private inst: PerfectScrollbarType;
|
private inst: PerfectScrollbarType;
|
||||||
|
|
||||||
|
|
|
@ -47,11 +47,11 @@ export const ShelfIcon = styled(IntShelfIcon)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Badge = styled.span<{ type: string }>`
|
export const Badge = styled.span<{ type: string; color?: string }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: ${props => props.theme.colors[props.type].main};
|
background-color: ${props => props.color || props.theme.colors[props.type].main};
|
||||||
color: ${props => props.theme.colors[props.type].contrastText};
|
color: ${props => props.theme.colors[props.type].contrastText};
|
||||||
font-size: ${props => props.theme.typography.code.fontSize};
|
font-size: ${props => props.theme.typography.code.fontSize};
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
|
@ -22,20 +22,13 @@ export interface ApiInfoProps {
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ApiInfo extends React.Component<ApiInfoProps> {
|
export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
handleDownloadClick = e => {
|
|
||||||
if (!e.target.href) {
|
|
||||||
e.target.href = this.props.store.spec.info.downloadLink;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { store } = this.props;
|
const { store } = this.props;
|
||||||
const { info, externalDocs } = store.spec;
|
const { info, externalDocs } = store.spec;
|
||||||
const hideDownloadButton = store.options.hideDownloadButton;
|
const hideDownloadButtons = store.options.hideDownloadButtons;
|
||||||
|
|
||||||
const downloadFilename = info.downloadFileName;
|
|
||||||
const downloadLink = info.downloadLink;
|
|
||||||
|
|
||||||
|
const downloadUrls = info.downloadUrls;
|
||||||
|
const downloadFileName = info.downloadFileName;
|
||||||
const license =
|
const license =
|
||||||
(info.license && (
|
(info.license && (
|
||||||
<InfoSpan>
|
<InfoSpan>
|
||||||
|
@ -83,17 +76,22 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
<ApiHeader>
|
<ApiHeader>
|
||||||
{info.title} {version}
|
{info.title} {version}
|
||||||
</ApiHeader>
|
</ApiHeader>
|
||||||
{!hideDownloadButton && (
|
{!hideDownloadButtons && (
|
||||||
<p>
|
<p>
|
||||||
{l('downloadSpecification')}:
|
{l('downloadSpecification')}:
|
||||||
|
{downloadUrls?.map(({ title, url }) => {
|
||||||
|
return (
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
download={downloadFilename || true}
|
download={downloadFileName || true}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={downloadLink}
|
href={url}
|
||||||
onClick={this.handleDownloadClick}
|
rel="noreferrer"
|
||||||
|
key={url}
|
||||||
>
|
>
|
||||||
{l('download')}
|
{title}
|
||||||
</DownloadButton>
|
</DownloadButton>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<StyledMarkdownBlock>
|
<StyledMarkdownBlock>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
||||||
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
import { H2, H3, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||||
import type { ContentItemModel } from '../../services';
|
import type { ContentItemModel } from '../../services';
|
||||||
import type { GroupModel, OperationModel } from '../../services/models';
|
import type { GroupModel, OperationModel } from '../../services/models';
|
||||||
import { Operation } from '../Operation/Operation';
|
import { Operation } from '../Operation/Operation';
|
||||||
|
@ -68,7 +68,7 @@ export class SectionItem extends React.Component<ContentItemProps> {
|
||||||
render() {
|
render() {
|
||||||
const { name, description, externalDocs, level } = this.props.item as GroupModel;
|
const { name, description, externalDocs, level } = this.props.item as GroupModel;
|
||||||
|
|
||||||
const Header = level === 2 ? H2 : H1;
|
const Header = level === 2 ? H3 : H2;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row>
|
<Row>
|
||||||
|
|
|
@ -18,14 +18,6 @@ export function ArrayItemDetails({ schema }: { schema: SchemaModel }) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.type === 'string' && schema.pattern) {
|
|
||||||
return (
|
|
||||||
<Wrapper>
|
|
||||||
[<Pattern schema={schema} />]
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
[ items
|
[ items
|
||||||
|
|
|
@ -5,17 +5,29 @@ import { l } from '../../services/Labels';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
|
import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
|
||||||
|
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||||
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
|
||||||
export interface EnumValuesProps {
|
export interface EnumValuesProps {
|
||||||
values: string[];
|
values?: string[] | { [name: string]: string };
|
||||||
isArrayType: boolean;
|
type: string | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnumValuesState {
|
export interface EnumValuesState {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DescriptionEnumsBlock = styled(StyledMarkdownBlock)`
|
||||||
|
table {
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesState> {
|
export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesState> {
|
||||||
|
constructor(props: EnumValuesProps) {
|
||||||
|
super(props);
|
||||||
|
this.toggle = this.toggle.bind(this);
|
||||||
|
}
|
||||||
state: EnumValuesState = {
|
state: EnumValuesState = {
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
};
|
};
|
||||||
|
@ -27,35 +39,79 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { values, isArrayType } = this.props;
|
const { values, type } = this.props;
|
||||||
const { collapsed } = this.state;
|
const { collapsed } = this.state;
|
||||||
|
const isDescriptionEnum = !Array.isArray(values);
|
||||||
|
const enums =
|
||||||
|
(Array.isArray(values) && values) ||
|
||||||
|
Object.entries(values || {}).map(([value, description]) => ({
|
||||||
|
value,
|
||||||
|
description,
|
||||||
|
}));
|
||||||
|
|
||||||
// TODO: provide context interface in more elegant way
|
// TODO: provide context interface in more elegant way
|
||||||
const { enumSkipQuotes, maxDisplayedEnumValues } = this.context as RedocRawOptions;
|
const { enumSkipQuotes, maxDisplayedEnumValues } = this.context as RedocRawOptions;
|
||||||
|
|
||||||
if (!values.length) {
|
if (!enums.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayedItems =
|
const displayedItems =
|
||||||
this.state.collapsed && maxDisplayedEnumValues
|
this.state.collapsed && maxDisplayedEnumValues
|
||||||
? values.slice(0, maxDisplayedEnumValues)
|
? enums.slice(0, maxDisplayedEnumValues)
|
||||||
: values;
|
: enums;
|
||||||
|
|
||||||
const showToggleButton = maxDisplayedEnumValues
|
const showToggleButton = maxDisplayedEnumValues ? enums.length > maxDisplayedEnumValues : false;
|
||||||
? values.length > maxDisplayedEnumValues
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const toggleButtonText = maxDisplayedEnumValues
|
const toggleButtonText = maxDisplayedEnumValues
|
||||||
? collapsed
|
? collapsed
|
||||||
? `… ${values.length - maxDisplayedEnumValues} more`
|
? `… ${enums.length - maxDisplayedEnumValues} more`
|
||||||
: 'Hide'
|
: 'Hide'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{isDescriptionEnum ? (
|
||||||
|
<>
|
||||||
|
<DescriptionEnumsBlock>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
<FieldLabel>
|
<FieldLabel>
|
||||||
{isArrayType ? l('enumArray') : ''}{' '}
|
{type === 'array' ? l('enumArray') : ''}{' '}
|
||||||
|
{enums.length === 1 ? l('enumSingleValue') : l('enum')}
|
||||||
|
</FieldLabel>{' '}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<strong>Description</strong>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{(displayedItems as { value: string; description: string }[]).map(
|
||||||
|
({ description, value }) => {
|
||||||
|
return (
|
||||||
|
<tr key={value}>
|
||||||
|
<td>{value}</td>
|
||||||
|
<td>
|
||||||
|
<Markdown source={description} compact inline />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</DescriptionEnumsBlock>
|
||||||
|
{showToggleButton ? (
|
||||||
|
<ToggleButton onClick={this.toggle}>{toggleButtonText}</ToggleButton>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FieldLabel>
|
||||||
|
{type === 'array' ? l('enumArray') : ''}{' '}
|
||||||
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
|
||||||
</FieldLabel>{' '}
|
</FieldLabel>{' '}
|
||||||
{displayedItems.map((value, idx) => {
|
{displayedItems.map((value, idx) => {
|
||||||
|
@ -67,14 +123,10 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{showToggleButton ? (
|
{showToggleButton ? (
|
||||||
<ToggleButton
|
<ToggleButton onClick={this.toggle}>{toggleButtonText}</ToggleButton>
|
||||||
onClick={() => {
|
|
||||||
this.toggle();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{toggleButtonText}
|
|
||||||
</ToggleButton>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import { Schema } from '../Schema/Schema';
|
||||||
|
|
||||||
import type { SchemaOptions } from '../Schema/Schema';
|
import type { SchemaOptions } from '../Schema/Schema';
|
||||||
import type { FieldModel } from '../../services/models';
|
import type { FieldModel } from '../../services/models';
|
||||||
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
|
import { RedocNormalizedOptions } from '../../services/RedocNormalizedOptions';
|
||||||
|
|
||||||
export interface FieldProps extends SchemaOptions {
|
export interface FieldProps extends SchemaOptions {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -27,12 +29,15 @@ export interface FieldProps extends SchemaOptions {
|
||||||
|
|
||||||
field: FieldModel;
|
field: FieldModel;
|
||||||
expandByDefault?: boolean;
|
expandByDefault?: boolean;
|
||||||
|
fieldParentsName?: string[];
|
||||||
renderDiscriminatorSwitch?: (opts: FieldProps) => JSX.Element;
|
renderDiscriminatorSwitch?: (opts: FieldProps) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Field extends React.Component<FieldProps> {
|
export class Field extends React.Component<FieldProps> {
|
||||||
|
static contextType = OptionsContext;
|
||||||
|
context: RedocNormalizedOptions;
|
||||||
|
|
||||||
toggle = () => {
|
toggle = () => {
|
||||||
if (this.props.field.expanded === undefined && this.props.expandByDefault) {
|
if (this.props.field.expanded === undefined && this.props.expandByDefault) {
|
||||||
this.props.field.collapse();
|
this.props.field.collapse();
|
||||||
|
@ -49,12 +54,12 @@ export class Field extends React.Component<FieldProps> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className = '', field, isLast, expandByDefault } = this.props;
|
const { hidePropertiesPrefix } = this.context;
|
||||||
|
const { className = '', field, isLast, expandByDefault, fieldParentsName = [] } = this.props;
|
||||||
const { name, deprecated, required, kind } = field;
|
const { name, deprecated, required, kind } = field;
|
||||||
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
|
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
|
||||||
|
|
||||||
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
|
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
|
||||||
|
|
||||||
const labels = (
|
const labels = (
|
||||||
<>
|
<>
|
||||||
{kind === 'additionalProperties' && <PropertyLabel>additional property</PropertyLabel>}
|
{kind === 'additionalProperties' && <PropertyLabel>additional property</PropertyLabel>}
|
||||||
|
@ -75,6 +80,10 @@ export class Field extends React.Component<FieldProps> {
|
||||||
onKeyPress={this.handleKeyPress}
|
onKeyPress={this.handleKeyPress}
|
||||||
aria-label={`expand ${name}`}
|
aria-label={`expand ${name}`}
|
||||||
>
|
>
|
||||||
|
{!hidePropertiesPrefix &&
|
||||||
|
fieldParentsName.map(
|
||||||
|
name => name + '.\u200B', // zero-width space, a special character is used for correct line breaking
|
||||||
|
)}
|
||||||
<span className="property-name">{name}</span>
|
<span className="property-name">{name}</span>
|
||||||
<ShelfIcon direction={expanded ? 'down' : 'right'} />
|
<ShelfIcon direction={expanded ? 'down' : 'right'} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -83,6 +92,10 @@ export class Field extends React.Component<FieldProps> {
|
||||||
) : (
|
) : (
|
||||||
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
|
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
|
||||||
<PropertyBullet />
|
<PropertyBullet />
|
||||||
|
{!hidePropertiesPrefix &&
|
||||||
|
fieldParentsName.map(
|
||||||
|
name => name + '.\u200B', // zero-width space, a special character is used for correct line breaking
|
||||||
|
)}
|
||||||
<span className="property-name">{name}</span>
|
<span className="property-name">{name}</span>
|
||||||
{labels}
|
{labels}
|
||||||
</PropertyNameCell>
|
</PropertyNameCell>
|
||||||
|
@ -102,6 +115,7 @@ export class Field extends React.Component<FieldProps> {
|
||||||
<InnerPropertiesWrap>
|
<InnerPropertiesWrap>
|
||||||
<Schema
|
<Schema
|
||||||
schema={field.schema}
|
schema={field.schema}
|
||||||
|
fieldParentsName={[...(fieldParentsName || []), field.name]}
|
||||||
skipReadOnly={this.props.skipReadOnly}
|
skipReadOnly={this.props.skipReadOnly}
|
||||||
skipWriteOnly={this.props.skipWriteOnly}
|
skipWriteOnly={this.props.skipWriteOnly}
|
||||||
showTitle={this.props.showTitle}
|
showTitle={this.props.showTitle}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
TypePrefix,
|
TypePrefix,
|
||||||
TypeTitle,
|
TypeTitle,
|
||||||
} from '../../common-elements/fields';
|
} from '../../common-elements/fields';
|
||||||
import { getSerializedValue, isObject } from '../../utils';
|
import { getSerializedValue, isArray, isObject } from '../../utils';
|
||||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { EnumValues } from './EnumValues';
|
import { EnumValues } from './EnumValues';
|
||||||
|
@ -30,7 +30,8 @@ export const FieldDetailsComponent = observer((props: FieldProps) => {
|
||||||
|
|
||||||
const { showExamples, field, renderDiscriminatorSwitch } = props;
|
const { showExamples, field, renderDiscriminatorSwitch } = props;
|
||||||
const { schema, description, deprecated, extensions, in: _in, const: _const } = field;
|
const { schema, description, deprecated, extensions, in: _in, const: _const } = field;
|
||||||
const isArrayType = schema.type === 'array';
|
const isArrayType =
|
||||||
|
schema.type === 'array' || (isArray(schema.type) && schema.type.includes('array'));
|
||||||
|
|
||||||
const rawDefault = enumSkipQuotes || _in === 'header'; // having quotes around header field default values is confusing and inappropriate
|
const rawDefault = enumSkipQuotes || _in === 'header'; // having quotes around header field default values is confusing and inappropriate
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ export const FieldDetailsComponent = observer((props: FieldProps) => {
|
||||||
)}
|
)}
|
||||||
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={defaultValue} />
|
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={defaultValue} />
|
||||||
{!renderDiscriminatorSwitch && (
|
{!renderDiscriminatorSwitch && (
|
||||||
<EnumValues isArrayType={isArrayType} values={schema.enum} />
|
<EnumValues type={schema.type} values={schema['x-enumDescriptions'] || schema.enum} />
|
||||||
)}{' '}
|
)}{' '}
|
||||||
{renderedExamples}
|
{renderedExamples}
|
||||||
<Extensions extensions={{ ...extensions, ...schema.extensions }} />
|
<Extensions extensions={{ ...extensions, ...schema.extensions }} />
|
||||||
|
|
|
@ -41,11 +41,12 @@ const Json = (props: JsonProps) => {
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
<PrismDiv
|
<PrismDiv
|
||||||
|
tabIndex={0}
|
||||||
className={props.className}
|
className={props.className}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
ref={node => setNode(node!)}
|
ref={node => setNode(node!)}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
|
__html: jsonToHTML(props.data, options.jsonSamplesExpandLevel),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -6,11 +6,14 @@ import { StylingMarkdownProps } from './Markdown';
|
||||||
import { StyledMarkdownBlock } from './styled.elements';
|
import { StyledMarkdownBlock } from './styled.elements';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const StyledMarkdownSpan = styled(props => <StyledMarkdownBlock {...props} />)`
|
// Workaround for DOMPurify type issues (https://github.com/cure53/DOMPurify/issues/1034)
|
||||||
|
const dompurify = DOMPurify['default'] as DOMPurify.DOMPurify;
|
||||||
|
|
||||||
|
const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
|
||||||
display: inline;
|
display: inline;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
|
const sanitize = (sanitize, html) => (sanitize ? dompurify.sanitize(html) : html);
|
||||||
|
|
||||||
export function SanitizedMarkdownHTML({
|
export function SanitizedMarkdownHTML({
|
||||||
inline,
|
inline,
|
||||||
|
@ -25,7 +28,7 @@ export function SanitizedMarkdownHTML({
|
||||||
<Wrap
|
<Wrap
|
||||||
className={'redoc-markdown ' + (rest.className || '')}
|
className={'redoc-markdown ' + (rest.className || '')}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: sanitize(options.untrustedSpec, rest.html),
|
__html: sanitize(options.sanitize, rest.html),
|
||||||
}}
|
}}
|
||||||
data-role={rest['data-role']}
|
data-role={rest['data-role']}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|
|
@ -28,9 +28,20 @@ export interface OperationProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Operation = observer(({ operation }: OperationProps): JSX.Element => {
|
export const Operation = observer(({ operation }: OperationProps): JSX.Element => {
|
||||||
const { name: summary, description, deprecated, externalDocs, isWebhook, httpVerb } = operation;
|
const {
|
||||||
|
name: summary,
|
||||||
|
description,
|
||||||
|
deprecated,
|
||||||
|
externalDocs,
|
||||||
|
isWebhook,
|
||||||
|
httpVerb,
|
||||||
|
badges,
|
||||||
|
} = operation;
|
||||||
const hasDescription = !!(description || externalDocs);
|
const hasDescription = !!(description || externalDocs);
|
||||||
const { showWebhookVerb } = React.useContext(OptionsContext);
|
const { showWebhookVerb } = React.useContext(OptionsContext);
|
||||||
|
const badgesBefore = badges.filter(({ position }) => position === 'before');
|
||||||
|
const badgesAfter = badges.filter(({ position }) => position === 'after');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
|
@ -38,6 +49,11 @@ export const Operation = observer(({ operation }: OperationProps): JSX.Element =
|
||||||
<MiddlePanel>
|
<MiddlePanel>
|
||||||
<H2>
|
<H2>
|
||||||
<ShareLink to={operation.id} />
|
<ShareLink to={operation.id} />
|
||||||
|
{badgesBefore.map(({ name, color }) => (
|
||||||
|
<Badge type="primary" key={name} color={color}>
|
||||||
|
{name}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||||
{isWebhook && (
|
{isWebhook && (
|
||||||
<Badge type="primary">
|
<Badge type="primary">
|
||||||
|
@ -45,6 +61,11 @@ export const Operation = observer(({ operation }: OperationProps): JSX.Element =
|
||||||
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
|
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
{badgesAfter.map(({ name, color }) => (
|
||||||
|
<Badge type="primary" key={name} color={color}>
|
||||||
|
{name}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
</H2>
|
</H2>
|
||||||
{options.pathInMiddlePanel && !isWebhook && (
|
{options.pathInMiddlePanel && !isWebhook && (
|
||||||
<Endpoint operation={operation} inverted={true} />
|
<Endpoint operation={operation} inverted={true} />
|
||||||
|
|
|
@ -16,14 +16,24 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
|
||||||
render() {
|
render() {
|
||||||
const schema = this.props.schema;
|
const schema = this.props.schema;
|
||||||
const itemsSchema = schema.items;
|
const itemsSchema = schema.items;
|
||||||
|
const fieldParentsName = this.props.fieldParentsName;
|
||||||
|
|
||||||
const minMaxItems =
|
const minMaxItems =
|
||||||
schema.minItems === undefined && schema.maxItems === undefined
|
schema.minItems === undefined && schema.maxItems === undefined
|
||||||
? ''
|
? ''
|
||||||
: `(${humanizeConstraints(schema)})`;
|
: `(${humanizeConstraints(schema)})`;
|
||||||
|
|
||||||
|
const updatedParentsArray = fieldParentsName
|
||||||
|
? [...fieldParentsName.slice(0, -1), fieldParentsName[fieldParentsName.length - 1] + '[]']
|
||||||
|
: fieldParentsName;
|
||||||
if (schema.fields) {
|
if (schema.fields) {
|
||||||
return <ObjectSchema {...(this.props as any)} level={this.props.level} />;
|
return (
|
||||||
|
<ObjectSchema
|
||||||
|
{...(this.props as any)}
|
||||||
|
level={this.props.level}
|
||||||
|
fieldParentsName={updatedParentsArray}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
||||||
return (
|
return (
|
||||||
|
@ -37,7 +47,7 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
|
||||||
<div>
|
<div>
|
||||||
<ArrayOpenningLabel> Array {minMaxItems}</ArrayOpenningLabel>
|
<ArrayOpenningLabel> Array {minMaxItems}</ArrayOpenningLabel>
|
||||||
<PaddedSchema>
|
<PaddedSchema>
|
||||||
<Schema {...this.props} schema={itemsSchema} />
|
<Schema {...this.props} schema={itemsSchema} fieldParentsName={updatedParentsArray} />
|
||||||
</PaddedSchema>
|
</PaddedSchema>
|
||||||
<ArrayClosingLabel />
|
<ArrayClosingLabel />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,6 +16,7 @@ export interface ObjectSchemaProps extends SchemaProps {
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
parentSchema: SchemaModel;
|
parentSchema: SchemaModel;
|
||||||
};
|
};
|
||||||
|
fieldParentsName?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ObjectSchema = observer(
|
export const ObjectSchema = observer(
|
||||||
|
@ -26,8 +27,9 @@ export const ObjectSchema = observer(
|
||||||
skipReadOnly,
|
skipReadOnly,
|
||||||
skipWriteOnly,
|
skipWriteOnly,
|
||||||
level,
|
level,
|
||||||
|
fieldParentsName,
|
||||||
}: ObjectSchemaProps) => {
|
}: ObjectSchemaProps) => {
|
||||||
const { expandSingleSchemaField, showObjectSchemaExamples, schemaExpansionLevel } =
|
const { expandSingleSchemaField, showObjectSchemaExamples, schemasExpansionLevel } =
|
||||||
React.useContext(OptionsContext);
|
React.useContext(OptionsContext);
|
||||||
|
|
||||||
const filteredFields = React.useMemo(
|
const filteredFields = React.useMemo(
|
||||||
|
@ -45,7 +47,7 @@ export const ObjectSchema = observer(
|
||||||
);
|
);
|
||||||
|
|
||||||
const expandByDefault =
|
const expandByDefault =
|
||||||
(expandSingleSchemaField && filteredFields.length === 1) || schemaExpansionLevel >= level!;
|
(expandSingleSchemaField && filteredFields.length === 1) || schemasExpansionLevel >= level!;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PropertiesTable>
|
<PropertiesTable>
|
||||||
|
@ -58,6 +60,7 @@ export const ObjectSchema = observer(
|
||||||
isLast={isLast}
|
isLast={isLast}
|
||||||
field={field}
|
field={field}
|
||||||
expandByDefault={expandByDefault}
|
expandByDefault={expandByDefault}
|
||||||
|
fieldParentsName={Number(level) > 1 ? fieldParentsName : []}
|
||||||
renderDiscriminatorSwitch={
|
renderDiscriminatorSwitch={
|
||||||
discriminator?.fieldName === field.name
|
discriminator?.fieldName === field.name
|
||||||
? () => (
|
? () => (
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface SchemaOptions {
|
||||||
|
|
||||||
export interface SchemaProps extends SchemaOptions {
|
export interface SchemaProps extends SchemaOptions {
|
||||||
schema: SchemaModel;
|
schema: SchemaModel;
|
||||||
|
fieldParentsName?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
||||||
activeItemRef: MenuItem | null = null;
|
activeItemRef: MenuItem | null = null;
|
||||||
|
|
||||||
static contextType = OptionsContext;
|
static contextType = OptionsContext;
|
||||||
|
declare context: React.ContextType<typeof OptionsContext>;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
|
@ -54,7 +54,7 @@ export function SecurityRequirements(props: SecurityRequirementsProps) {
|
||||||
</SecuritiesColumn>
|
</SecuritiesColumn>
|
||||||
</Wrap>
|
</Wrap>
|
||||||
{expanded &&
|
{expanded &&
|
||||||
operationSecuritySchemes?.length &&
|
!!operationSecuritySchemes?.length &&
|
||||||
operationSecuritySchemes.map((scheme, idx) => (
|
operationSecuritySchemes.map((scheme, idx) => (
|
||||||
<SecurityDetailsStyle key={idx}>
|
<SecurityDetailsStyle key={idx}>
|
||||||
<h5>
|
<h5>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||||
|
|
||||||
import { ClipboardService } from '../../services';
|
import { ClipboardService } from '../../services';
|
||||||
|
|
||||||
export class SelectOnClick extends React.PureComponent {
|
export class SelectOnClick extends React.PureComponent<React.PropsWithChildren<any>> {
|
||||||
private child: HTMLDivElement | null;
|
private child: HTMLDivElement | null;
|
||||||
selectElement = () => {
|
selectElement = () => {
|
||||||
ClipboardService.selectElement(this.child);
|
ClipboardService.selectElement(this.child);
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { ShelfIcon } from '../../common-elements/shelfs';
|
import { ShelfIcon } from '../../common-elements/shelfs';
|
||||||
|
import type { IMenuItem } from '../../services';
|
||||||
import { OperationModel } from '../../services';
|
import { OperationModel } from '../../services';
|
||||||
import { shortenHTTPVerb } from '../../utils/openapi';
|
|
||||||
import { MenuItems } from './MenuItems';
|
|
||||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
import { scrollIntoViewIfNeeded } from '../../utils';
|
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||||
|
import { shortenHTTPVerb } from '../../utils/openapi';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import type { IMenuItem } from '../../services';
|
import { MenuItems } from './MenuItems';
|
||||||
|
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||||
|
|
||||||
export interface MenuItemProps {
|
export interface MenuItemProps {
|
||||||
item: IMenuItem;
|
item: IMenuItem;
|
||||||
|
@ -47,9 +47,18 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
<MenuItemLi
|
<MenuItemLi
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={this.activate}
|
onClick={this.activate}
|
||||||
|
onKeyDown={evt => {
|
||||||
|
// Space or Enter key will activate the menu item
|
||||||
|
if (evt.key === 'Enter' || evt.key === ' ') {
|
||||||
|
this.props.onActivate!(this.props.item);
|
||||||
|
evt.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
depth={item.depth}
|
depth={item.depth}
|
||||||
data-item-id={item.id}
|
data-item-id={item.id}
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
|
aria-label={item.sidebarLabel}
|
||||||
|
aria-expanded={item.expanded}
|
||||||
>
|
>
|
||||||
{item.type === 'operation' ? (
|
{item.type === 'operation' ? (
|
||||||
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
||||||
|
@ -101,6 +110,12 @@ export const OperationMenuItemContent = observer((props: OperationMenuItemConten
|
||||||
$deprecated={item.deprecated}
|
$deprecated={item.deprecated}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
|
{item.badges &&
|
||||||
|
item.badges?.map(({ name, color }) => (
|
||||||
|
<OperationBadge type="badge" color={color} key={name}>
|
||||||
|
{name}
|
||||||
|
</OperationBadge>
|
||||||
|
))}
|
||||||
{item.isWebhook ? (
|
{item.isWebhook ? (
|
||||||
<OperationBadge type="hook">
|
<OperationBadge type="hook">
|
||||||
{showWebhookVerb ? item.httpVerb : l('webhook')}
|
{showWebhookVerb ? item.httpVerb : l('webhook')}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import RedoclyLogo from './Logo';
|
||||||
@observer
|
@observer
|
||||||
export class SideMenu extends React.Component<{ menu: MenuStore; className?: string }> {
|
export class SideMenu extends React.Component<{ menu: MenuStore; className?: string }> {
|
||||||
static contextType = OptionsContext;
|
static contextType = OptionsContext;
|
||||||
|
declare context: React.ContextType<typeof OptionsContext>;
|
||||||
private _updateScroll?: () => void;
|
private _updateScroll?: () => void;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { default as classnames } from 'classnames';
|
import * as classnames from 'classnames';
|
||||||
import { darken } from 'polished';
|
import { darken } from 'polished';
|
||||||
|
|
||||||
import { deprecatedCss, ShelfIcon } from '../../common-elements';
|
import { deprecatedCss, ShelfIcon } from '../../common-elements';
|
||||||
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';
|
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';
|
||||||
|
|
||||||
export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
|
export const OperationBadge = styled.span.attrs((props: { type: string; color?: string }) => ({
|
||||||
className: `operation-type ${props.type}`,
|
className: `operation-type ${props.type}`,
|
||||||
}))<{ type: string }>`
|
}))<{ type: string; color?: string }>`
|
||||||
width: 9ex;
|
width: 9ex;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: ${props => props.theme.typography.code.fontSize};
|
height: ${props => props.theme.typography.code.fontSize};
|
||||||
line-height: ${props => props.theme.typography.code.fontSize};
|
line-height: ${props => props.theme.typography.code.fontSize};
|
||||||
background-color: #333;
|
background-color: ${props => props.color || '#333'};
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 6px 4px;
|
background-position: 6px 4px;
|
||||||
|
|
|
@ -85,7 +85,7 @@ const FloatingButton = styled.div`
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class StickyResponsiveSidebar extends React.Component<
|
export class StickyResponsiveSidebar extends React.Component<
|
||||||
StickySidebarProps,
|
React.PropsWithChildren<StickySidebarProps>,
|
||||||
StickySidebarState
|
StickySidebarState
|
||||||
> {
|
> {
|
||||||
static contextType = OptionsContext;
|
static contextType = OptionsContext;
|
||||||
|
|
|
@ -56,6 +56,7 @@ export function StoreBuilder(props: StoreBuilderProps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [spec, specUrl]);
|
}, [spec, specUrl]);
|
||||||
|
|
||||||
const store = React.useMemo(() => {
|
const store = React.useMemo(() => {
|
||||||
|
|
|
@ -90,7 +90,7 @@ describe('FieldDetailsComponent', () => {
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
pattern: '^see regex[0-9]$',
|
pattern: '^see regex[0-9]$',
|
||||||
constraints: [''],
|
constraints: ['<= 128 characters'],
|
||||||
externalDocs: undefined,
|
externalDocs: undefined,
|
||||||
},
|
},
|
||||||
} as any as SchemaModel,
|
} as any as SchemaModel,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { mount, ReactWrapper } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { act } from 'react';
|
||||||
|
|
||||||
import { JsonViewer } from '../';
|
import { JsonViewer } from '../';
|
||||||
import { withTheme } from '../testProviders';
|
import { withTheme } from '../testProviders';
|
||||||
|
@ -50,5 +51,54 @@ describe('Components', () => {
|
||||||
expect(flatDataComponent.html()).not.toContain('Expand all');
|
expect(flatDataComponent.html()).not.toContain('Expand all');
|
||||||
expect(flatDataComponent.html()).not.toContain('Collapse all');
|
expect(flatDataComponent.html()).not.toContain('Collapse all');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Keyboard Navigation', () => {
|
||||||
|
let component: ReactWrapper;
|
||||||
|
const data = {
|
||||||
|
a: 1,
|
||||||
|
b: {
|
||||||
|
c:
|
||||||
|
// Long string to test horizontal scrolling
|
||||||
|
Array(100).fill('hello').join(''),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component = mount(withTheme(<JsonViewer data={data} />));
|
||||||
|
ClipboardService.copySelected = origCopySelected;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle arrow key navigation', () => {
|
||||||
|
const prismDiv = component.find('div[tabIndex=0]');
|
||||||
|
const divElement = prismDiv.getDOMNode();
|
||||||
|
|
||||||
|
// Mock scrollLeft before events
|
||||||
|
Object.defineProperty(divElement, 'scrollLeft', {
|
||||||
|
get: jest.fn(() => 0),
|
||||||
|
set: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger events inside act()
|
||||||
|
act(() => {
|
||||||
|
divElement.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', {
|
||||||
|
key: 'ArrowRight',
|
||||||
|
bubbles: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
divElement.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', {
|
||||||
|
key: 'ArrowLeft',
|
||||||
|
bubbles: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(divElement.scrollLeft).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -159,11 +159,19 @@ exports[`FieldDetailsComponent renders correctly when field items have string ty
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="sc-kpDqfm sc-dAlyuH sc-dxcDKg cGRfjn gHomYR gXntsr"
|
class="sc-kpDqfm sc-dAlyuH sc-gvZAcH cGRfjn gHomYR eXivNJ"
|
||||||
>
|
>
|
||||||
[
|
[ items
|
||||||
|
<span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="sc-kpDqfm sc-eDPEul cGRfjn erJHow"
|
class="sc-kpDqfm sc-gFqAkR cGRfjn fYEICH"
|
||||||
|
>
|
||||||
|
<= 128 characters
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="sc-kpDqfm sc-eDPEul cGRfjn cCKYVD"
|
||||||
>
|
>
|
||||||
^see regex[0-9]$
|
^see regex[0-9]$
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`SecurityRequirement should render SecurityDefs 1`] = `
|
exports[`SecurityRequirement should render SecurityDefs 1`] = `
|
||||||
"<div id=\\"section/Authentication/petstore_auth\\" data-section-id=\\"section/Authentication/petstore_auth\\" class=\\"sc-dcJsrY bBkGhy\\"><div class=\\"sc-kAyceB hBQWIZ\\"><div class=\\"sc-fqkvVR oJKYx\\"><h2 class=\\"sc-jXbUNg fWnwAh\\">petstore_auth</h2><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>Get access to data while protecting your account credentials.
|
"<div id="section/Authentication/petstore_auth" data-section-id="section/Authentication/petstore_auth" class="sc-dcJsrY bBkGhy"><div class="sc-kAyceB hBQWIZ"><div class="sc-fqkvVR oJKYx"><h2 class="sc-jXbUNg fWnwAh">petstore_auth</h2><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>Get access to data while protecting your account credentials.
|
||||||
OAuth2 is also a safer and more secure way to give you access.</p>
|
OAuth2 is also a safer and more secure way to give you access.</p>
|
||||||
</div><div class=\\"sc-ejfMa-d a-DjBE\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Security Scheme Type: </b><span>OAuth2</span></div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Flow type: </b><code>implicit </code></div><div class=\\"sc-dkmUuB hFwAIA\\"><strong> Authorization URL: </strong><code><a target=\\"_blank\\" rel=\\"noopener noreferrer\\" href=\\"http://petstore.swagger.io/api/oauth/dialog\\">http://petstore.swagger.io/api/oauth/dialog</a></code></div><div class=\\"sc-dkmUuB hFwAIA\\"><b> Scopes: </b></div><div class=\\"sc-iEXKAA blExNw container\\" style=\\"height: 4em;\\"><ul><li><code>write:pets</code> - <div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru sc-fhzFiK hXtrri redoc-markdown\\"><p>modify pets in your account</p>
|
</div><div class="sc-iEXKAA ebCiwb"><div class="sc-ejfMa-d bdDYxc"><b>Security Scheme Type: </b><span>OAuth2</span></div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Flow type: </b><code>implicit </code></div><div class="sc-ejfMa-d bdDYxc"><strong> Authorization URL: </strong><code><a target="_blank" rel="noopener noreferrer" href="http://petstore.swagger.io/api/oauth/dialog">http://petstore.swagger.io/api/oauth/dialog</a></code></div><div class="sc-ejfMa-d bdDYxc"><b> Scopes: </b></div><div class="sc-EgOXT kRIdPi container" style="height: 4em;"><ul><li><code>write:pets</code> - <div class="sc-eeDRCY sc-eBMEME sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>modify pets in your account</p>
|
||||||
</div></li><li><code>read:pets</code> - <div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru sc-fhzFiK hXtrri redoc-markdown\\"><p>read your pets</p>
|
</div></li><li><code>read:pets</code> - <div class="sc-eeDRCY sc-eBMEME sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>read your pets</p>
|
||||||
</div></li></ul></div><div class=\\"sc-EgOXT bNSpXO\\"></div></div></div></div></div></div><div id=\\"section/Authentication/GitLab_PersonalAccessToken\\" data-section-id=\\"section/Authentication/GitLab_PersonalAccessToken\\" class=\\"sc-dcJsrY bBkGhy\\"><div class=\\"sc-kAyceB hBQWIZ\\"><div class=\\"sc-fqkvVR oJKYx\\"><h2 class=\\"sc-jXbUNg fWnwAh\\">GitLab_PersonalAccessToken</h2><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>GitLab Personal Access Token description</p>
|
</div></li></ul></div><div class="sc-eZYNyq dIKkVb"></div></div></div></div></div></div><div id="section/Authentication/GitLab_PersonalAccessToken" data-section-id="section/Authentication/GitLab_PersonalAccessToken" class="sc-dcJsrY bBkGhy"><div class="sc-kAyceB hBQWIZ"><div class="sc-fqkvVR oJKYx"><h2 class="sc-jXbUNg fWnwAh">GitLab_PersonalAccessToken</h2><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>GitLab Personal Access Token description</p>
|
||||||
</div><div class=\\"sc-ejfMa-d a-DjBE\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Security Scheme Type: </b><span>API Key</span></div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Header parameter name: </b><code>PRIVATE-TOKEN</code></div></div></div></div></div></div><div id=\\"section/Authentication/GitLab_OpenIdConnect\\" data-section-id=\\"section/Authentication/GitLab_OpenIdConnect\\" class=\\"sc-dcJsrY bBkGhy\\"><div class=\\"sc-kAyceB hBQWIZ\\"><div class=\\"sc-fqkvVR oJKYx\\"><h2 class=\\"sc-jXbUNg fWnwAh\\">GitLab_OpenIdConnect</h2><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>GitLab OpenIdConnect description</p>
|
</div><div class="sc-iEXKAA ebCiwb"><div class="sc-ejfMa-d bdDYxc"><b>Security Scheme Type: </b><span>API Key</span></div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Header parameter name: </b><code>PRIVATE-TOKEN</code></div></div></div></div></div></div><div id="section/Authentication/GitLab_OpenIdConnect" data-section-id="section/Authentication/GitLab_OpenIdConnect" class="sc-dcJsrY bBkGhy"><div class="sc-kAyceB hBQWIZ"><div class="sc-fqkvVR oJKYx"><h2 class="sc-jXbUNg fWnwAh">GitLab_OpenIdConnect</h2><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>GitLab OpenIdConnect description</p>
|
||||||
</div><div class=\\"sc-ejfMa-d a-DjBE\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Security Scheme Type: </b><span>OpenID Connect</span></div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Connect URL: </b><code><a target=\\"_blank\\" rel=\\"noopener noreferrer\\" href=\\"https://gitlab.com/.well-known/openid-configuration\\">https://gitlab.com/.well-known/openid-configuration</a></code></div></div></div></div></div></div><div id=\\"section/Authentication/basicAuth\\" data-section-id=\\"section/Authentication/basicAuth\\" class=\\"sc-dcJsrY bBkGhy\\"><div class=\\"sc-kAyceB hBQWIZ\\"><div class=\\"sc-fqkvVR oJKYx\\"><h2 class=\\"sc-jXbUNg fWnwAh\\">basicAuth</h2><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"></div><div class=\\"sc-ejfMa-d a-DjBE\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Security Scheme Type: </b><span>HTTP</span></div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>HTTP Authorization Scheme: </b><code>basic</code></div><div class=\\"sc-dkmUuB hFwAIA\\"></div></div></div></div></div></div>"
|
</div><div class="sc-iEXKAA ebCiwb"><div class="sc-ejfMa-d bdDYxc"><b>Security Scheme Type: </b><span>OpenID Connect</span></div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Connect URL: </b><code><a target="_blank" rel="noopener noreferrer" href="https://gitlab.com/.well-known/openid-configuration">https://gitlab.com/.well-known/openid-configuration</a></code></div></div></div></div></div></div><div id="section/Authentication/basicAuth" data-section-id="section/Authentication/basicAuth" class="sc-dcJsrY bBkGhy"><div class="sc-kAyceB hBQWIZ"><div class="sc-fqkvVR oJKYx"><h2 class="sc-jXbUNg fWnwAh">basicAuth</h2><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"></div><div class="sc-iEXKAA ebCiwb"><div class="sc-ejfMa-d bdDYxc"><b>Security Scheme Type: </b><span>HTTP</span></div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>HTTP Authorization Scheme: </b><code>basic</code></div><div class="sc-ejfMa-d bdDYxc"></div></div></div></div></div></div>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`SecurityRequirement should render authDefinition 1`] = `"<div class=\\"sc-bDumWk iWBBny\\"><div class=\\"sc-sLsrZ hgeUJn\\"><h5 class=\\"sc-dAlyuH sc-fifgRP jbQuod kWJur\\">Authorizations:</h5><svg class=\\"sc-cwHptR iZRiKW\\" version=\\"1.1\\" viewBox=\\"0 0 24 24\\" x=\\"0\\" xmlns=\\"http://www.w3.org/2000/svg\\" y=\\"0\\" aria-hidden=\\"true\\"><polygon points=\\"17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 \\"></polygon></svg></div><div class=\\"sc-dBmzty eoFcYg\\"><span class=\\"sc-kbousE cpXQuZ\\">(<span class=\\"sc-gfoqjT kbvnry\\">API Key: <i>GitLab_PersonalAccessToken</i></span><span class=\\"sc-gfoqjT kbvnry\\">OpenID Connect: <i>GitLab_OpenIdConnect</i></span><span class=\\"sc-gfoqjT kbvnry\\">HTTP: <i>basicAuth</i></span>) </span><span class=\\"sc-kbousE cpXQuZ\\"><span class=\\"sc-gfoqjT kbvnry\\">OAuth2: <i>petstore_auth</i></span></span></div></div>,"`;
|
exports[`SecurityRequirement should render authDefinition 1`] = `"<div class="sc-dkmUuB fUBzjk"><div class="sc-dBmzty iDyBRL"><h5 class="sc-dAlyuH sc-bDumWk jbQuod feBYnB">Authorizations:</h5><svg class="sc-cwHptR iZRiKW" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></div><div class="sc-fifgRP eqIYDA"><span class="sc-sLsrZ jmro">(<span class="sc-kbousE iMnLRS">API Key: <i>GitLab_PersonalAccessToken</i></span><span class="sc-kbousE iMnLRS">OpenID Connect: <i>GitLab_OpenIdConnect</i></span><span class="sc-kbousE iMnLRS">HTTP: <i>basicAuth</i></span>) </span><span class="sc-sLsrZ jmro"><span class="sc-kbousE iMnLRS">OAuth2: <i>petstore_auth</i></span></span></div></div>,"`;
|
||||||
|
|
||||||
exports[`SecurityRequirement should render authDefinition 2`] = `
|
exports[`SecurityRequirement should render authDefinition 2`] = `
|
||||||
"<div class=\\"sc-bDumWk gtsPcy\\"><div class=\\"sc-sLsrZ hgeUJn\\"><h5 class=\\"sc-dAlyuH sc-fifgRP jbQuod kWJur\\">Authorizations:</h5><svg class=\\"sc-cwHptR dSJqIk\\" version=\\"1.1\\" viewBox=\\"0 0 24 24\\" x=\\"0\\" xmlns=\\"http://www.w3.org/2000/svg\\" y=\\"0\\" aria-hidden=\\"true\\"><polygon points=\\"17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 \\"></polygon></svg></div><div class=\\"sc-dBmzty llvZdI\\"><span class=\\"sc-kbousE dOwJQz\\">(<span class=\\"sc-gfoqjT kbvnry\\">API Key: <i>GitLab_PersonalAccessToken</i></span><span class=\\"sc-gfoqjT kbvnry\\">OpenID Connect: <i>GitLab_OpenIdConnect</i></span><span class=\\"sc-gfoqjT kbvnry\\">HTTP: <i>basicAuth</i></span>) </span><span class=\\"sc-kbousE dOwJQz\\"><span class=\\"sc-gfoqjT kbvnry\\">OAuth2: <i>petstore_auth</i> (<code class=\\"sc-eyvILC bzHwfc\\">write:pets</code><code class=\\"sc-eyvILC bzHwfc\\">read:pets</code>) </span></span></div></div><div class=\\"sc-ejfMa-d a-DjBE\\"><h5><svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\" width=\\"11\\" height=\\"11\\"><path fill=\\"currentColor\\" d=\\"M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z\\"></path></svg> OAuth2: petstore_auth</h5><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>Get access to data while protecting your account credentials.
|
"<div class="sc-dkmUuB KTEsk"><div class="sc-dBmzty iDyBRL"><h5 class="sc-dAlyuH sc-bDumWk jbQuod feBYnB">Authorizations:</h5><svg class="sc-cwHptR dSJqIk" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></div><div class="sc-fifgRP gNcumo"><span class="sc-sLsrZ iTheFK">(<span class="sc-kbousE iMnLRS">API Key: <i>GitLab_PersonalAccessToken</i></span><span class="sc-kbousE iMnLRS">OpenID Connect: <i>GitLab_OpenIdConnect</i></span><span class="sc-kbousE iMnLRS">HTTP: <i>basicAuth</i></span>) </span><span class="sc-sLsrZ iTheFK"><span class="sc-kbousE iMnLRS">OAuth2: <i>petstore_auth</i> (<code class="sc-gfoqjT dapMvh">write:pets</code><code class="sc-gfoqjT dapMvh">read:pets</code>) </span></span></div></div><div class="sc-iEXKAA ebCiwb"><h5><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z"></path></svg> OAuth2: petstore_auth</h5><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>Get access to data while protecting your account credentials.
|
||||||
OAuth2 is also a safer and more secure way to give you access.</p>
|
OAuth2 is also a safer and more secure way to give you access.</p>
|
||||||
</div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Flow type: </b><code>implicit </code></div><div class=\\"sc-dkmUuB hFwAIA\\"><strong> Authorization URL: </strong><code><a target=\\"_blank\\" rel=\\"noopener noreferrer\\" href=\\"http://petstore.swagger.io/api/oauth/dialog\\">http://petstore.swagger.io/api/oauth/dialog</a></code></div><div><b>Required scopes: </b><code>write:pets</code> <code>read:pets</code> </div><div class=\\"sc-dkmUuB hFwAIA\\"><b> Scopes: </b></div><div class=\\"sc-iEXKAA blExNw container\\" style=\\"height: 4em;\\"><ul><li><code>write:pets</code> - <div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru sc-fhzFiK hXtrri redoc-markdown\\"><p>modify pets in your account</p>
|
</div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Flow type: </b><code>implicit </code></div><div class="sc-ejfMa-d bdDYxc"><strong> Authorization URL: </strong><code><a target="_blank" rel="noopener noreferrer" href="http://petstore.swagger.io/api/oauth/dialog">http://petstore.swagger.io/api/oauth/dialog</a></code></div><div><b>Required scopes: </b><code>write:pets</code> <code>read:pets</code> </div><div class="sc-ejfMa-d bdDYxc"><b> Scopes: </b></div><div class="sc-EgOXT kRIdPi container" style="height: 4em;"><ul><li><code>write:pets</code> - <div class="sc-eeDRCY sc-eBMEME sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>modify pets in your account</p>
|
||||||
</div></li><li><code>read:pets</code> - <div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru sc-fhzFiK hXtrri redoc-markdown\\"><p>read your pets</p>
|
</div></li><li><code>read:pets</code> - <div class="sc-eeDRCY sc-eBMEME sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>read your pets</p>
|
||||||
</div></li></ul></div><div class=\\"sc-EgOXT bNSpXO\\"></div></div></div><div class=\\"sc-ejfMa-d a-DjBE\\"><h5><svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\" width=\\"11\\" height=\\"11\\"><path fill=\\"currentColor\\" d=\\"M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z\\"></path></svg> API Key: GitLab_PersonalAccessToken</h5><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>GitLab Personal Access Token description</p>
|
</div></li></ul></div><div class="sc-eZYNyq dIKkVb"></div></div></div><div class="sc-iEXKAA ebCiwb"><h5><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z"></path></svg> API Key: GitLab_PersonalAccessToken</h5><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>GitLab Personal Access Token description</p>
|
||||||
</div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Header parameter name: </b><code>PRIVATE-TOKEN</code></div></div></div><div class=\\"sc-ejfMa-d a-DjBE\\"><h5><svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\" width=\\"11\\" height=\\"11\\"><path fill=\\"currentColor\\" d=\\"M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z\\"></path></svg> OpenID Connect: GitLab_OpenIdConnect</h5><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><p>GitLab OpenIdConnect description</p>
|
</div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Header parameter name: </b><code>PRIVATE-TOKEN</code></div></div></div><div class="sc-iEXKAA ebCiwb"><h5><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z"></path></svg> OpenID Connect: GitLab_OpenIdConnect</h5><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><p>GitLab OpenIdConnect description</p>
|
||||||
</div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>Connect URL: </b><code><a target=\\"_blank\\" rel=\\"noopener noreferrer\\" href=\\"https://gitlab.com/.well-known/openid-configuration\\">https://gitlab.com/.well-known/openid-configuration</a></code></div></div></div><div class=\\"sc-ejfMa-d a-DjBE\\"><h5><svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\" width=\\"11\\" height=\\"11\\"><path fill=\\"currentColor\\" d=\\"M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z\\"></path></svg> HTTP: basicAuth</h5><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"></div><div class=\\"sc-eeDRCY sc-eBMEME gTGgei fMmru\\"><div class=\\"sc-dkmUuB hFwAIA\\"><b>HTTP Authorization Scheme: </b><code>basic</code></div><div class=\\"sc-dkmUuB hFwAIA\\"></div></div></div>,"
|
</div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>Connect URL: </b><code><a target="_blank" rel="noopener noreferrer" href="https://gitlab.com/.well-known/openid-configuration">https://gitlab.com/.well-known/openid-configuration</a></code></div></div></div><div class="sc-iEXKAA ebCiwb"><h5><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z"></path></svg> HTTP: basicAuth</h5><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"></div><div class="sc-eeDRCY sc-eBMEME gTGgei fMmru"><div class="sc-ejfMa-d bdDYxc"><b>HTTP Authorization Scheme: </b><code>basic</code></div><div class="sc-ejfMa-d bdDYxc"></div></div></div>,"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -2,7 +2,9 @@ import * as React from 'react';
|
||||||
import { ThemeProvider } from 'styled-components';
|
import { ThemeProvider } from 'styled-components';
|
||||||
import defaultTheme, { resolveTheme } from '../theme';
|
import defaultTheme, { resolveTheme } from '../theme';
|
||||||
|
|
||||||
export default class TestThemeProvider extends React.Component {
|
import { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export default class TestThemeProvider extends React.Component<PropsWithChildren<any>> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={resolveTheme(defaultTheme)}>
|
<ThemeProvider theme={resolveTheme(defaultTheme)}>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { OpenAPISpec, OpenAPIPaths, OpenAPITag, OpenAPISchema } from '../types';
|
import type { OpenAPIPaths, OpenAPITag, OpenAPISchema } from '../types';
|
||||||
import { isOperationName, JsonPointer, alphabeticallyByProp } from '../utils';
|
import { isOperationName, JsonPointer, alphabeticallyByProp } from '../utils';
|
||||||
import { MarkdownRenderer } from './MarkdownRenderer';
|
import { MarkdownRenderer } from './MarkdownRenderer';
|
||||||
import { GroupModel, OperationModel } from './models';
|
import { GroupModel, OperationModel } from './models';
|
||||||
|
@ -17,9 +17,19 @@ export class MenuBuilder {
|
||||||
options: RedocNormalizedOptions,
|
options: RedocNormalizedOptions,
|
||||||
): ContentItemModel[] {
|
): ContentItemModel[] {
|
||||||
const spec = parser.spec;
|
const spec = parser.spec;
|
||||||
|
const { schemaDefinitionsTagName } = options;
|
||||||
|
|
||||||
const items: ContentItemModel[] = [];
|
const items: ContentItemModel[] = [];
|
||||||
const tagsMap = MenuBuilder.getTagsWithOperations(parser, spec);
|
const tags = [...(spec.tags || [])];
|
||||||
|
const hasAutogenerated = tags.find(
|
||||||
|
tag => tag?.name === schemaDefinitionsTagName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasAutogenerated && schemaDefinitionsTagName) {
|
||||||
|
tags.push({ name: schemaDefinitionsTagName });
|
||||||
|
}
|
||||||
|
const tagsMap = MenuBuilder.getTagsWithOperations(parser, tags);
|
||||||
|
|
||||||
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, 1, options));
|
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, 1, options));
|
||||||
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
|
||||||
items.push(
|
items.push(
|
||||||
|
@ -28,6 +38,7 @@ export class MenuBuilder {
|
||||||
} else {
|
} else {
|
||||||
items.push(...MenuBuilder.getTagsItems(parser, tagsMap, undefined, undefined, options));
|
items.push(...MenuBuilder.getTagsItems(parser, tagsMap, undefined, undefined, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +152,7 @@ export class MenuBuilder {
|
||||||
parser,
|
parser,
|
||||||
tag,
|
tag,
|
||||||
parent: item,
|
parent: item,
|
||||||
|
schemaDefinitionsTagName: options.schemaDefinitionsTagName,
|
||||||
});
|
});
|
||||||
|
|
||||||
item.items = [
|
item.items = [
|
||||||
|
@ -195,10 +207,11 @@ export class MenuBuilder {
|
||||||
/**
|
/**
|
||||||
* collects tags and maps each tag to list of operations belonging to this tag
|
* collects tags and maps each tag to list of operations belonging to this tag
|
||||||
*/
|
*/
|
||||||
static getTagsWithOperations(parser: OpenAPIParser, spec: OpenAPISpec): TagsInfoMap {
|
static getTagsWithOperations(parser: OpenAPIParser, explicitTags: OpenAPITag[]): TagsInfoMap {
|
||||||
|
const { spec } = parser;
|
||||||
const tags: TagsInfoMap = {};
|
const tags: TagsInfoMap = {};
|
||||||
const webhooks = spec['x-webhooks'] || spec.webhooks;
|
const webhooks = spec['x-webhooks'] || spec.webhooks;
|
||||||
for (const tag of spec.tags || []) {
|
for (const tag of explicitTags || []) {
|
||||||
tags[tag.name] = { ...tag, operations: [] };
|
tags[tag.name] = { ...tag, operations: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,14 +273,18 @@ export class MenuBuilder {
|
||||||
parser,
|
parser,
|
||||||
tag,
|
tag,
|
||||||
parent,
|
parent,
|
||||||
|
schemaDefinitionsTagName,
|
||||||
}: {
|
}: {
|
||||||
parser: OpenAPIParser;
|
parser: OpenAPIParser;
|
||||||
tag: TagInfo;
|
tag: TagInfo;
|
||||||
parent: GroupModel;
|
parent: GroupModel;
|
||||||
|
schemaDefinitionsTagName?: string;
|
||||||
}): GroupModel[] {
|
}): GroupModel[] {
|
||||||
|
const defaultTags = schemaDefinitionsTagName ? [schemaDefinitionsTagName] : [];
|
||||||
|
|
||||||
return Object.entries(parser.spec.components?.schemas || {})
|
return Object.entries(parser.spec.components?.schemas || {})
|
||||||
.map(([schemaName, schema]) => {
|
.map(([schemaName, schema]) => {
|
||||||
const schemaTags = schema['x-tags'];
|
const schemaTags = schema['x-tags'] || defaultTags;
|
||||||
if (!schemaTags?.includes(tag.name)) return null;
|
if (!schemaTags?.includes(tag.name)) return null;
|
||||||
|
|
||||||
const item = new GroupModel(
|
const item = new GroupModel(
|
||||||
|
|
|
@ -50,7 +50,7 @@ export class OpenAPIParser {
|
||||||
/**
|
/**
|
||||||
* get spec part by JsonPointer ($ref)
|
* get spec part by JsonPointer ($ref)
|
||||||
*/
|
*/
|
||||||
byRef = <T extends any = any>(ref: string): T | undefined => {
|
byRef = <T = any>(ref: string): T | undefined => {
|
||||||
let res;
|
let res;
|
||||||
if (!this.spec) {
|
if (!this.spec) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,7 +70,7 @@ export class OpenAPIParser {
|
||||||
/**
|
/**
|
||||||
* checks if the object is OpenAPI reference (contains $ref property)
|
* checks if the object is OpenAPI reference (contains $ref property)
|
||||||
*/
|
*/
|
||||||
isRef<T extends unknown>(obj: OpenAPIRef | T): obj is OpenAPIRef {
|
isRef<T>(obj: OpenAPIRef | T): obj is OpenAPIRef {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ export class OpenAPIParser {
|
||||||
* @param forceCircular whether to dereference even if it is circular ref
|
* @param forceCircular whether to dereference even if it is circular ref
|
||||||
* @param mergeAsAllOf
|
* @param mergeAsAllOf
|
||||||
*/
|
*/
|
||||||
deref<T extends unknown>(
|
deref<T>(
|
||||||
obj: OpenAPIRef | T,
|
obj: OpenAPIRef | T,
|
||||||
baseRefsStack: string[] = [],
|
baseRefsStack: string[] = [],
|
||||||
mergeAsAllOf = false,
|
mergeAsAllOf = false,
|
||||||
|
@ -124,7 +124,7 @@ export class OpenAPIParser {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeRefs<T extends unknown>(ref: OpenAPIRef, resolved: T, mergeAsAllOf: boolean): T {
|
mergeRefs<T>(ref: OpenAPIRef, resolved: T, mergeAsAllOf: boolean): T {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { $ref, ...rest } = ref;
|
const { $ref, ...rest } = ref;
|
||||||
const keys = Object.keys(rest);
|
const keys = Object.keys(rest);
|
||||||
|
@ -364,14 +364,18 @@ export class OpenAPIParser {
|
||||||
|
|
||||||
const allOf = schema.allOf;
|
const allOf = schema.allOf;
|
||||||
for (let i = 0; i < allOf.length; i++) {
|
for (let i = 0; i < allOf.length; i++) {
|
||||||
const sub = allOf[i];
|
const { oneOf, ...sub } = allOf[i];
|
||||||
if (Array.isArray(sub.oneOf)) {
|
if (!oneOf) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Array.isArray(oneOf)) {
|
||||||
const beforeAllOf = allOf.slice(0, i);
|
const beforeAllOf = allOf.slice(0, i);
|
||||||
const afterAllOf = allOf.slice(i + 1);
|
const afterAllOf = allOf.slice(i + 1);
|
||||||
|
const siblingValues = Object.keys(sub).length > 0 ? [sub] : [];
|
||||||
return {
|
return {
|
||||||
oneOf: sub.oneOf.map((part: OpenAPISchema) => {
|
oneOf: oneOf.map((part: OpenAPISchema) => {
|
||||||
return {
|
return {
|
||||||
allOf: [...beforeAllOf, part, ...afterAllOf],
|
allOf: [...beforeAllOf, ...siblingValues, part, ...afterAllOf],
|
||||||
'x-refsStack': refsStack,
|
'x-refsStack': refsStack,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -6,23 +6,32 @@ import { setRedocLabels } from './Labels';
|
||||||
import { SideNavStyleEnum } from './types';
|
import { SideNavStyleEnum } from './types';
|
||||||
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
|
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
|
||||||
|
|
||||||
|
export type DownloadUrlsConfig = {
|
||||||
|
title?: string;
|
||||||
|
url: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
export interface RedocRawOptions {
|
export interface RedocRawOptions {
|
||||||
theme?: ThemeInterface;
|
theme?: ThemeInterface;
|
||||||
scrollYOffset?: number | string | (() => number);
|
scrollYOffset?: number | string | (() => number);
|
||||||
hideHostname?: boolean | string;
|
hideHostname?: boolean | string;
|
||||||
expandResponses?: string | 'all';
|
expandResponses?: string | 'all';
|
||||||
requiredPropsFirst?: boolean | string;
|
requiredPropsFirst?: boolean | string; // remove in next major release
|
||||||
|
sortRequiredPropsFirst?: boolean | string;
|
||||||
sortPropsAlphabetically?: boolean | string;
|
sortPropsAlphabetically?: boolean | string;
|
||||||
sortEnumValuesAlphabetically?: boolean | string;
|
sortEnumValuesAlphabetically?: boolean | string;
|
||||||
sortOperationsAlphabetically?: boolean | string;
|
sortOperationsAlphabetically?: boolean | string;
|
||||||
sortTagsAlphabetically?: boolean | string;
|
sortTagsAlphabetically?: boolean | string;
|
||||||
nativeScrollbars?: boolean | string;
|
nativeScrollbars?: boolean | string;
|
||||||
pathInMiddlePanel?: boolean | string;
|
pathInMiddlePanel?: boolean | string;
|
||||||
untrustedSpec?: boolean | string;
|
untrustedSpec?: boolean | string; // remove in next major release
|
||||||
|
sanitize?: boolean | string;
|
||||||
hideLoading?: boolean | string;
|
hideLoading?: boolean | string;
|
||||||
hideDownloadButton?: boolean | string;
|
hideDownloadButton?: boolean | string; // remove in next major release
|
||||||
|
hideDownloadButtons?: boolean | string;
|
||||||
downloadFileName?: string;
|
downloadFileName?: string;
|
||||||
downloadDefinitionUrl?: string;
|
downloadDefinitionUrl?: string;
|
||||||
|
downloadUrls?: DownloadUrlsConfig;
|
||||||
disableSearch?: boolean | string;
|
disableSearch?: boolean | string;
|
||||||
onlyRequiredInSamples?: boolean | string;
|
onlyRequiredInSamples?: boolean | string;
|
||||||
showExtensions?: boolean | string | string[];
|
showExtensions?: boolean | string | string[];
|
||||||
|
@ -30,12 +39,15 @@ export interface RedocRawOptions {
|
||||||
hideSingleRequestSampleTab?: boolean | string;
|
hideSingleRequestSampleTab?: boolean | string;
|
||||||
hideRequestPayloadSample?: boolean;
|
hideRequestPayloadSample?: boolean;
|
||||||
menuToggle?: boolean | string;
|
menuToggle?: boolean | string;
|
||||||
jsonSampleExpandLevel?: number | string | 'all';
|
jsonSampleExpandLevel?: number | string | 'all'; // remove in next major release
|
||||||
|
jsonSamplesExpandLevel?: number | string | 'all';
|
||||||
hideSchemaTitles?: boolean | string;
|
hideSchemaTitles?: boolean | string;
|
||||||
simpleOneOfTypeLabel?: boolean | string;
|
simpleOneOfTypeLabel?: boolean | string;
|
||||||
payloadSampleIdx?: number;
|
payloadSampleIdx?: number;
|
||||||
expandSingleSchemaField?: boolean | string;
|
expandSingleSchemaField?: boolean | string;
|
||||||
schemaExpansionLevel?: number | string | 'all';
|
schemaExpansionLevel?: number | string | 'all'; // remove in next major release
|
||||||
|
schemasExpansionLevel?: number | string | 'all';
|
||||||
|
schemaDefinitionsTagName?: string;
|
||||||
showObjectSchemaExamples?: boolean | string;
|
showObjectSchemaExamples?: boolean | string;
|
||||||
showSecuritySchemeType?: boolean;
|
showSecuritySchemeType?: boolean;
|
||||||
hideSecuritySection?: boolean;
|
hideSecuritySection?: boolean;
|
||||||
|
@ -52,11 +64,13 @@ export interface RedocRawOptions {
|
||||||
maxDisplayedEnumValues?: number;
|
maxDisplayedEnumValues?: number;
|
||||||
ignoreNamedSchemas?: string[] | string;
|
ignoreNamedSchemas?: string[] | string;
|
||||||
hideSchemaPattern?: boolean;
|
hideSchemaPattern?: boolean;
|
||||||
generatedPayloadSamplesMaxDepth?: number;
|
generatedPayloadSamplesMaxDepth?: number; // remove in next major release
|
||||||
|
generatedSamplesMaxDepth?: number;
|
||||||
nonce?: string;
|
nonce?: string;
|
||||||
hideFab?: boolean;
|
hideFab?: boolean;
|
||||||
minCharacterLengthToInitSearch?: number;
|
minCharacterLengthToInitSearch?: number;
|
||||||
showWebhookVerb?: boolean;
|
showWebhookVerb?: boolean;
|
||||||
|
hidePropertiesPrefix?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
|
export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
|
||||||
|
@ -216,17 +230,18 @@ export class RedocNormalizedOptions {
|
||||||
scrollYOffset: () => number;
|
scrollYOffset: () => number;
|
||||||
hideHostname: boolean;
|
hideHostname: boolean;
|
||||||
expandResponses: { [code: string]: boolean } | 'all';
|
expandResponses: { [code: string]: boolean } | 'all';
|
||||||
requiredPropsFirst: boolean;
|
sortRequiredPropsFirst: boolean;
|
||||||
sortPropsAlphabetically: boolean;
|
sortPropsAlphabetically: boolean;
|
||||||
sortEnumValuesAlphabetically: boolean;
|
sortEnumValuesAlphabetically: boolean;
|
||||||
sortOperationsAlphabetically: boolean;
|
sortOperationsAlphabetically: boolean;
|
||||||
sortTagsAlphabetically: boolean;
|
sortTagsAlphabetically: boolean;
|
||||||
nativeScrollbars: boolean;
|
nativeScrollbars: boolean;
|
||||||
pathInMiddlePanel: boolean;
|
pathInMiddlePanel: boolean;
|
||||||
untrustedSpec: boolean;
|
sanitize: boolean;
|
||||||
hideDownloadButton: boolean;
|
hideDownloadButtons: boolean;
|
||||||
downloadFileName?: string;
|
downloadFileName?: string;
|
||||||
downloadDefinitionUrl?: string;
|
downloadDefinitionUrl?: string;
|
||||||
|
downloadUrls?: DownloadUrlsConfig;
|
||||||
disableSearch: boolean;
|
disableSearch: boolean;
|
||||||
onlyRequiredInSamples: boolean;
|
onlyRequiredInSamples: boolean;
|
||||||
showExtensions: boolean | string[];
|
showExtensions: boolean | string[];
|
||||||
|
@ -234,13 +249,14 @@ export class RedocNormalizedOptions {
|
||||||
hideSingleRequestSampleTab: boolean;
|
hideSingleRequestSampleTab: boolean;
|
||||||
hideRequestPayloadSample: boolean;
|
hideRequestPayloadSample: boolean;
|
||||||
menuToggle: boolean;
|
menuToggle: boolean;
|
||||||
jsonSampleExpandLevel: number;
|
jsonSamplesExpandLevel: number;
|
||||||
enumSkipQuotes: boolean;
|
enumSkipQuotes: boolean;
|
||||||
hideSchemaTitles: boolean;
|
hideSchemaTitles: boolean;
|
||||||
simpleOneOfTypeLabel: boolean;
|
simpleOneOfTypeLabel: boolean;
|
||||||
payloadSampleIdx: number;
|
payloadSampleIdx: number;
|
||||||
expandSingleSchemaField: boolean;
|
expandSingleSchemaField: boolean;
|
||||||
schemaExpansionLevel: number;
|
schemasExpansionLevel: number;
|
||||||
|
schemaDefinitionsTagName?: string;
|
||||||
showObjectSchemaExamples: boolean;
|
showObjectSchemaExamples: boolean;
|
||||||
showSecuritySchemeType?: boolean;
|
showSecuritySchemeType?: boolean;
|
||||||
hideSecuritySection?: boolean;
|
hideSecuritySection?: boolean;
|
||||||
|
@ -254,10 +270,11 @@ export class RedocNormalizedOptions {
|
||||||
|
|
||||||
ignoreNamedSchemas: Set<string>;
|
ignoreNamedSchemas: Set<string>;
|
||||||
hideSchemaPattern: boolean;
|
hideSchemaPattern: boolean;
|
||||||
generatedPayloadSamplesMaxDepth: number;
|
generatedSamplesMaxDepth: number;
|
||||||
hideFab: boolean;
|
hideFab: boolean;
|
||||||
minCharacterLengthToInitSearch: number;
|
minCharacterLengthToInitSearch: number;
|
||||||
showWebhookVerb: boolean;
|
showWebhookVerb: boolean;
|
||||||
|
hidePropertiesPrefix?: boolean;
|
||||||
|
|
||||||
nonce?: string;
|
nonce?: string;
|
||||||
|
|
||||||
|
@ -288,17 +305,20 @@ export class RedocNormalizedOptions {
|
||||||
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
|
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
|
||||||
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
|
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
|
||||||
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
|
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
|
||||||
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
|
this.sortRequiredPropsFirst = argValueToBoolean(
|
||||||
|
raw.sortRequiredPropsFirst || raw.requiredPropsFirst,
|
||||||
|
);
|
||||||
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
|
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
|
||||||
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
|
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
|
||||||
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
|
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
|
||||||
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
|
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
|
||||||
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
|
||||||
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
|
||||||
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
|
this.sanitize = argValueToBoolean(raw.sanitize || raw.untrustedSpec);
|
||||||
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
|
this.hideDownloadButtons = argValueToBoolean(raw.hideDownloadButtons || raw.hideDownloadButton);
|
||||||
this.downloadFileName = raw.downloadFileName;
|
this.downloadFileName = raw.downloadFileName;
|
||||||
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
|
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
|
||||||
|
this.downloadUrls = raw.downloadUrls;
|
||||||
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
this.disableSearch = argValueToBoolean(raw.disableSearch);
|
||||||
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
|
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
|
||||||
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
|
||||||
|
@ -306,15 +326,18 @@ export class RedocNormalizedOptions {
|
||||||
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
|
||||||
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
|
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
|
||||||
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
|
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
|
||||||
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
this.jsonSamplesExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
|
||||||
raw.jsonSampleExpandLevel,
|
raw.jsonSamplesExpandLevel || raw.jsonSampleExpandLevel,
|
||||||
);
|
);
|
||||||
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
|
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
|
||||||
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
|
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
|
||||||
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
|
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
|
||||||
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
|
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
|
||||||
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
|
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
|
||||||
this.schemaExpansionLevel = argValueToExpandLevel(raw.schemaExpansionLevel);
|
this.schemasExpansionLevel = argValueToExpandLevel(
|
||||||
|
raw.schemasExpansionLevel || raw.schemaExpansionLevel,
|
||||||
|
);
|
||||||
|
this.schemaDefinitionsTagName = raw.schemaDefinitionsTagName;
|
||||||
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
|
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
|
||||||
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
|
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
|
||||||
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);
|
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);
|
||||||
|
@ -330,13 +353,13 @@ export class RedocNormalizedOptions {
|
||||||
: raw.ignoreNamedSchemas?.split(',').map(s => s.trim());
|
: raw.ignoreNamedSchemas?.split(',').map(s => s.trim());
|
||||||
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
|
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
|
||||||
this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern);
|
this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern);
|
||||||
this.generatedPayloadSamplesMaxDepth =
|
this.generatedSamplesMaxDepth = RedocNormalizedOptions.normalizeGeneratedPayloadSamplesMaxDepth(
|
||||||
RedocNormalizedOptions.normalizeGeneratedPayloadSamplesMaxDepth(
|
raw.generatedSamplesMaxDepth || raw.generatedPayloadSamplesMaxDepth,
|
||||||
raw.generatedPayloadSamplesMaxDepth,
|
|
||||||
);
|
);
|
||||||
this.nonce = raw.nonce;
|
this.nonce = raw.nonce;
|
||||||
this.hideFab = argValueToBoolean(raw.hideFab);
|
this.hideFab = argValueToBoolean(raw.hideFab);
|
||||||
this.minCharacterLengthToInitSearch = argValueToNumber(raw.minCharacterLengthToInitSearch) || 3;
|
this.minCharacterLengthToInitSearch = argValueToNumber(raw.minCharacterLengthToInitSearch) || 3;
|
||||||
this.showWebhookVerb = argValueToBoolean(raw.showWebhookVerb);
|
this.showWebhookVerb = argValueToBoolean(raw.showWebhookVerb);
|
||||||
|
this.hidePropertiesPrefix = argValueToBoolean(raw.hidePropertiesPrefix, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { bind } from 'decko';
|
import { bind } from 'decko';
|
||||||
import * as EventEmitter from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
|
|
||||||
import { IS_BROWSER, querySelector, Throttle } from '../utils';
|
import { IS_BROWSER, querySelector, Throttle } from '../utils';
|
||||||
import type { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
import type { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Models Schema should correct resolve double $ref if no need sibling 1`] = `
|
exports[`Models Schema should correct resolve double $ref if no need sibling 1`] = `
|
||||||
Object {
|
{
|
||||||
"refsStack": Array [
|
"refsStack": [
|
||||||
"#/components/schemas/Parent",
|
"#/components/schemas/Parent",
|
||||||
],
|
],
|
||||||
"resolved": Object {
|
"resolved": {
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"test": Object {
|
"test": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -17,38 +17,46 @@ Object {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Models Schema should hoist oneOfs when mergin allOf 1`] = `
|
exports[`Models Schema should hoist oneOfs when mergin allOf 1`] = `
|
||||||
Object {
|
{
|
||||||
"oneOf": Array [
|
"oneOf": [
|
||||||
Object {
|
{
|
||||||
"allOf": Array [
|
"allOf": [
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"username": Object {
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"type": "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"username": {
|
||||||
"description": "The user's name",
|
"description": "The user's name",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"extra": Object {
|
"extra": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"oneOf": Array [
|
"oneOf": [
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"password": Object {
|
"password": {
|
||||||
"description": "The user's password",
|
"description": "The user's password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"mobile": Object {
|
"mobile": {
|
||||||
"description": "The user's mobile",
|
"description": "The user's mobile",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
@ -59,36 +67,93 @@ Object {
|
||||||
],
|
],
|
||||||
"x-refsStack": undefined,
|
"x-refsStack": undefined,
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"allOf": Array [
|
"allOf": [
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"email": Object {
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"type": "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"email": {
|
||||||
"description": "The user's email",
|
"description": "The user's email",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"extra": Object {
|
"extra": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"oneOf": Array [
|
"oneOf": [
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"password": Object {
|
"password": {
|
||||||
"description": "The user's password",
|
"description": "The user's password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"mobile": Object {
|
"mobile": {
|
||||||
|
"description": "The user's mobile",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"x-refsStack": undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"type": "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"format": "uuid",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"extra": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"description": "The user's password",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mobile": {
|
||||||
"description": "The user's mobile",
|
"description": "The user's mobile",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
@ -104,11 +169,11 @@ Object {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Models Schema should override description from $ref of the referenced component, when sibling description exists 1`] = `
|
exports[`Models Schema should override description from $ref of the referenced component, when sibling description exists 1`] = `
|
||||||
Object {
|
{
|
||||||
"refsStack": Array [
|
"refsStack": [
|
||||||
"#/components/schemas/Test",
|
"#/components/schemas/Test",
|
||||||
],
|
],
|
||||||
"resolved": Object {
|
"resolved": {
|
||||||
"description": "Overriden description",
|
"description": "Overriden description",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`prism.js helpers highlight js code 1`] = `"<span class=\\"token keyword\\">const</span> t <span class=\\"token operator\\">=</span> <span class=\\"token number\\">10</span><span class=\\"token punctuation\\">;</span>"`;
|
exports[`prism.js helpers highlight js code 1`] = `"<span class="token keyword">const</span> t <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span>"`;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"version": "1.0",
|
||||||
|
"title": "Test"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Test": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "test description",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "test description",
|
||||||
|
"enum": ["authorize", "do-nothing"],
|
||||||
|
"x-enumDescriptions": {
|
||||||
|
"authorize-and-void": "Will create an authorize transaction in the amount/currency of the request, followed by a void",
|
||||||
|
"do-nothing": "Will do nothing, and return an approved `setup` transaction. This is the default behavior."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,12 @@
|
||||||
"test": {
|
"test": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -25,6 +31,15 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The user's ID",
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -139,10 +139,18 @@ describe('Models', () => {
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const opts = new RedocNormalizedOptions({
|
const opts = new RedocNormalizedOptions({
|
||||||
downloadDefinitionUrl: 'https:test.com/filename.yaml',
|
downloadUrls: [{ title: 'Openapi description', url: 'https:test.com/filename.yaml' }],
|
||||||
});
|
});
|
||||||
const info = new ApiInfoModel(parser, opts);
|
const info = new ApiInfoModel(parser, opts);
|
||||||
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
|
expect(info.downloadUrls).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Openapi description",
|
||||||
|
"url": "https:test.com/filename.yaml",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
expect(info.downloadFileName).toMatchInlineSnapshot(`"openapi.json"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should correctly populate download link and download file name', () => {
|
test('should correctly populate download link and download file name', () => {
|
||||||
|
@ -158,8 +166,29 @@ describe('Models', () => {
|
||||||
downloadFileName: 'test.yaml',
|
downloadFileName: 'test.yaml',
|
||||||
});
|
});
|
||||||
const info = new ApiInfoModel(parser, opts);
|
const info = new ApiInfoModel(parser, opts);
|
||||||
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
|
expect(info.downloadUrls).toMatchInlineSnapshot(`
|
||||||
expect(info.downloadFileName).toEqual('test.yaml');
|
[
|
||||||
|
{
|
||||||
|
"title": "Download",
|
||||||
|
"url": "https:test.com/filename.yaml",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
expect(info.downloadFileName).toMatchInlineSnapshot(`"test.yaml"`);
|
||||||
|
|
||||||
|
const opts2 = new RedocNormalizedOptions({
|
||||||
|
downloadUrls: [{ title: 'Download file', url: 'https:test.com/filename.yaml' }],
|
||||||
|
});
|
||||||
|
const info2 = new ApiInfoModel(parser, opts2);
|
||||||
|
expect(info2.downloadUrls).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Download file",
|
||||||
|
"url": "https:test.com/filename.yaml",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
expect(info2.downloadFileName).toMatchInlineSnapshot(`"openapi.json"`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,17 @@ describe('Models', () => {
|
||||||
describe('Schema', () => {
|
describe('Schema', () => {
|
||||||
let parser;
|
let parser;
|
||||||
|
|
||||||
|
test('parsing nested x-enumDescription', () => {
|
||||||
|
const spec = require('../fixtures/nestedEnumDescroptionSample.json');
|
||||||
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
const testSchema = spec.components.schemas.Test;
|
||||||
|
const schemaModel = new SchemaModel(parser, testSchema, '', opts);
|
||||||
|
|
||||||
|
expect(schemaModel['x-enumDescriptions']).toStrictEqual(
|
||||||
|
testSchema.items['x-enumDescriptions'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('discriminator with one field', () => {
|
test('discriminator with one field', () => {
|
||||||
const spec = require('../fixtures/discriminator.json');
|
const spec = require('../fixtures/discriminator.json');
|
||||||
parser = new OpenAPIParser(spec, undefined, opts);
|
parser = new OpenAPIParser(spec, undefined, opts);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Models Schema schemaDefinition should resolve field with conditional operators 1`] = `
|
exports[`Models Schema schemaDefinition should resolve field with conditional operators 1`] = `
|
||||||
Object {
|
{
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"items": Object {
|
"items": {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"format": "url",
|
"format": "url",
|
||||||
|
@ -14,7 +14,7 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
},
|
},
|
||||||
"maxItems": 20,
|
"maxItems": 20,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
|
@ -24,16 +24,16 @@ Object {
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-displayName": "isString",
|
"x-displayName": "isString",
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Models Schema schemaDefinition should resolve field with conditional operators 2`] = `
|
exports[`Models Schema schemaDefinition should resolve field with conditional operators 2`] = `
|
||||||
Object {
|
{
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"default": undefined,
|
"default": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"items": Object {
|
"items": {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"format": "url",
|
"format": "url",
|
||||||
|
@ -42,14 +42,14 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
},
|
},
|
||||||
"maxItems": 10,
|
"maxItems": 10,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"pattern": "\\\\d+",
|
"pattern": "\\d+",
|
||||||
"readOnly": undefined,
|
"readOnly": undefined,
|
||||||
"title": "notString",
|
"title": "notString",
|
||||||
"type": Array [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"integer",
|
"integer",
|
||||||
"null",
|
"null",
|
||||||
|
@ -57,23 +57,23 @@ Object {
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-displayName": "notString",
|
"x-displayName": "notString",
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 1`] = `
|
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 1`] = `
|
||||||
Object {
|
{
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"maxItems": 2,
|
"maxItems": 2,
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"test": Object {
|
"test": {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": "The list of URL to a cute photos featuring pet",
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
"enum": Array [
|
"enum": [
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"items": Object {
|
"items": {
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"format": "url",
|
"format": "url",
|
||||||
|
@ -82,21 +82,21 @@ Object {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
},
|
},
|
||||||
"maxItems": 20,
|
"maxItems": 20,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"readOnly": undefined,
|
"readOnly": undefined,
|
||||||
"title": undefined,
|
"title": undefined,
|
||||||
"type": Array [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"integer",
|
"integer",
|
||||||
"null",
|
"null",
|
||||||
],
|
],
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
"x-refsStack": Array [
|
"x-refsStack": [
|
||||||
"/oneOf/0",
|
"/oneOf/0",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -106,30 +106,30 @@ Object {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 2`] = `
|
exports[`Models Schema schemaDefinition should resolve schema with conditional operators 2`] = `
|
||||||
Object {
|
{
|
||||||
"allOf": undefined,
|
"allOf": undefined,
|
||||||
"description": undefined,
|
"description": undefined,
|
||||||
"maxItems": 20,
|
"maxItems": 20,
|
||||||
"properties": Object {
|
"properties": {
|
||||||
"test": Object {
|
"test": {
|
||||||
"description": "The list of URL to a cute photos featuring pet",
|
"description": "The list of URL to a cute photos featuring pet",
|
||||||
"items": Object {
|
"items": {
|
||||||
"format": "url",
|
"format": "url",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"maxItems": 20,
|
"maxItems": 20,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"type": Array [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
"integer",
|
"integer",
|
||||||
"null",
|
"null",
|
||||||
],
|
],
|
||||||
"x-refsStack": Array [
|
"x-refsStack": [
|
||||||
"/oneOf/1",
|
"/oneOf/1",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -139,6 +139,6 @@ Object {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"writeOnly": undefined,
|
"writeOnly": undefined,
|
||||||
"x-circular-ref": undefined,
|
"x-circular-ref": undefined,
|
||||||
"x-parentRefs": Array [],
|
"x-parentRefs": [],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
|
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
|
||||||
import { IS_BROWSER } from '../../utils/';
|
import { IS_BROWSER } from '../../utils/';
|
||||||
|
import { l } from '../Labels';
|
||||||
import type { OpenAPIParser } from '../OpenAPIParser';
|
import type { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
|
|
||||||
|
@ -13,7 +14,10 @@ export class ApiInfoModel implements OpenAPIInfo {
|
||||||
contact?: OpenAPIContact;
|
contact?: OpenAPIContact;
|
||||||
license?: OpenAPILicense;
|
license?: OpenAPILicense;
|
||||||
|
|
||||||
downloadLink?: string;
|
downloadUrls: {
|
||||||
|
title?: string;
|
||||||
|
url?: string;
|
||||||
|
}[];
|
||||||
downloadFileName?: string;
|
downloadFileName?: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -29,13 +33,28 @@ export class ApiInfoModel implements OpenAPIInfo {
|
||||||
this.description = this.description.substring(0, firstHeadingLinePos);
|
this.description = this.description.substring(0, firstHeadingLinePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.downloadLink = this.getDownloadLink();
|
this.downloadUrls = this.getDownloadUrls();
|
||||||
this.downloadFileName = this.getDownloadFileName();
|
this.downloadFileName = this.getDownloadFileName();
|
||||||
}
|
}
|
||||||
|
private getDownloadUrls() {
|
||||||
|
return (
|
||||||
|
!this.options.downloadUrls
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: l('download'),
|
||||||
|
url: this.getDownloadLink(this.options.downloadDefinitionUrl),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: this.options.downloadUrls.map(({ title, url }) => ({
|
||||||
|
title: title || l('download'),
|
||||||
|
url: this.getDownloadLink(url),
|
||||||
|
}))
|
||||||
|
).filter(({ title, url }) => title && url);
|
||||||
|
}
|
||||||
|
|
||||||
private getDownloadLink(): string | undefined {
|
private getDownloadLink(url?: string): string | undefined {
|
||||||
if (this.options.downloadDefinitionUrl) {
|
if (url) {
|
||||||
return this.options.downloadDefinitionUrl;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parser.specUrl) {
|
if (this.parser.specUrl) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class MediaTypeModel {
|
||||||
name: string;
|
name: string;
|
||||||
isRequestType: boolean;
|
isRequestType: boolean;
|
||||||
onlyRequiredInSamples: boolean;
|
onlyRequiredInSamples: boolean;
|
||||||
generatedPayloadSamplesMaxDepth: number;
|
generatedSamplesMaxDepth: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param isRequestType needed to know if skipe RO/RW fields in objects
|
* @param isRequestType needed to know if skipe RO/RW fields in objects
|
||||||
|
@ -30,7 +30,7 @@ export class MediaTypeModel {
|
||||||
this.isRequestType = isRequestType;
|
this.isRequestType = isRequestType;
|
||||||
this.schema = info.schema && new SchemaModel(parser, info.schema, '', options);
|
this.schema = info.schema && new SchemaModel(parser, info.schema, '', options);
|
||||||
this.onlyRequiredInSamples = options.onlyRequiredInSamples;
|
this.onlyRequiredInSamples = options.onlyRequiredInSamples;
|
||||||
this.generatedPayloadSamplesMaxDepth = options.generatedPayloadSamplesMaxDepth;
|
this.generatedSamplesMaxDepth = options.generatedSamplesMaxDepth;
|
||||||
if (info.examples !== undefined) {
|
if (info.examples !== undefined) {
|
||||||
this.examples = mapValues(
|
this.examples = mapValues(
|
||||||
info.examples,
|
info.examples,
|
||||||
|
@ -55,7 +55,7 @@ export class MediaTypeModel {
|
||||||
skipReadOnly: this.isRequestType,
|
skipReadOnly: this.isRequestType,
|
||||||
skipWriteOnly: !this.isRequestType,
|
skipWriteOnly: !this.isRequestType,
|
||||||
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
|
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
|
||||||
maxSampleDepth: this.generatedPayloadSamplesMaxDepth,
|
maxSampleDepth: this.generatedSamplesMaxDepth,
|
||||||
};
|
};
|
||||||
if (this.schema && this.schema.oneOf) {
|
if (this.schema && this.schema.oneOf) {
|
||||||
this.examples = {};
|
this.examples = {};
|
||||||
|
|
|
@ -20,7 +20,12 @@ import { RequestBodyModel } from './RequestBody';
|
||||||
import { ResponseModel } from './Response';
|
import { ResponseModel } from './Response';
|
||||||
import { SideNavStyleEnum } from '../types';
|
import { SideNavStyleEnum } from '../types';
|
||||||
|
|
||||||
import type { OpenAPIExternalDocumentation, OpenAPIServer, OpenAPIXCodeSample } from '../../types';
|
import type {
|
||||||
|
OpenAPIExternalDocumentation,
|
||||||
|
OpenAPIServer,
|
||||||
|
OpenAPIXBadges,
|
||||||
|
OpenAPIXCodeSample,
|
||||||
|
} from '../../types';
|
||||||
import type { OpenAPIParser } from '../OpenAPIParser';
|
import type { OpenAPIParser } from '../OpenAPIParser';
|
||||||
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import type { MediaContentModel } from './MediaContent';
|
import type { MediaContentModel } from './MediaContent';
|
||||||
|
@ -71,6 +76,7 @@ export class OperationModel implements IMenuItem {
|
||||||
operationId?: string;
|
operationId?: string;
|
||||||
operationHash?: string;
|
operationHash?: string;
|
||||||
httpVerb: string;
|
httpVerb: string;
|
||||||
|
badges: OpenAPIXBadges[];
|
||||||
deprecated: boolean;
|
deprecated: boolean;
|
||||||
path: string;
|
path: string;
|
||||||
servers: OpenAPIServer[];
|
servers: OpenAPIServer[];
|
||||||
|
@ -112,6 +118,12 @@ export class OperationModel implements IMenuItem {
|
||||||
: options.sideNavStyle === SideNavStyleEnum.PathOnly
|
: options.sideNavStyle === SideNavStyleEnum.PathOnly
|
||||||
? this.path
|
? this.path
|
||||||
: this.name;
|
: this.name;
|
||||||
|
this.badges =
|
||||||
|
operationSpec['x-badges']?.map(({ name, color, position }) => ({
|
||||||
|
name,
|
||||||
|
color: color,
|
||||||
|
position: position || 'after',
|
||||||
|
})) || [];
|
||||||
|
|
||||||
if (this.isCallback) {
|
if (this.isCallback) {
|
||||||
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
|
||||||
|
@ -235,7 +247,7 @@ export class OperationModel implements IMenuItem {
|
||||||
if (this.options.sortPropsAlphabetically) {
|
if (this.options.sortPropsAlphabetically) {
|
||||||
return sortByField(_parameters, 'name');
|
return sortByField(_parameters, 'name');
|
||||||
}
|
}
|
||||||
if (this.options.requiredPropsFirst) {
|
if (this.options.sortRequiredPropsFirst) {
|
||||||
return sortByRequired(_parameters);
|
return sortByRequired(_parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ export class SchemaModel {
|
||||||
rawSchema: OpenAPISchema;
|
rawSchema: OpenAPISchema;
|
||||||
schema: MergedOpenAPISchema;
|
schema: MergedOpenAPISchema;
|
||||||
extensions?: Record<string, any>;
|
extensions?: Record<string, any>;
|
||||||
|
'x-enumDescriptions': { [name: string]: string };
|
||||||
const: any;
|
const: any;
|
||||||
contentEncoding?: string;
|
contentEncoding?: string;
|
||||||
contentMediaType?: string;
|
contentMediaType?: string;
|
||||||
|
@ -122,6 +123,7 @@ export class SchemaModel {
|
||||||
this.type = schema.type || detectType(schema);
|
this.type = schema.type || detectType(schema);
|
||||||
this.format = schema.format;
|
this.format = schema.format;
|
||||||
this.enum = schema.enum || [];
|
this.enum = schema.enum || [];
|
||||||
|
this['x-enumDescriptions'] = schema['x-enumDescriptions'];
|
||||||
this.example = schema.example;
|
this.example = schema.example;
|
||||||
this.examples = schema.examples;
|
this.examples = schema.examples;
|
||||||
this.deprecated = !!schema.deprecated;
|
this.deprecated = !!schema.deprecated;
|
||||||
|
@ -221,6 +223,7 @@ export class SchemaModel {
|
||||||
}
|
}
|
||||||
if (this.items?.isPrimitive) {
|
if (this.items?.isPrimitive) {
|
||||||
this.enum = this.items.enum;
|
this.enum = this.items.enum;
|
||||||
|
this['x-enumDescriptions'] = this.items['x-enumDescriptions'];
|
||||||
}
|
}
|
||||||
if (isArray(this.type)) {
|
if (isArray(this.type)) {
|
||||||
const filteredType = this.type.filter(item => item !== 'array');
|
const filteredType = this.type.filter(item => item !== 'array');
|
||||||
|
@ -463,7 +466,7 @@ function buildFields(
|
||||||
if (options.sortPropsAlphabetically) {
|
if (options.sortPropsAlphabetically) {
|
||||||
fields = sortByField(fields, 'name');
|
fields = sortByField(fields, 'name');
|
||||||
}
|
}
|
||||||
if (options.requiredPropsFirst) {
|
if (options.sortRequiredPropsFirst) {
|
||||||
// if not sort alphabetically sort in the order from required keyword
|
// if not sort alphabetically sort in the order from required keyword
|
||||||
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
|
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import * as Enzyme from 'enzyme';
|
import * as Enzyme from 'enzyme';
|
||||||
import * as Adapter from '@wojtekmaj/enzyme-adapter-react-17';
|
import Adapter from '@cfaester/enzyme-adapter-react-18';
|
||||||
|
import { TextEncoder, TextDecoder } from 'util';
|
||||||
|
|
||||||
|
Object.assign(global, { TextDecoder, TextEncoder });
|
||||||
|
|
||||||
import 'raf/polyfill';
|
import 'raf/polyfill';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { hydrate as hydrateComponent, render, unmountComponentAtNode } from 'react-dom';
|
import { createRoot, hydrateRoot } from 'react-dom/client';
|
||||||
import { configure } from 'mobx';
|
import { configure } from 'mobx';
|
||||||
|
|
||||||
import { Redoc, RedocStandalone } from './components/';
|
import { Redoc, RedocStandalone } from './components/';
|
||||||
|
@ -59,7 +59,8 @@ export function init(
|
||||||
spec = specOrSpecUrl;
|
spec = specOrSpecUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
const root = createRoot(element!);
|
||||||
|
root.render(
|
||||||
React.createElement(
|
React.createElement(
|
||||||
RedocStandalone,
|
RedocStandalone,
|
||||||
{
|
{
|
||||||
|
@ -70,13 +71,12 @@ export function init(
|
||||||
},
|
},
|
||||||
['Loading...'],
|
['Loading...'],
|
||||||
),
|
),
|
||||||
element,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function destroy(element: Element | null = querySelector('redoc')): void {
|
export function destroy(element: Element | null = querySelector('redoc')): void {
|
||||||
if (element) {
|
if (element) {
|
||||||
unmountComponentAtNode(element);
|
createRoot(element).unmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ export function hydrate(
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
debugTime('Redoc hydrate');
|
debugTime('Redoc hydrate');
|
||||||
hydrateComponent(<Redoc store={store} />, element, callback);
|
hydrateRoot(element!, <Redoc store={store} />, { onRecoverableError: callback });
|
||||||
debugTimeEnd('Redoc hydrate');
|
debugTimeEnd('Redoc hydrate');
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,12 @@ export interface OpenAPIXCodeSample {
|
||||||
source: string;
|
source: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OpenAPIXBadges {
|
||||||
|
name: string;
|
||||||
|
color?: string;
|
||||||
|
position?: 'before' | 'after';
|
||||||
|
}
|
||||||
|
|
||||||
export interface OpenAPIOperation {
|
export interface OpenAPIOperation {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
summary?: string;
|
summary?: string;
|
||||||
|
@ -85,6 +91,7 @@ export interface OpenAPIOperation {
|
||||||
servers?: OpenAPIServer[];
|
servers?: OpenAPIServer[];
|
||||||
'x-codeSamples'?: OpenAPIXCodeSample[];
|
'x-codeSamples'?: OpenAPIXCodeSample[];
|
||||||
'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated
|
'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated
|
||||||
|
'x-badges'?: OpenAPIXBadges[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenAPIParameter {
|
export interface OpenAPIParameter {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -71,6 +71,30 @@ describe('Utils', () => {
|
||||||
const obj2 = { a: ['C'], b: ['D'] };
|
const obj2 = { a: ['C'], b: ['D'] };
|
||||||
expect(mergeObjects({}, obj1, obj2)).toEqual({ a: ['C'], b: ['D'] });
|
expect(mergeObjects({}, obj1, obj2)).toEqual({ a: ['C'], b: ['D'] });
|
||||||
});
|
});
|
||||||
|
test('should prevent prototype pollution', () => {
|
||||||
|
const target = {};
|
||||||
|
const source = JSON.parse('{"__proto__": {"polluted": "yes"}}');
|
||||||
|
|
||||||
|
mergeObjects(target, source);
|
||||||
|
|
||||||
|
expect(({} as any).polluted).toBeUndefined();
|
||||||
|
});
|
||||||
|
test('should merge objects correctly', () => {
|
||||||
|
const target = { a: 1 };
|
||||||
|
const source = { b: 2 };
|
||||||
|
|
||||||
|
const result = mergeObjects(target, source);
|
||||||
|
|
||||||
|
expect(result).toEqual({ a: 1, b: 2 });
|
||||||
|
});
|
||||||
|
test('should handle nested objects', () => {
|
||||||
|
const target = { a: { b: 1 } };
|
||||||
|
const source = { a: { c: 2 } };
|
||||||
|
|
||||||
|
const result = mergeObjects(target, source);
|
||||||
|
|
||||||
|
expect(result).toEqual({ a: { b: 1, c: 2 } });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('titleize', () => {
|
describe('titleize', () => {
|
||||||
|
|
|
@ -15,6 +15,7 @@ function throttle(func, wait) {
|
||||||
return function () {
|
return function () {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const remaining = wait - (now - previous);
|
const remaining = wait - (now - previous);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
context = this;
|
context = this;
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
args = arguments;
|
args = arguments;
|
||||||
|
|
|
@ -81,7 +81,6 @@ export function appendToMdHeading(md: string, heading: string, content: string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// credits https://stackoverflow.com/a/46973278/1749888
|
|
||||||
export const mergeObjects = (target: any, ...sources: any[]): any => {
|
export const mergeObjects = (target: any, ...sources: any[]): any => {
|
||||||
if (!sources.length) {
|
if (!sources.length) {
|
||||||
return target;
|
return target;
|
||||||
|
@ -93,6 +92,7 @@ export const mergeObjects = (target: any, ...sources: any[]): any => {
|
||||||
|
|
||||||
if (isMergebleObject(target) && isMergebleObject(source)) {
|
if (isMergebleObject(target) && isMergebleObject(source)) {
|
||||||
Object.keys(source).forEach((key: string) => {
|
Object.keys(source).forEach((key: string) => {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, key) && key !== '__proto__') {
|
||||||
if (isMergebleObject(source[key])) {
|
if (isMergebleObject(source[key])) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
|
@ -101,6 +101,7 @@ export const mergeObjects = (target: any, ...sources: any[]): any => {
|
||||||
} else {
|
} else {
|
||||||
target[key] = source[key];
|
target[key] = source[key];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -393,7 +393,7 @@ export function getSerializedValue(field: FieldModel, example: any) {
|
||||||
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
|
// decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
|
||||||
return decodeURIComponent(serializeParameterValue(field, example));
|
return decodeURIComponent(serializeParameterValue(field, example));
|
||||||
} else {
|
} else {
|
||||||
return String(example);
|
return typeof example === 'object' ? example : String(example);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,12 +654,13 @@ export function isRedocExtension(key: string): boolean {
|
||||||
'x-codeSamples': true,
|
'x-codeSamples': true,
|
||||||
'x-displayName': true,
|
'x-displayName': true,
|
||||||
'x-examples': true,
|
'x-examples': true,
|
||||||
'x-ignoredHeaderParameters': true,
|
'x-enumDescriptions': true,
|
||||||
'x-logo': true,
|
'x-logo': true,
|
||||||
'x-nullable': true,
|
'x-nullable': true,
|
||||||
'x-servers': true,
|
'x-servers': true,
|
||||||
'x-tagGroups': true,
|
'x-tagGroups': true,
|
||||||
'x-traitTag': true,
|
'x-traitTag': true,
|
||||||
|
'x-badges': true,
|
||||||
'x-additionalPropertiesName': true,
|
'x-additionalPropertiesName': true,
|
||||||
'x-explicitMappingOnly': true,
|
'x-explicitMappingOnly': true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
'use strict';
|
|
||||||
const verifyNoBrowserErrors = require('./helpers').verifyNoBrowserErrors;
|
|
||||||
const scrollToEl = require('./helpers').scrollToEl;
|
|
||||||
const fixFFTest = require('./helpers').fixFFTest;
|
|
||||||
const eachNth = require('./helpers').eachNth;
|
|
||||||
const getInnerHtml = require('./helpers').getInnerHtml;
|
|
||||||
|
|
||||||
const URL = 'index.html';
|
|
||||||
|
|
||||||
function waitForInit() {
|
|
||||||
const EC = protractor.ExpectedConditions;
|
|
||||||
const $apiInfo = $('api-info');
|
|
||||||
const $errorMessage = $('.redoc-error');
|
|
||||||
browser.wait(EC.or(EC.visibilityOf($apiInfo), EC.visibilityOf($errorMessage)), 60000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function basicTests(swaggerUrl, title) {
|
|
||||||
describe(`Basic suite for ${title}`, () => {
|
|
||||||
let specUrl = URL;
|
|
||||||
if (swaggerUrl) {
|
|
||||||
specUrl += `?url=${encodeURIComponent(swaggerUrl)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(done => {
|
|
||||||
browser.get(specUrl);
|
|
||||||
waitForInit();
|
|
||||||
fixFFTest(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
verifyNoBrowserErrors();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should init redoc without errors', done => {
|
|
||||||
const $redoc = $('redoc');
|
|
||||||
expect($redoc.isPresent()).toBe(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
const $operations = $$('operation');
|
|
||||||
expect($operations.count()).toBeGreaterThan(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
basicTests(null, 'Extended Petstore');
|
|
||||||
|
|
||||||
describe('Scroll sync', () => {
|
|
||||||
const specUrl = URL;
|
|
||||||
|
|
||||||
beforeEach(done => {
|
|
||||||
browser.get(specUrl);
|
|
||||||
waitForInit();
|
|
||||||
fixFFTest(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update active menu entries on page scroll forwards', () => {
|
|
||||||
scrollToEl('[section="tag/store"]').then(() => {
|
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-1.active > .menu-item-header')).toContain(
|
|
||||||
'store',
|
|
||||||
);
|
|
||||||
expect(getInnerHtml('.selected-tag')).toContain('store');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update active menu entries on page scroll backwards', () => {
|
|
||||||
scrollToEl('[operation-id="getPetById"]').then(() => {
|
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-1.active .menu-item-header')).toContain(
|
|
||||||
'pet',
|
|
||||||
);
|
|
||||||
expect(getInnerHtml('.selected-tag')).toContain('pet');
|
|
||||||
expect(getInnerHtml('.menu-item.menu-item-depth-2.active .menu-item-header')).toContain(
|
|
||||||
'Find pet by ID',
|
|
||||||
);
|
|
||||||
expect(getInnerHtml('.selected-endpoint')).toContain('Find pet by ID');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Language tabs sync', () => {
|
|
||||||
const specUrl = URL;
|
|
||||||
|
|
||||||
beforeEach(done => {
|
|
||||||
browser.get(specUrl);
|
|
||||||
waitForInit();
|
|
||||||
fixFFTest(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
// skip as it fails for no reason on IE on sauce-labs
|
|
||||||
// TODO: fixme
|
|
||||||
xit('should sync language tabs', () => {
|
|
||||||
const $item = $$('[operation-id="addPet"] tabs > ul > li').last();
|
|
||||||
// check if correct item
|
|
||||||
expect($item.getText()).toContain('PHP');
|
|
||||||
const EC = protractor.ExpectedConditions;
|
|
||||||
browser.wait(EC.elementToBeClickable($item), 5000);
|
|
||||||
$item.click().then(() => {
|
|
||||||
expect($('[operation-id="updatePet"] li.active').getText()).toContain('PHP');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.env.JOB === 'e2e-guru') {
|
|
||||||
describe('APIs.guru specs test', () => {
|
|
||||||
// global.apisGuruList was loaded in onPrepare method of protractor config
|
|
||||||
let apisGuruList = global.apisGuruList;
|
|
||||||
|
|
||||||
// Remove certain APIs that are known to cause problems
|
|
||||||
delete apisGuruList['motaword.com']; // invalid (see https://github.com/BigstickCarpet/swagger-parser/issues/26)
|
|
||||||
delete apisGuruList['learnifier.com']; // allof object and no type
|
|
||||||
delete apisGuruList['googleapis.com:mirror']; // bad urls in images
|
|
||||||
delete apisGuruList['googleapis.com:discovery']; // non-string references
|
|
||||||
delete apisGuruList['clarify.io']; // non-string references
|
|
||||||
//delete apisGuruList['pushpay.com']; // https://github.com/Redocly/redoc/issues/30
|
|
||||||
delete apisGuruList['bbci.co.uk']; // too big
|
|
||||||
delete apisGuruList['bbc.com']; // too big
|
|
||||||
delete apisGuruList['osisoft.com']; // too big
|
|
||||||
delete apisGuruList['magento.com']; // too big
|
|
||||||
|
|
||||||
// run quick version of e2e test on all builds except releases
|
|
||||||
if (process.env.TRAVIS && !process.env.TRAVIS_TAG) {
|
|
||||||
console.log('Running on a short APIs guru list');
|
|
||||||
apisGuruList = eachNth(apisGuruList, 20);
|
|
||||||
} else {
|
|
||||||
console.log('Running on full APIs guru list');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const apiName of Object.keys(apisGuruList)) {
|
|
||||||
const apiInfo = apisGuruList[apiName].versions[apisGuruList[apiName].preferred];
|
|
||||||
let url = apiInfo.swaggerUrl;
|
|
||||||
|
|
||||||
// temporary hack due to this issue: https://github.com/substack/https-browserify/issues/6
|
|
||||||
url = url.replace('https://', 'http://');
|
|
||||||
url = url.replace('apis-guru.github.io/', 'apis-guru.github.io:80/');
|
|
||||||
basicTests(url, `${apiName}:${apiInfo.info.version}\n${url}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user