Compare commits

..

No commits in common. "main" and "v2.1.5" have entirely different histories.
main ... v2.1.5

56 changed files with 1932 additions and 2349 deletions

View File

@ -21,7 +21,7 @@ jobs:
- run: npm ci
- run: npm run bundle
- name: Store bundle artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: bundles
path: bundles
@ -39,7 +39,7 @@ jobs:
- uses: actions/checkout@v3
- run: npm ci
- name: Download bundled artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: bundles
path: bundles
@ -73,7 +73,7 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- uses: actions/checkout@v3
- name: Download bundled artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: bundles
path: bundles
@ -107,7 +107,7 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Download all artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
- name: Publish to S3
run: npm run publish-cdn

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

@ -0,0 +1,18 @@
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

View File

@ -1,62 +1,3 @@
# [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)

View File

@ -116,14 +116,15 @@ 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-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-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-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-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-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-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
## Releases

View File

@ -5,7 +5,7 @@
# npm i -g http-server
# http-server -p 8000 --cors
FROM node:18-alpine
FROM node:12-alpine
RUN apk update && apk add --no-cache git
@ -13,7 +13,6 @@ RUN apk update && apk add --no-cache git
WORKDIR /build
COPY package.json package-lock.json /build/
RUN npm ci --no-optional --ignore-scripts
RUN npm explore esbuild -- npm run postinstall
# copy only required for the build files
COPY src /build/src

View File

@ -122,7 +122,7 @@ class DemoApp extends React.Component<
<RedocStandalone
spec={this.state.spec}
specUrl={proxiedUrl}
options={{ scrollYOffset: 'nav', sanitize: true }}
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
/>
</>
);

View File

@ -22,10 +22,6 @@ paths:
operationId: getMuseumHours
tags:
- Operations
x-badges:
- name: 'Beta'
position: before
color: purple
parameters:
- $ref: '#/components/parameters/StartDate'
- $ref: '#/components/parameters/PaginationPage'
@ -68,9 +64,6 @@ paths:
summary: Create special event
tags:
- Events
x-badges:
- name: 'Alpha'
color: purple
requestBody:
required: true
content:
@ -99,8 +92,6 @@ paths:
description: Return a list of upcoming special events at the museum.
security: []
operationId: listSpecialEvents
x-badges:
- name: 'Gamma'
tags:
- Events
parameters:
@ -309,9 +300,6 @@ components:
enum:
- event
- general
x-enumDescriptions:
event: Special event ticket
general: General museum entry ticket
example: event
Date:
type: string
@ -779,9 +767,6 @@ x-tagGroups:
- name: Purchases
tags:
- Tickets
- name: Entities
tags:
- Schemas
security:
- MuseumPlaceholderAuth: []

View File

@ -106,10 +106,6 @@ paths:
post:
tags:
- pet
x-badges:
- name: 'Beta'
position: before
color: purple
summary: Add a new pet to the store
description: Add new pet to the store inventory.
operationId: addPet
@ -154,9 +150,6 @@ paths:
put:
tags:
- pet
x-badges:
- name: 'Alpha'
color: purple
summary: Update an existing pet
description: ''
operationId: updatePet
@ -190,8 +183,6 @@ paths:
get:
tags:
- pet
x-badges:
- name: 'Gamma'
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
@ -1083,10 +1074,6 @@ components:
- available
- pending
- sold
x-enumDescriptions:
available: Available status
pending: Pending status
sold: Sold status
petType:
description: Type of a pet
type: string

View File

@ -11,11 +11,7 @@ const userUrl = window.location.search.match(/url=(.*)$/);
const specUrl =
(userUrl && userUrl[1]) || (swagger ? 'museum.yaml' : big ? 'big-openapi.json' : 'museum.yaml');
const options: RedocRawOptions = {
nativeScrollbars: false,
maxDisplayedEnumValues: 3,
schemaDefinitionsTagName: 'schemas',
};
const options: RedocRawOptions = { nativeScrollbars: false, maxDisplayedEnumValues: 3 };
const container = document.getElementById('example');
const root = createRoot(container!);

View File

@ -26,19 +26,59 @@ Sets the minimum amount of characters that need to be typed into the search dial
_Default: 3_
### hideDownloadButtons
### 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.
### 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.
### hideHostname
If set to `true`, the protocol and hostname are not shown in the operation definition.
### hideLoading
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
Hides the schema title next to to the type.
### jsonSamplesExpandLevel
### hideSecuritySection
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.
@ -48,17 +88,35 @@ _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.
### 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
Shows only required fields in request samples.
### sortRequiredPropsFirst
### 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.
### requiredPropsFirst
Shows required properties in schemas first, ordered in the same order as in the required array.
### schemasExpansionLevel
### 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, `schemasExpansionLevel: 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, `schemaExpansionLevel: 3` expands schemas up to three levels deep. The default value is `0`, meaning no schemas are expanded automatically.
### scrollYOffset
@ -74,111 +132,6 @@ 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.
### 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
Shows object schema example in the properties; default `false`.
@ -209,12 +162,12 @@ When set to true, sorts tags in the navigation sidebar and in the middle panel a
_Default: false_
### untrustedSpec
### untrustedDefinition
If set to `true`, the API definition is considered untrusted and all HTML/Markdown is sanitized to prevent XSS.
## Theme settings
Change styles for the API documentation page. **Supported in Redoc CE 2.x**.
* `spacing`
* `unit`: 5 # main spacing unit used in autocomputed theme values later
* `sectionHorizontal`: 40 # Horizontal section padding. COMPUTED: spacing.unit * 8
@ -295,7 +248,7 @@ For more information, refer to [Security definitions injection](./security-defin
### OpenAPI specification extensions
Redoc uses the following [specification extensions](https://redocly.com/docs-legacy/api-reference-docs/spec-extensions/):
Redoc uses the following [specification extensions](https://redocly.com/docs/api-reference-docs/spec-extensions/):
* [`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-codeSamples`](./redoc-vendor-extensions.md#x-codesamples) - specify operation code samples
@ -304,8 +257,10 @@ Redoc uses the following [specification extensions](https://redocly.com/docs-leg
* [`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-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-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

View File

@ -6,7 +6,7 @@ seo:
# How to use the Redocly CLI
With Redocly CLI, you can bundle your OpenAPI definition and API documentation
(made with Redoc) into an HTML file and render it locally.
(made with Redoc) into a zero-dependency HTML file and render it locally.
## Step 1 - Install Redocly CLI
@ -18,9 +18,9 @@ Or you can install it during [runtime](../../cli/installation.md#use-npx-at-runt
## Step 2 - Build the HTML file
The Redocly CLI `build-docs` command builds Redoc into an HTML file.
The Redocly CLI `build-docs` command builds Redoc into a zero-dependency HTML file.
To build an HTML file using Redocly CLI, enter the following command,
To build a zero-dependency HTML file using Redocly CLI, enter the following command,
replacing `apis/openapi.yaml` with your API definition file's name and path:
```bash

View File

@ -104,7 +104,7 @@ npm install -g http-server
Then, `cd` into your project directory and run the following command:
```node
http-server
http - server;
```
The output after entering the command provides the local URL where the preview can be accessed.

View File

@ -67,7 +67,7 @@ theme:
openapi:
disableSearch: true
expandResponses: 200,202
jsonSamplesExpandLevel: 1
jsonSampleExpandLevel: 1
theme:
sidebar:

View File

@ -44,7 +44,7 @@ replace the `spec-url` attribute with the URL or local file address to your defi
</html>
```
{% admonition type="info" name="Redoc requires an HTTP server to run locally" %}
{% admonition type="attention" name="Redoc requires an HTTP server to run locally" %}
Loading local OpenAPI definitions is impossible without running a web server because of issues with
[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and
other security reasons. Refer to [Running Redoc locally](./deployment/intro.md#how-to-run-redoc-locally) for more information.

View File

@ -10,6 +10,9 @@ You can use the following [vendor extensions](https://redocly.com/docs/openapi-v
- [Tag Group Object](#tag-group-object)
- [Fixed fields](#fixed-fields)
- [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)
- [x-logo](#x-logo)
- [How to use with Redoc](#how-to-use-with-redoc-2)
@ -36,21 +39,21 @@ You can use the following [vendor extensions](https://redocly.com/docs/openapi-v
- [Schema Object](#schema-object)
- [x-nullable](#x-nullable)
- [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)
- [How to use with Redoc](#how-to-use-with-redoc-9)
- [x-additionalPropertiesName example](#x-additionalpropertiesname-example)
- [x-explicitMappingOnly](#x-explicitmappingonly)
- [How to use with Redoc](#how-to-use-with-redoc-10)
- [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
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-1/)
### x-servers
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.
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.
### x-tagGroups
@ -102,6 +105,29 @@ x-tagGroups:
- Secondary Stats
```
### x-ignoredHeaderParameters
| 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/)
@ -226,11 +252,6 @@ lang: JavaScript
source: console.log('Hello World');
```
### 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/)
@ -264,6 +285,59 @@ Extends the OpenAPI [Schema Object](https://redocly.com/docs/openapi-visual-refe
#### How to use with Redoc
Schemas marked as `x-nullable` are marked in Redoc with the label Nullable.
### x-extendedDiscriminator
**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.
@ -323,31 +397,3 @@ Pet:
```
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
```

View File

@ -52,31 +52,6 @@ describe('Menu', () => {
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', () => {
cy.contains('[role=menuitem] > label.-depth1', 'pet').click({ force: true });
cy.location('hash').should('equal', '#tag/pet');

2868
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "redoc",
"version": "2.5.0",
"version": "2.1.5",
"description": "ReDoc",
"repository": {
"type": "git",
@ -61,9 +61,8 @@
"pre-commit": "pretty-quick --staged && npm run lint"
},
"devDependencies": {
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@cypress/webpack-preprocessor": "^5.17.1",
"@size-limit/file": "^11.1.4",
"@size-limit/preset-app": "^8.2.6",
"@types/chai": "^4.2.18",
"@types/dompurify": "^2.2.2",
"@types/enzyme": "^3.10.5",
@ -93,7 +92,7 @@
"cypress": "^13.8.1",
"enzyme": "^3.11.0",
"enzyme-to-json": "^3.6.2",
"esbuild-loader": "^4.3.0",
"esbuild-loader": "^3.0.1",
"eslint": "^7.27.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-react": "^7.34.2",
@ -115,7 +114,7 @@
"react-dom": "^18.0.0",
"rimraf": "^3.0.2",
"shelljs": "^0.8.4",
"size-limit": "^11.1.4",
"size-limit": "^8.2.6",
"style-loader": "^3.3.1",
"styled-components": "^5.3.0",
"ts-jest": "^29.1.1",
@ -124,7 +123,7 @@
"typescript": "^4.9.0",
"unfetch": "^4.2.0",
"url": "^0.11.1",
"webpack": "^5.94.0",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-node-externals": "^3.0.0",
@ -133,15 +132,16 @@
"peerDependencies": {
"core-js": "^3.1.4",
"mobx": "^6.0.4",
"react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^16.8.4 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0",
"styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5"
},
"dependencies": {
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@redocly/openapi-core": "^1.4.0",
"classnames": "^2.3.2",
"decko": "^1.2.0",
"dompurify": "^3.2.4",
"dompurify": "^3.0.6",
"eventemitter3": "^5.0.1",
"json-pointer": "^0.6.2",
"lunr": "^2.3.9",

View File

@ -97,11 +97,9 @@ export const RecursiveLabel = styled(FieldLabel)`
export const PatternLabel = styled(FieldLabel)`
color: #0e7c86;
font-family: ${props => props.theme.typography.code.fontFamily};
font-size: 12px;
&::before,
&::after {
content: ' ';
font-weight: bold;
}
`;

View File

@ -47,11 +47,11 @@ export const ShelfIcon = styled(IntShelfIcon)`
}
`;
export const Badge = styled.span<{ type: string; color?: string }>`
export const Badge = styled.span<{ type: string }>`
display: inline-block;
padding: 2px 8px;
margin: 0;
background-color: ${props => props.color || props.theme.colors[props.type].main};
background-color: ${props => props.theme.colors[props.type].main};
color: ${props => props.theme.colors[props.type].contrastText};
font-size: ${props => props.theme.typography.code.fontSize};
vertical-align: middle;

View File

@ -22,13 +22,20 @@ export interface ApiInfoProps {
@observer
export class ApiInfo extends React.Component<ApiInfoProps> {
handleDownloadClick = e => {
if (!e.target.href) {
e.target.href = this.props.store.spec.info.downloadLink;
}
};
render() {
const { store } = this.props;
const { info, externalDocs } = store.spec;
const hideDownloadButtons = store.options.hideDownloadButtons;
const hideDownloadButton = store.options.hideDownloadButton;
const downloadFilename = info.downloadFileName;
const downloadLink = info.downloadLink;
const downloadUrls = info.downloadUrls;
const downloadFileName = info.downloadFileName;
const license =
(info.license && (
<InfoSpan>
@ -76,22 +83,17 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
<ApiHeader>
{info.title} {version}
</ApiHeader>
{!hideDownloadButtons && (
{!hideDownloadButton && (
<p>
{l('downloadSpecification')}:
{downloadUrls?.map(({ title, url }) => {
return (
<DownloadButton
download={downloadFileName || true}
download={downloadFilename || true}
target="_blank"
href={url}
rel="noreferrer"
key={url}
href={downloadLink}
onClick={this.handleDownloadClick}
>
{title}
{l('download')}
</DownloadButton>
);
})}
</p>
)}
<StyledMarkdownBlock>

View File

@ -18,6 +18,14 @@ export function ArrayItemDetails({ schema }: { schema: SchemaModel }) {
return null;
}
if (schema.type === 'string' && schema.pattern) {
return (
<Wrapper>
[<Pattern schema={schema} />]
</Wrapper>
);
}
return (
<Wrapper>
[ items

View File

@ -5,29 +5,17 @@ import { l } from '../../services/Labels';
import { OptionsContext } from '../OptionsProvider';
import styled from '../../styled-components';
import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
import { Markdown } from '../Markdown/Markdown';
export interface EnumValuesProps {
values?: string[] | { [name: string]: string };
type: string | string[];
values: string[];
isArrayType: boolean;
}
export interface EnumValuesState {
collapsed: boolean;
}
const DescriptionEnumsBlock = styled(StyledMarkdownBlock)`
table {
margin-bottom: 0.2em;
}
`;
export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesState> {
constructor(props: EnumValuesProps) {
super(props);
this.toggle = this.toggle.bind(this);
}
state: EnumValuesState = {
collapsed: true,
};
@ -39,79 +27,35 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
}
render() {
const { values, type } = this.props;
const { values, isArrayType } = this.props;
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
const { enumSkipQuotes, maxDisplayedEnumValues } = this.context as RedocRawOptions;
if (!enums.length) {
if (!values.length) {
return null;
}
const displayedItems =
this.state.collapsed && maxDisplayedEnumValues
? enums.slice(0, maxDisplayedEnumValues)
: enums;
? values.slice(0, maxDisplayedEnumValues)
: values;
const showToggleButton = maxDisplayedEnumValues ? enums.length > maxDisplayedEnumValues : false;
const showToggleButton = maxDisplayedEnumValues
? values.length > maxDisplayedEnumValues
: false;
const toggleButtonText = maxDisplayedEnumValues
? collapsed
? `${enums.length - maxDisplayedEnumValues} more`
? `${values.length - maxDisplayedEnumValues} more`
: 'Hide'
: '';
return (
<div>
{isDescriptionEnum ? (
<>
<DescriptionEnumsBlock>
<table>
<thead>
<tr>
<th>
<FieldLabel>
{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') : ''}{' '}
{isArrayType ? l('enumArray') : ''}{' '}
{values.length === 1 ? l('enumSingleValue') : l('enum')}:
</FieldLabel>{' '}
{displayedItems.map((value, idx) => {
@ -123,10 +67,14 @@ export class EnumValues extends React.PureComponent<EnumValuesProps, EnumValuesS
);
})}
{showToggleButton ? (
<ToggleButton onClick={this.toggle}>{toggleButtonText}</ToggleButton>
<ToggleButton
onClick={() => {
this.toggle();
}}
>
{toggleButtonText}
</ToggleButton>
) : null}
</>
)}
</div>
);
}

View File

@ -19,8 +19,6 @@ import { Schema } from '../Schema/Schema';
import type { SchemaOptions } from '../Schema/Schema';
import type { FieldModel } from '../../services/models';
import { OptionsContext } from '../OptionsProvider';
import { RedocNormalizedOptions } from '../../services/RedocNormalizedOptions';
export interface FieldProps extends SchemaOptions {
className?: string;
@ -29,15 +27,12 @@ export interface FieldProps extends SchemaOptions {
field: FieldModel;
expandByDefault?: boolean;
fieldParentsName?: string[];
renderDiscriminatorSwitch?: (opts: FieldProps) => JSX.Element;
}
@observer
export class Field extends React.Component<FieldProps> {
static contextType = OptionsContext;
context: RedocNormalizedOptions;
toggle = () => {
if (this.props.field.expanded === undefined && this.props.expandByDefault) {
this.props.field.collapse();
@ -54,12 +49,12 @@ export class Field extends React.Component<FieldProps> {
};
render() {
const { hidePropertiesPrefix } = this.context;
const { className = '', field, isLast, expandByDefault, fieldParentsName = [] } = this.props;
const { className = '', field, isLast, expandByDefault } = this.props;
const { name, deprecated, required, kind } = field;
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
const labels = (
<>
{kind === 'additionalProperties' && <PropertyLabel>additional property</PropertyLabel>}
@ -80,10 +75,6 @@ export class Field extends React.Component<FieldProps> {
onKeyPress={this.handleKeyPress}
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>
<ShelfIcon direction={expanded ? 'down' : 'right'} />
</button>
@ -92,10 +83,6 @@ export class Field extends React.Component<FieldProps> {
) : (
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
<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>
{labels}
</PropertyNameCell>
@ -115,7 +102,6 @@ export class Field extends React.Component<FieldProps> {
<InnerPropertiesWrap>
<Schema
schema={field.schema}
fieldParentsName={[...(fieldParentsName || []), field.name]}
skipReadOnly={this.props.skipReadOnly}
skipWriteOnly={this.props.skipWriteOnly}
showTitle={this.props.showTitle}

View File

@ -8,7 +8,7 @@ import {
TypePrefix,
TypeTitle,
} from '../../common-elements/fields';
import { getSerializedValue, isArray, isObject } from '../../utils';
import { getSerializedValue, isObject } from '../../utils';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { Markdown } from '../Markdown/Markdown';
import { EnumValues } from './EnumValues';
@ -30,8 +30,7 @@ export const FieldDetailsComponent = observer((props: FieldProps) => {
const { showExamples, field, renderDiscriminatorSwitch } = props;
const { schema, description, deprecated, extensions, in: _in, const: _const } = field;
const isArrayType =
schema.type === 'array' || (isArray(schema.type) && schema.type.includes('array'));
const isArrayType = schema.type === 'array';
const rawDefault = enumSkipQuotes || _in === 'header'; // having quotes around header field default values is confusing and inappropriate
@ -99,7 +98,7 @@ export const FieldDetailsComponent = observer((props: FieldProps) => {
)}
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={defaultValue} />
{!renderDiscriminatorSwitch && (
<EnumValues type={schema.type} values={schema['x-enumDescriptions'] || schema.enum} />
<EnumValues isArrayType={isArrayType} values={schema.enum} />
)}{' '}
{renderedExamples}
<Extensions extensions={{ ...extensions, ...schema.extensions }} />

View File

@ -41,12 +41,11 @@ const Json = (props: JsonProps) => {
<OptionsContext.Consumer>
{options => (
<PrismDiv
tabIndex={0}
className={props.className}
// tslint:disable-next-line
ref={node => setNode(node!)}
dangerouslySetInnerHTML={{
__html: jsonToHTML(props.data, options.jsonSamplesExpandLevel),
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
}}
/>
)}

View File

@ -6,14 +6,11 @@ import { StylingMarkdownProps } from './Markdown';
import { StyledMarkdownBlock } from './styled.elements';
import styled from 'styled-components';
// Workaround for DOMPurify type issues (https://github.com/cure53/DOMPurify/issues/1034)
const dompurify = DOMPurify['default'] as DOMPurify.DOMPurify;
const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
const StyledMarkdownSpan = styled(props => <StyledMarkdownBlock {...props} />)`
display: inline;
`;
const sanitize = (sanitize, html) => (sanitize ? dompurify.sanitize(html) : html);
const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
export function SanitizedMarkdownHTML({
inline,
@ -28,7 +25,7 @@ export function SanitizedMarkdownHTML({
<Wrap
className={'redoc-markdown ' + (rest.className || '')}
dangerouslySetInnerHTML={{
__html: sanitize(options.sanitize, rest.html),
__html: sanitize(options.untrustedSpec, rest.html),
}}
data-role={rest['data-role']}
{...rest}

View File

@ -28,20 +28,9 @@ export interface OperationProps {
}
export const Operation = observer(({ operation }: OperationProps): JSX.Element => {
const {
name: summary,
description,
deprecated,
externalDocs,
isWebhook,
httpVerb,
badges,
} = operation;
const { name: summary, description, deprecated, externalDocs, isWebhook, httpVerb } = operation;
const hasDescription = !!(description || externalDocs);
const { showWebhookVerb } = React.useContext(OptionsContext);
const badgesBefore = badges.filter(({ position }) => position === 'before');
const badgesAfter = badges.filter(({ position }) => position === 'after');
return (
<OptionsContext.Consumer>
{options => (
@ -49,11 +38,6 @@ export const Operation = observer(({ operation }: OperationProps): JSX.Element =
<MiddlePanel>
<H2>
<ShareLink to={operation.id} />
{badgesBefore.map(({ name, color }) => (
<Badge type="primary" key={name} color={color}>
{name}
</Badge>
))}
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
{isWebhook && (
<Badge type="primary">
@ -61,11 +45,6 @@ export const Operation = observer(({ operation }: OperationProps): JSX.Element =
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
</Badge>
)}
{badgesAfter.map(({ name, color }) => (
<Badge type="primary" key={name} color={color}>
{name}
</Badge>
))}
</H2>
{options.pathInMiddlePanel && !isWebhook && (
<Endpoint operation={operation} inverted={true} />

View File

@ -16,24 +16,14 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
render() {
const schema = this.props.schema;
const itemsSchema = schema.items;
const fieldParentsName = this.props.fieldParentsName;
const minMaxItems =
schema.minItems === undefined && schema.maxItems === undefined
? ''
: `(${humanizeConstraints(schema)})`;
const updatedParentsArray = fieldParentsName
? [...fieldParentsName.slice(0, -1), fieldParentsName[fieldParentsName.length - 1] + '[]']
: fieldParentsName;
if (schema.fields) {
return (
<ObjectSchema
{...(this.props as any)}
level={this.props.level}
fieldParentsName={updatedParentsArray}
/>
);
return <ObjectSchema {...(this.props as any)} level={this.props.level} />;
}
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
return (
@ -47,7 +37,7 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
<div>
<ArrayOpenningLabel> Array {minMaxItems}</ArrayOpenningLabel>
<PaddedSchema>
<Schema {...this.props} schema={itemsSchema} fieldParentsName={updatedParentsArray} />
<Schema {...this.props} schema={itemsSchema} />
</PaddedSchema>
<ArrayClosingLabel />
</div>

View File

@ -16,7 +16,6 @@ export interface ObjectSchemaProps extends SchemaProps {
fieldName: string;
parentSchema: SchemaModel;
};
fieldParentsName?: string[];
}
export const ObjectSchema = observer(
@ -27,9 +26,8 @@ export const ObjectSchema = observer(
skipReadOnly,
skipWriteOnly,
level,
fieldParentsName,
}: ObjectSchemaProps) => {
const { expandSingleSchemaField, showObjectSchemaExamples, schemasExpansionLevel } =
const { expandSingleSchemaField, showObjectSchemaExamples, schemaExpansionLevel } =
React.useContext(OptionsContext);
const filteredFields = React.useMemo(
@ -47,7 +45,7 @@ export const ObjectSchema = observer(
);
const expandByDefault =
(expandSingleSchemaField && filteredFields.length === 1) || schemasExpansionLevel >= level!;
(expandSingleSchemaField && filteredFields.length === 1) || schemaExpansionLevel >= level!;
return (
<PropertiesTable>
@ -60,7 +58,6 @@ export const ObjectSchema = observer(
isLast={isLast}
field={field}
expandByDefault={expandByDefault}
fieldParentsName={Number(level) > 1 ? fieldParentsName : []}
renderDiscriminatorSwitch={
discriminator?.fieldName === field.name
? () => (

View File

@ -21,7 +21,6 @@ export interface SchemaOptions {
export interface SchemaProps extends SchemaOptions {
schema: SchemaModel;
fieldParentsName?: string[];
}
@observer

View File

@ -2,14 +2,14 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { ShelfIcon } from '../../common-elements/shelfs';
import type { IMenuItem } from '../../services';
import { OperationModel } from '../../services';
import { l } from '../../services/Labels';
import { scrollIntoViewIfNeeded } from '../../utils';
import { shortenHTTPVerb } from '../../utils/openapi';
import { OptionsContext } from '../OptionsProvider';
import { MenuItems } from './MenuItems';
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
import { l } from '../../services/Labels';
import { scrollIntoViewIfNeeded } from '../../utils';
import { OptionsContext } from '../OptionsProvider';
import type { IMenuItem } from '../../services';
export interface MenuItemProps {
item: IMenuItem;
@ -47,18 +47,9 @@ export class MenuItem extends React.Component<MenuItemProps> {
<MenuItemLi
tabIndex={0}
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}
data-item-id={item.id}
role="menuitem"
aria-label={item.sidebarLabel}
aria-expanded={item.expanded}
>
{item.type === 'operation' ? (
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
@ -110,12 +101,6 @@ export const OperationMenuItemContent = observer((props: OperationMenuItemConten
$deprecated={item.deprecated}
ref={ref}
>
{item.badges &&
item.badges?.map(({ name, color }) => (
<OperationBadge type="badge" color={color} key={name}>
{name}
</OperationBadge>
))}
{item.isWebhook ? (
<OperationBadge type="hook">
{showWebhookVerb ? item.httpVerb : l('webhook')}

View File

@ -4,14 +4,14 @@ import { darken } from 'polished';
import { deprecatedCss, ShelfIcon } from '../../common-elements';
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';
export const OperationBadge = styled.span.attrs((props: { type: string; color?: string }) => ({
export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
className: `operation-type ${props.type}`,
}))<{ type: string; color?: string }>`
}))<{ type: string }>`
width: 9ex;
display: inline-block;
height: ${props => props.theme.typography.code.fontSize};
line-height: ${props => props.theme.typography.code.fontSize};
background-color: ${props => props.color || '#333'};
background-color: #333;
border-radius: 3px;
background-repeat: no-repeat;
background-position: 6px 4px;

View File

@ -56,7 +56,6 @@ export function StoreBuilder(props: StoreBuilderProps) {
}
}
load();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [spec, specUrl]);
const store = React.useMemo(() => {

View File

@ -90,7 +90,7 @@ describe('FieldDetailsComponent', () => {
items: {
type: 'string',
pattern: '^see regex[0-9]$',
constraints: ['<= 128 characters'],
constraints: [''],
externalDocs: undefined,
},
} as any as SchemaModel,

View File

@ -2,7 +2,6 @@
import { mount, ReactWrapper } from 'enzyme';
import * as React from 'react';
import { act } from 'react';
import { JsonViewer } from '../';
import { withTheme } from '../testProviders';
@ -51,54 +50,5 @@ describe('Components', () => {
expect(flatDataComponent.html()).not.toContain('Expand 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);
});
});
});
});

View File

@ -79,23 +79,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -104,9 +102,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -117,7 +114,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -296,6 +292,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog/properties/packSize",
@ -316,7 +313,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "number",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
FieldModel {
@ -355,23 +351,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -380,9 +374,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -393,7 +386,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -572,6 +564,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog/properties/type",
@ -604,7 +597,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "string",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
],
@ -618,23 +610,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -643,9 +633,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -656,7 +645,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -835,6 +823,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog",
@ -889,7 +878,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "object",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
SchemaModel {
"activeOneOf": 0,
@ -943,23 +931,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -968,9 +954,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -981,7 +966,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -1160,6 +1144,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Cat/properties/type",
@ -1192,7 +1177,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "string",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
FieldModel {
@ -1231,23 +1215,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -1256,9 +1238,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -1269,7 +1250,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -1448,6 +1428,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Cat/properties/packSize",
@ -1476,7 +1457,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "number",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
],
@ -1490,23 +1470,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -1515,9 +1493,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -1528,7 +1505,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -1707,6 +1683,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Cat",
@ -1766,7 +1743,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "object",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
],
"options": RedocNormalizedOptions {
@ -1774,23 +1750,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -1799,9 +1773,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -1812,7 +1785,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -1991,6 +1963,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Pet",
@ -2030,7 +2003,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "object",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
}
}
@ -2088,23 +2060,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -2113,9 +2083,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -2126,7 +2095,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -2305,6 +2273,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog/properties/packSize",
@ -2325,7 +2294,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "number",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
FieldModel {
@ -2364,23 +2332,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -2389,9 +2355,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -2402,7 +2367,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -2581,6 +2545,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog/properties/type",
@ -2613,7 +2578,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "string",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
},
],
@ -2627,23 +2591,21 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"disableSearch": false,
"downloadDefinitionUrl": undefined,
"downloadFileName": undefined,
"downloadUrls": undefined,
"enumSkipQuotes": false,
"expandDefaultServerVariables": false,
"expandResponses": {},
"expandSingleSchemaField": false,
"generatedSamplesMaxDepth": 10,
"hideDownloadButtons": false,
"generatedPayloadSamplesMaxDepth": 10,
"hideDownloadButton": false,
"hideFab": false,
"hideHostname": false,
"hidePropertiesPrefix": true,
"hideRequestPayloadSample": false,
"hideSchemaPattern": false,
"hideSchemaTitles": false,
"hideSecuritySection": false,
"hideSingleRequestSampleTab": false,
"ignoreNamedSchemas": Set {},
"jsonSamplesExpandLevel": 2,
"jsonSampleExpandLevel": 2,
"maxDisplayedEnumValues": undefined,
"menuToggle": true,
"minCharacterLengthToInitSearch": 3,
@ -2652,9 +2614,8 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"onlyRequiredInSamples": false,
"pathInMiddlePanel": false,
"payloadSampleIdx": 0,
"sanitize": false,
"schemaDefinitionsTagName": undefined,
"schemasExpansionLevel": 0,
"requiredPropsFirst": false,
"schemaExpansionLevel": 0,
"scrollYOffset": [Function],
"showExtensions": false,
"showObjectSchemaExamples": false,
@ -2665,7 +2626,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"sortEnumValuesAlphabetically": false,
"sortOperationsAlphabetically": false,
"sortPropsAlphabetically": false,
"sortRequiredPropsFirst": false,
"sortTagsAlphabetically": false,
"theme": {
"breakpoints": {
@ -2844,6 +2804,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
},
},
"unstable_ignoreMimeParameters": false,
"untrustedSpec": false,
},
"pattern": undefined,
"pointer": "#/components/schemas/Dog",
@ -2898,7 +2859,6 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"type": "object",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
}
}
/>
@ -2961,11 +2921,9 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"type": "number",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
}
}
fieldParentsName={[]}
isLast={false}
key="packSize"
showExamples={false}
@ -3036,11 +2994,9 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"type": "string",
"typePrefix": "",
"writeOnly": false,
"x-enumDescriptions": undefined,
},
}
}
fieldParentsName={[]}
isLast={true}
key="type"
renderDiscriminatorSwitch={[Function]}

View File

@ -159,19 +159,11 @@ exports[`FieldDetailsComponent renders correctly when field items have string ty
</span>
</span>
<span
class="sc-kpDqfm sc-dAlyuH sc-gvZAcH cGRfjn gHomYR eXivNJ"
class="sc-kpDqfm sc-dAlyuH sc-dxcDKg cGRfjn gHomYR gXntsr"
>
[ items
<span>
[
<span
class="sc-kpDqfm sc-gFqAkR cGRfjn fYEICH"
>
&lt;= 128 characters
</span>
</span>
<span
class="sc-kpDqfm sc-eDPEul cGRfjn cCKYVD"
class="sc-kpDqfm sc-eDPEul cGRfjn erJHow"
>
^see regex[0-9]$
</span>

View File

@ -3,21 +3,21 @@
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.
OAuth2 is also a safer and more secure way to give you access.</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 sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>read your pets</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-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-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>"
</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></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></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><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-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>"
`;
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 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 2`] = `
"<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.
"<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.
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-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 sc-fhzFiK gTGgei iCmQdS hXtrri redoc-markdown"><p>read your pets</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-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-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>,"
</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></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></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><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-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>,"
`;

View File

@ -1,4 +1,4 @@
import type { OpenAPIPaths, OpenAPITag, OpenAPISchema } from '../types';
import type { OpenAPISpec, OpenAPIPaths, OpenAPITag, OpenAPISchema } from '../types';
import { isOperationName, JsonPointer, alphabeticallyByProp } from '../utils';
import { MarkdownRenderer } from './MarkdownRenderer';
import { GroupModel, OperationModel } from './models';
@ -17,19 +17,9 @@ export class MenuBuilder {
options: RedocNormalizedOptions,
): ContentItemModel[] {
const spec = parser.spec;
const { schemaDefinitionsTagName } = options;
const items: ContentItemModel[] = [];
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);
const tagsMap = MenuBuilder.getTagsWithOperations(parser, spec);
items.push(...MenuBuilder.addMarkdownItems(spec.info.description || '', undefined, 1, options));
if (spec['x-tagGroups'] && spec['x-tagGroups'].length > 0) {
items.push(
@ -38,7 +28,6 @@ export class MenuBuilder {
} else {
items.push(...MenuBuilder.getTagsItems(parser, tagsMap, undefined, undefined, options));
}
return items;
}
@ -152,7 +141,6 @@ export class MenuBuilder {
parser,
tag,
parent: item,
schemaDefinitionsTagName: options.schemaDefinitionsTagName,
});
item.items = [
@ -207,11 +195,10 @@ export class MenuBuilder {
/**
* collects tags and maps each tag to list of operations belonging to this tag
*/
static getTagsWithOperations(parser: OpenAPIParser, explicitTags: OpenAPITag[]): TagsInfoMap {
const { spec } = parser;
static getTagsWithOperations(parser: OpenAPIParser, spec: OpenAPISpec): TagsInfoMap {
const tags: TagsInfoMap = {};
const webhooks = spec['x-webhooks'] || spec.webhooks;
for (const tag of explicitTags || []) {
for (const tag of spec.tags || []) {
tags[tag.name] = { ...tag, operations: [] };
}
@ -273,18 +260,14 @@ export class MenuBuilder {
parser,
tag,
parent,
schemaDefinitionsTagName,
}: {
parser: OpenAPIParser;
tag: TagInfo;
parent: GroupModel;
schemaDefinitionsTagName?: string;
}): GroupModel[] {
const defaultTags = schemaDefinitionsTagName ? [schemaDefinitionsTagName] : [];
return Object.entries(parser.spec.components?.schemas || {})
.map(([schemaName, schema]) => {
const schemaTags = schema['x-tags'] || defaultTags;
const schemaTags = schema['x-tags'];
if (!schemaTags?.includes(tag.name)) return null;
const item = new GroupModel(

View File

@ -364,18 +364,14 @@ export class OpenAPIParser {
const allOf = schema.allOf;
for (let i = 0; i < allOf.length; i++) {
const { oneOf, ...sub } = allOf[i];
if (!oneOf) {
continue;
}
if (Array.isArray(oneOf)) {
const sub = allOf[i];
if (Array.isArray(sub.oneOf)) {
const beforeAllOf = allOf.slice(0, i);
const afterAllOf = allOf.slice(i + 1);
const siblingValues = Object.keys(sub).length > 0 ? [sub] : [];
return {
oneOf: oneOf.map((part: OpenAPISchema) => {
oneOf: sub.oneOf.map((part: OpenAPISchema) => {
return {
allOf: [...beforeAllOf, ...siblingValues, part, ...afterAllOf],
allOf: [...beforeAllOf, part, ...afterAllOf],
'x-refsStack': refsStack,
};
}),

View File

@ -6,32 +6,23 @@ import { setRedocLabels } from './Labels';
import { SideNavStyleEnum } from './types';
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
export type DownloadUrlsConfig = {
title?: string;
url: string;
}[];
export interface RedocRawOptions {
theme?: ThemeInterface;
scrollYOffset?: number | string | (() => number);
hideHostname?: boolean | string;
expandResponses?: string | 'all';
requiredPropsFirst?: boolean | string; // remove in next major release
sortRequiredPropsFirst?: boolean | string;
requiredPropsFirst?: boolean | string;
sortPropsAlphabetically?: boolean | string;
sortEnumValuesAlphabetically?: boolean | string;
sortOperationsAlphabetically?: boolean | string;
sortTagsAlphabetically?: boolean | string;
nativeScrollbars?: boolean | string;
pathInMiddlePanel?: boolean | string;
untrustedSpec?: boolean | string; // remove in next major release
sanitize?: boolean | string;
untrustedSpec?: boolean | string;
hideLoading?: boolean | string;
hideDownloadButton?: boolean | string; // remove in next major release
hideDownloadButtons?: boolean | string;
hideDownloadButton?: boolean | string;
downloadFileName?: string;
downloadDefinitionUrl?: string;
downloadUrls?: DownloadUrlsConfig;
disableSearch?: boolean | string;
onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[];
@ -39,15 +30,12 @@ export interface RedocRawOptions {
hideSingleRequestSampleTab?: boolean | string;
hideRequestPayloadSample?: boolean;
menuToggle?: boolean | string;
jsonSampleExpandLevel?: number | string | 'all'; // remove in next major release
jsonSamplesExpandLevel?: number | string | 'all';
jsonSampleExpandLevel?: number | string | 'all';
hideSchemaTitles?: boolean | string;
simpleOneOfTypeLabel?: boolean | string;
payloadSampleIdx?: number;
expandSingleSchemaField?: boolean | string;
schemaExpansionLevel?: number | string | 'all'; // remove in next major release
schemasExpansionLevel?: number | string | 'all';
schemaDefinitionsTagName?: string;
schemaExpansionLevel?: number | string | 'all';
showObjectSchemaExamples?: boolean | string;
showSecuritySchemeType?: boolean;
hideSecuritySection?: boolean;
@ -64,13 +52,11 @@ export interface RedocRawOptions {
maxDisplayedEnumValues?: number;
ignoreNamedSchemas?: string[] | string;
hideSchemaPattern?: boolean;
generatedPayloadSamplesMaxDepth?: number; // remove in next major release
generatedSamplesMaxDepth?: number;
generatedPayloadSamplesMaxDepth?: number;
nonce?: string;
hideFab?: boolean;
minCharacterLengthToInitSearch?: number;
showWebhookVerb?: boolean;
hidePropertiesPrefix?: boolean;
}
export function argValueToBoolean(val?: string | boolean, defaultValue?: boolean): boolean {
@ -230,18 +216,17 @@ export class RedocNormalizedOptions {
scrollYOffset: () => number;
hideHostname: boolean;
expandResponses: { [code: string]: boolean } | 'all';
sortRequiredPropsFirst: boolean;
requiredPropsFirst: boolean;
sortPropsAlphabetically: boolean;
sortEnumValuesAlphabetically: boolean;
sortOperationsAlphabetically: boolean;
sortTagsAlphabetically: boolean;
nativeScrollbars: boolean;
pathInMiddlePanel: boolean;
sanitize: boolean;
hideDownloadButtons: boolean;
untrustedSpec: boolean;
hideDownloadButton: boolean;
downloadFileName?: string;
downloadDefinitionUrl?: string;
downloadUrls?: DownloadUrlsConfig;
disableSearch: boolean;
onlyRequiredInSamples: boolean;
showExtensions: boolean | string[];
@ -249,14 +234,13 @@ export class RedocNormalizedOptions {
hideSingleRequestSampleTab: boolean;
hideRequestPayloadSample: boolean;
menuToggle: boolean;
jsonSamplesExpandLevel: number;
jsonSampleExpandLevel: number;
enumSkipQuotes: boolean;
hideSchemaTitles: boolean;
simpleOneOfTypeLabel: boolean;
payloadSampleIdx: number;
expandSingleSchemaField: boolean;
schemasExpansionLevel: number;
schemaDefinitionsTagName?: string;
schemaExpansionLevel: number;
showObjectSchemaExamples: boolean;
showSecuritySchemeType?: boolean;
hideSecuritySection?: boolean;
@ -270,11 +254,10 @@ export class RedocNormalizedOptions {
ignoreNamedSchemas: Set<string>;
hideSchemaPattern: boolean;
generatedSamplesMaxDepth: number;
generatedPayloadSamplesMaxDepth: number;
hideFab: boolean;
minCharacterLengthToInitSearch: number;
showWebhookVerb: boolean;
hidePropertiesPrefix?: boolean;
nonce?: string;
@ -305,20 +288,17 @@ export class RedocNormalizedOptions {
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
this.sortRequiredPropsFirst = argValueToBoolean(
raw.sortRequiredPropsFirst || raw.requiredPropsFirst,
);
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
this.sanitize = argValueToBoolean(raw.sanitize || raw.untrustedSpec);
this.hideDownloadButtons = argValueToBoolean(raw.hideDownloadButtons || raw.hideDownloadButton);
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
this.downloadFileName = raw.downloadFileName;
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
this.downloadUrls = raw.downloadUrls;
this.disableSearch = argValueToBoolean(raw.disableSearch);
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
@ -326,18 +306,15 @@ export class RedocNormalizedOptions {
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
this.jsonSamplesExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
raw.jsonSamplesExpandLevel || raw.jsonSampleExpandLevel,
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
raw.jsonSampleExpandLevel,
);
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
this.schemasExpansionLevel = argValueToExpandLevel(
raw.schemasExpansionLevel || raw.schemaExpansionLevel,
);
this.schemaDefinitionsTagName = raw.schemaDefinitionsTagName;
this.schemaExpansionLevel = argValueToExpandLevel(raw.schemaExpansionLevel);
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);
@ -353,13 +330,13 @@ export class RedocNormalizedOptions {
: raw.ignoreNamedSchemas?.split(',').map(s => s.trim());
this.ignoreNamedSchemas = new Set(ignoreNamedSchemas);
this.hideSchemaPattern = argValueToBoolean(raw.hideSchemaPattern);
this.generatedSamplesMaxDepth = RedocNormalizedOptions.normalizeGeneratedPayloadSamplesMaxDepth(
raw.generatedSamplesMaxDepth || raw.generatedPayloadSamplesMaxDepth,
this.generatedPayloadSamplesMaxDepth =
RedocNormalizedOptions.normalizeGeneratedPayloadSamplesMaxDepth(
raw.generatedPayloadSamplesMaxDepth,
);
this.nonce = raw.nonce;
this.hideFab = argValueToBoolean(raw.hideFab);
this.minCharacterLengthToInitSearch = argValueToNumber(raw.minCharacterLengthToInitSearch) || 3;
this.showWebhookVerb = argValueToBoolean(raw.showWebhookVerb);
this.hidePropertiesPrefix = argValueToBoolean(raw.hidePropertiesPrefix, true);
}
}

View File

@ -21,14 +21,6 @@ exports[`Models Schema should hoist oneOfs when mergin allOf 1`] = `
"oneOf": [
{
"allOf": [
{
"properties": {
"id": {
"description": "The user's ID",
"type": "integer",
},
},
},
{
"properties": {
"username": {
@ -69,14 +61,6 @@ exports[`Models Schema should hoist oneOfs when mergin allOf 1`] = `
},
{
"allOf": [
{
"properties": {
"id": {
"description": "The user's ID",
"type": "integer",
},
},
},
{
"properties": {
"email": {
@ -115,55 +99,6 @@ exports[`Models Schema should hoist oneOfs when mergin allOf 1`] = `
],
"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",
"type": "string",
},
},
},
],
},
],
"x-refsStack": undefined,
},
],
}
`;

View File

@ -1,24 +0,0 @@
{
"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."
}
}
}
}
}
}

View File

@ -9,12 +9,6 @@
"test": {
"allOf": [
{
"properties": {
"id": {
"description": "The user's ID",
"type": "integer"
}
},
"oneOf": [
{
"properties": {
@ -31,15 +25,6 @@
"type": "string"
}
}
},
{
"properties": {
"id": {
"description": "The user's ID",
"type": "string",
"format": "uuid"
}
}
}
]
},

View File

@ -139,18 +139,10 @@ describe('Models', () => {
} as any;
const opts = new RedocNormalizedOptions({
downloadUrls: [{ title: 'Openapi description', url: 'https:test.com/filename.yaml' }],
downloadDefinitionUrl: 'https:test.com/filename.yaml',
});
const info = new ApiInfoModel(parser, opts);
expect(info.downloadUrls).toMatchInlineSnapshot(`
[
{
"title": "Openapi description",
"url": "https:test.com/filename.yaml",
},
]
`);
expect(info.downloadFileName).toMatchInlineSnapshot(`"openapi.json"`);
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
});
test('should correctly populate download link and download file name', () => {
@ -166,29 +158,8 @@ describe('Models', () => {
downloadFileName: 'test.yaml',
});
const info = new ApiInfoModel(parser, opts);
expect(info.downloadUrls).toMatchInlineSnapshot(`
[
{
"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"`);
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
expect(info.downloadFileName).toEqual('test.yaml');
});
});
});

View File

@ -13,17 +13,6 @@ describe('Models', () => {
describe('Schema', () => {
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', () => {
const spec = require('../fixtures/discriminator.json');
parser = new OpenAPIParser(spec, undefined, opts);

View File

@ -1,6 +1,5 @@
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
import { IS_BROWSER } from '../../utils/';
import { l } from '../Labels';
import type { OpenAPIParser } from '../OpenAPIParser';
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
@ -14,10 +13,7 @@ export class ApiInfoModel implements OpenAPIInfo {
contact?: OpenAPIContact;
license?: OpenAPILicense;
downloadUrls: {
title?: string;
url?: string;
}[];
downloadLink?: string;
downloadFileName?: string;
constructor(
@ -33,28 +29,13 @@ export class ApiInfoModel implements OpenAPIInfo {
this.description = this.description.substring(0, firstHeadingLinePos);
}
this.downloadUrls = this.getDownloadUrls();
this.downloadLink = this.getDownloadLink();
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(url?: string): string | undefined {
if (url) {
return url;
private getDownloadLink(): string | undefined {
if (this.options.downloadDefinitionUrl) {
return this.options.downloadDefinitionUrl;
}
if (this.parser.specUrl) {

View File

@ -14,7 +14,7 @@ export class MediaTypeModel {
name: string;
isRequestType: boolean;
onlyRequiredInSamples: boolean;
generatedSamplesMaxDepth: number;
generatedPayloadSamplesMaxDepth: number;
/**
* @param isRequestType needed to know if skipe RO/RW fields in objects
@ -30,7 +30,7 @@ export class MediaTypeModel {
this.isRequestType = isRequestType;
this.schema = info.schema && new SchemaModel(parser, info.schema, '', options);
this.onlyRequiredInSamples = options.onlyRequiredInSamples;
this.generatedSamplesMaxDepth = options.generatedSamplesMaxDepth;
this.generatedPayloadSamplesMaxDepth = options.generatedPayloadSamplesMaxDepth;
if (info.examples !== undefined) {
this.examples = mapValues(
info.examples,
@ -55,7 +55,7 @@ export class MediaTypeModel {
skipReadOnly: this.isRequestType,
skipWriteOnly: !this.isRequestType,
skipNonRequired: this.isRequestType && this.onlyRequiredInSamples,
maxSampleDepth: this.generatedSamplesMaxDepth,
maxSampleDepth: this.generatedPayloadSamplesMaxDepth,
};
if (this.schema && this.schema.oneOf) {
this.examples = {};

View File

@ -20,12 +20,7 @@ import { RequestBodyModel } from './RequestBody';
import { ResponseModel } from './Response';
import { SideNavStyleEnum } from '../types';
import type {
OpenAPIExternalDocumentation,
OpenAPIServer,
OpenAPIXBadges,
OpenAPIXCodeSample,
} from '../../types';
import type { OpenAPIExternalDocumentation, OpenAPIServer, OpenAPIXCodeSample } from '../../types';
import type { OpenAPIParser } from '../OpenAPIParser';
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import type { MediaContentModel } from './MediaContent';
@ -76,7 +71,6 @@ export class OperationModel implements IMenuItem {
operationId?: string;
operationHash?: string;
httpVerb: string;
badges: OpenAPIXBadges[];
deprecated: boolean;
path: string;
servers: OpenAPIServer[];
@ -118,12 +112,6 @@ export class OperationModel implements IMenuItem {
: options.sideNavStyle === SideNavStyleEnum.PathOnly
? this.path
: this.name;
this.badges =
operationSpec['x-badges']?.map(({ name, color, position }) => ({
name,
color: color,
position: position || 'after',
})) || [];
if (this.isCallback) {
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
@ -247,7 +235,7 @@ export class OperationModel implements IMenuItem {
if (this.options.sortPropsAlphabetically) {
return sortByField(_parameters, 'name');
}
if (this.options.sortRequiredPropsFirst) {
if (this.options.requiredPropsFirst) {
return sortByRequired(_parameters);
}

View File

@ -65,7 +65,6 @@ export class SchemaModel {
rawSchema: OpenAPISchema;
schema: MergedOpenAPISchema;
extensions?: Record<string, any>;
'x-enumDescriptions': { [name: string]: string };
const: any;
contentEncoding?: string;
contentMediaType?: string;
@ -123,7 +122,6 @@ export class SchemaModel {
this.type = schema.type || detectType(schema);
this.format = schema.format;
this.enum = schema.enum || [];
this['x-enumDescriptions'] = schema['x-enumDescriptions'];
this.example = schema.example;
this.examples = schema.examples;
this.deprecated = !!schema.deprecated;
@ -223,7 +221,6 @@ export class SchemaModel {
}
if (this.items?.isPrimitive) {
this.enum = this.items.enum;
this['x-enumDescriptions'] = this.items['x-enumDescriptions'];
}
if (isArray(this.type)) {
const filteredType = this.type.filter(item => item !== 'array');
@ -466,7 +463,7 @@ function buildFields(
if (options.sortPropsAlphabetically) {
fields = sortByField(fields, 'name');
}
if (options.sortRequiredPropsFirst) {
if (options.requiredPropsFirst) {
// if not sort alphabetically sort in the order from required keyword
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
}

View File

@ -70,12 +70,6 @@ export interface OpenAPIXCodeSample {
source: string;
}
export interface OpenAPIXBadges {
name: string;
color?: string;
position?: 'before' | 'after';
}
export interface OpenAPIOperation {
tags?: string[];
summary?: string;
@ -91,7 +85,6 @@ export interface OpenAPIOperation {
servers?: OpenAPIServer[];
'x-codeSamples'?: OpenAPIXCodeSample[];
'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated
'x-badges'?: OpenAPIXBadges[];
}
export interface OpenAPIParameter {

View File

@ -311,11 +311,6 @@ exports[`#loadAndBundleSpec should load And Bundle Spec demo/openapi.yaml 1`] =
"sold",
],
"type": "string",
"x-enumDescriptions": {
"available": "Available status",
"pending": "Pending status",
"sold": "Sold status",
},
},
"tags": {
"description": "Tags attached to the pet",
@ -586,13 +581,6 @@ and standard method from web, mobile and desktop applications.
"tags": [
"pet",
],
"x-badges": [
{
"color": "purple",
"name": "Beta",
"position": "before",
},
],
"x-codeSamples": [
{
"lang": "C#",
@ -657,12 +645,6 @@ try {
"tags": [
"pet",
],
"x-badges": [
{
"color": "purple",
"name": "Alpha",
},
],
"x-codeSamples": [
{
"lang": "PHP",
@ -901,11 +883,6 @@ try {
"tags": [
"pet",
],
"x-badges": [
{
"name": "Gamma",
},
],
},
"post": {
"description": "",

View File

@ -71,30 +71,6 @@ describe('Utils', () => {
const obj2 = { 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', () => {

View File

@ -81,6 +81,7 @@ export function appendToMdHeading(md: string, heading: string, content: string)
}
}
// credits https://stackoverflow.com/a/46973278/1749888
export const mergeObjects = (target: any, ...sources: any[]): any => {
if (!sources.length) {
return target;
@ -92,7 +93,6 @@ export const mergeObjects = (target: any, ...sources: any[]): any => {
if (isMergebleObject(target) && isMergebleObject(source)) {
Object.keys(source).forEach((key: string) => {
if (Object.prototype.hasOwnProperty.call(source, key) && key !== '__proto__') {
if (isMergebleObject(source[key])) {
if (!target[key]) {
target[key] = {};
@ -101,7 +101,6 @@ export const mergeObjects = (target: any, ...sources: any[]): any => {
} else {
target[key] = source[key];
}
}
});
}

View File

@ -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
return decodeURIComponent(serializeParameterValue(field, example));
} else {
return typeof example === 'object' ? example : String(example);
return String(example);
}
}
@ -654,13 +654,12 @@ export function isRedocExtension(key: string): boolean {
'x-codeSamples': true,
'x-displayName': true,
'x-examples': true,
'x-enumDescriptions': true,
'x-ignoredHeaderParameters': true,
'x-logo': true,
'x-nullable': true,
'x-servers': true,
'x-tagGroups': true,
'x-traitTag': true,
'x-badges': true,
'x-additionalPropertiesName': true,
'x-explicitMappingOnly': true,
};