Compare commits

..
Author SHA1 Message Date
Kyle Shockey 1e2baeb91b this.downloadJSON -> this.downloadText
we're always saving as `.txt`, so JSON is a misnomer
2018-04-12 17:56:35 -07:00
Kyle Shockey ac43ee5feb center "Download" text in button 2018-04-12 17:56:07 -07:00
Kyle Shockey 64b83738ce prevent HighlightCode from scrolling entire document 2018-04-12 17:52:27 -07:00
kyle 0cdd54c24d Merge branch 'master' into auto-hide-contents 2018-04-12 17:17:20 -07:00
giancarlokc a3ef0d609f CSS fix. 2018-04-05 23:31:35 -07:00
giancarlokc 80e994ef79 Code clean up. 2018-04-05 23:21:00 -07:00
giancarlokc 115c5305ea Removed collapsing, added scrolling. 2018-04-05 23:16:46 -07:00
giancarlokc cd065d674c Merge branch 'auto-hide-contents' of https://github.com/giancarlokc/swagger-ui into auto-hide-contents 2018-04-05 22:39:19 -07:00
giancarlokc 6f061d8de5 Fix dist 2018-04-05 22:38:43 -07:00
Giancarlo Klemm Camilo 11eb6ef002 Merge branch 'master' into auto-hide-contents 2018-04-05 22:38:11 -07:00
Graeme 177464e06f Fix the angry linter 2018-04-05 21:07:07 -07:00
Graeme 6173d3b966 Better downloading
now downloads file on button click
2018-04-05 20:51:12 -07:00
Graeme 485d04ff3c Added basic downloading
Slightly broken
2018-04-03 15:21:05 -07:00
giancarlokc 0b8dab2d5e Auto hidding content that is longer than 600 characters long. 2018-04-02 23:56:26 -07:00
124 changed files with 904 additions and 23440 deletions
+4 -5
View File
@@ -6,7 +6,6 @@
],
"plugins": [
"transform-runtime",
"babel-plugin-transform-es2015-constants",
[
"module-alias",
[
@@ -18,10 +17,6 @@
"expose": "components",
"src": "src/core/components"
},
{
"expose": "containers",
"src": "src/core/containers"
},
{
"expose": "core",
"src": "src/core"
@@ -41,6 +36,10 @@
{
"expose": "less",
"src": "src/less"
},
{
"expose": "base",
"src": "npm:getbase/src/less/base"
}
]
]
+3 -8
View File
@@ -13,15 +13,11 @@
}
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"extends": ["eslint:recommended", "plugin:react/recommended"],
"plugins": [
"react",
"mocha",
"import"
"mocha"
],
"rules": {
@@ -38,7 +34,6 @@
"no-console": ["error", { allow: ["warn", "error"] }],
"react/jsx-no-bind": 1,
"react/display-name": 0,
"mocha/no-exclusive-tests": "error",
"import/no-extraneous-dependencies": [2]
"mocha/no-exclusive-tests": "error"
}
}
-73
View File
@@ -1,73 +0,0 @@
---
name: Bug report
about: Report an issue you're experiencing
---
<!---
Thanks for filing a bug report! 😄
Before you submit, please read the following:
If you're here to report a security issue, please STOP writing an issue and
contact us at security@swagger.io instead!
Search open/closed issues before submitting!
Issues on GitHub are only related to problems of Swagger-UI itself. We'll try
to offer support here for your use case, but we can't offer help with projects
that use Swagger-UI indirectly, like Springfox or swagger-node.
Likewise, we can't accept bugs in the Swagger/OpenAPI specifications
themselves, or anything that violates the specifications.
-->
### Q&A (please complete the following information)
- OS: [e.g. macOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]
- Method of installation: [e.g. npm, dist assets]
- Swagger-UI version: [e.g. 3.10.0]
- Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0]
### Content & configuration
<!--
Provide us with a way to see what you're seeing,
so that we can fix your issue.
-->
Example Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Describe the bug you're encountering
<!-- A clear and concise description of what the bug is. -->
### To reproduce...
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
### Additional context or thoughts
<!-- Add any other context about the problem here. -->
-42
View File
@@ -1,42 +0,0 @@
---
name: Feature request
about: Suggest an new feature or enhancement for this project
---
### Content & configuration
Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Is your feature request related to a problem?
<!--
Please provide a clear and concise description of what the problem is.
"I'm always frustrated when..."
-->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!--
A clear and concise description of any alternative solutions or features
you've considered.
-->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->
-46
View File
@@ -1,46 +0,0 @@
---
name: Support
about: Ask a question or request help with your implementation.
---
<!--
We can only offer support for Swagger-UI itself.
If you're having a problem with a library that uses Swagger-UI
(for example, Springfox or swagger-node), please open an issue
in that project's repository instead.
-->
### Q&A (please complete the following information)
- OS: [e.g. macOS]
- Browser: [e.g. chrome, safari]
- Version: [e.g. 22]
- Method of installation: [e.g. npm, dist assets]
- Swagger-UI version: [e.g. 3.10.0]
- Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0]
### Content & configuration
<!-- Provide us with a way to see what you're seeing, so that we can help. -->
Swagger/OpenAPI definition:
```yaml
# your YAML here
```
Swagger-UI configuration options:
```js
SwaggerUI({
// your config options here
})
```
```
?yourQueryStringConfig
```
### Screenshots
<!-- If applicable, add screenshots to help give context to your problem. -->
### How can we help?
<!-- Your question or problem goes here! -->
+70
View File
@@ -0,0 +1,70 @@
<!---
Thanks for filing an issue 😄 ! Before you submit, please read the following:
If you're here to report a security issue, please STOP writing an issue and contact us
at security@swagger.io instead!
Search open/closed issues before submitting since someone might have asked the same thing before!
Issues on GitHub are only related to problems of Swagger-UI itself. We'll try to offer support
here for your use case, but we can't offer help with projects that use Swagger-UI indirectly,
like Springfox or swagger-node.
Likewise, we can't accept features or bugs in the Swagger/OpenAPI specifications themselves,
or anything that violates the specifications.
-->
<!--- Provide a general summary of the issue in the title above -->
<!---
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md
--->
| Q | A
| ------------------------------- | -------
| Bug or feature request? |
| Which Swagger/OpenAPI version? |
| Which Swagger-UI version? |
| How did you install Swagger-UI? |
| Which browser & version? |
| Which operating system? |
### Demonstration API definition
<!--- If you're describing a bug, please provide an API definition that reproduces your problem -->
<!--- If you have link to a demo repo please link that! -->
<!--- If your spec is large, please put it into a Gist (https://gist.github.com) instead of pasting it here. -->
```yaml
your: "API definition goes here"
```
### Configuration (browser query string, constructor, config.yaml)
<!--- If describing a bug, tell us what your configuration looks like -->
```js
{
"your": { "constructorConfig": "here" }
}
```
`?yourQueryStringConfig=here`
### Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
### Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
### Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
### Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
+1
View File
@@ -5,6 +5,7 @@ node_modules
.nyc_output
npm-debug.log*
.eslintcache
package-lock.json
*.iml
selenium-debug.log
test/e2e/db.json
+1 -5
View File
@@ -15,8 +15,6 @@ before_deploy:
- npm run build
env:
- DOCKER_IMAGE_NAME=swaggerapi/swagger-ui
script:
- npm test
deploy:
- provider: npm
email: apiteam@swagger.io
@@ -39,7 +37,7 @@ after_success:
docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD;
if [ ! -z "$TRAVIS_TAG" ]; then
DOCKER_IMAGE_TAG=${TRAVIS_TAG#?};
DOCKER_IMAGE_TAG=$TRAVIS_TAG;
else
DOCKER_IMAGE_TAG=unstable;
fi;
@@ -54,5 +52,3 @@ after_success:
docker push $DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG;
fi;
fi;
- npm run build-bundle
- bundlesize
+1 -1
View File
@@ -4,7 +4,7 @@ LABEL maintainer="fehguy"
ENV VERSION "v2.2.10"
ENV FOLDER "swagger-ui-2.2.10"
ENV API_URL "https://petstore.swagger.io/v2/swagger.json"
ENV API_URL "http://petstore.swagger.io/v2/swagger.json"
ENV API_URLS ""
ENV API_KEY "**None**"
ENV OAUTH_CLIENT_ID "**None**"
+11 -9
View File
@@ -2,25 +2,27 @@
[![NPM version](https://badge.fury.io/js/swagger-ui.svg)](http://badge.fury.io/js/swagger-ui)
## New!
**This is the new version of swagger-ui, 3.x. Want to learn more? Check out our [FAQ](http://swagger.io/new-ui-faq/).**
**👉🏼 Want to score an easy open-source contribution?** Check out our [Good first issue](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22) label.
**🕰️ Looking for the older version of Swagger UI?** Refer to the [*2.x* branch](https://github.com/swagger-api/swagger-ui/tree/2.x).
As a brand new version, written from the ground up, there are some known issues and unimplemented features. Check out the [Known Issues](#known-issues) section for more details.
This repository publishes to two different NPM modules:
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is a traditional npm module intended for use in single-page applications that are capable of resolving dependencies (via Webpack, Browserify, etc).
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) is a dependency-free module that includes everything you need to serve Swagger-UI in a server-side project, or a single-page application that can't resolve npm module dependencies.
We strongly suggest that you use `swagger-ui` instead of `swagger-ui-dist` if you're building a single-page application, since `swagger-ui-dist` is significantly larger.
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is a traditional npm module intended for use in JavaScript web application projects that are capable of resolving dependencies (via Webpack, Browserify, etc).
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) is a dependency-free module that includes everything you need to serve Swagger-UI in a server-side project, or a web project that can't resolve npm module dependencies.
For the older version of swagger-ui, refer to the [*2.x branch*](https://github.com/swagger-api/swagger-ui/tree/2.x).
## Compatibility
The OpenAPI Specification has undergone 5 revisions since initial creation in 2010. Compatibility between Swagger UI and the OpenAPI Specification is as follows:
The OpenAPI Specification has undergone 5 revisions since initial creation in 2010. Compatibility between swagger-ui and the OpenAPI Specification is as follows:
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | -----
3.17.3 | 2018-06-16 | 2.0, 3.0 | [tag v3.17.3](https://github.com/swagger-api/swagger-ui/tree/v3.17.3)
3.13.3 | 2018-03-23 | 2.0, 3.0 | [tag v3.13.3](https://github.com/swagger-api/swagger-ui/tree/v3.13.3)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
@@ -74,7 +76,7 @@ Please disclose any security-related issues or vulnerabilities by emailing [secu
## License
Copyright 2018 SmartBear Software
Copyright 2017 SmartBear Software
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+2 -1
View File
@@ -5,6 +5,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
@@ -42,7 +43,7 @@
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
+2 -1
View File
@@ -4,6 +4,7 @@
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
@@ -40,7 +41,7 @@
// Build a system
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
+31 -42
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -3
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -38,10 +38,10 @@ fi
if [[ -f $SWAGGER_JSON ]]; then
cp -s $SWAGGER_JSON $NGINX_ROOT
REL_PATH="./$(basename $SWAGGER_JSON)"
sed -i "s|https://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
sed -i "s|http://example.com/api|$REL_PATH|g" $INDEX_FILE
else
sed -i "s|https://petstore.swagger.io/v2/swagger.json|$API_URL|g" $INDEX_FILE
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$API_URL|g" $INDEX_FILE
sed -i "s|http://example.com/api|$API_URL|g" $INDEX_FILE
fi
@@ -55,11 +55,11 @@ fi
# replace `url` with `urls` option if API_URLS is set
if [[ -n "$API_URLS" ]]; then
sed -i "s|^\(\s*\)url: .*,|\1urls: $API_URLS,|g" $INDEX_FILE
sed -i "s|url: .*,|urls: $API_URLS,|g" $INDEX_FILE
fi
# replace the PORT that nginx listens on if PORT is supplied
if [[ -n "${PORT}" ]]; then
if [[ -n "${PORT}" ]]; then
sed -i "s|8080|${PORT}|g" /etc/nginx/nginx.conf
fi
+2 -2
View File
@@ -40,7 +40,7 @@ const OperationsLayoutPlugin = () => {
// Provide the plugin to Swagger-UI, and select OperationsLayout
// as the layout for Swagger-UI
SwaggerUI({
url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://petstore.swagger.io/v2/swagger.json",
plugins: [ OperationsLayoutPlugin ],
layout: "OperationsLayout"
})
@@ -85,7 +85,7 @@ const AugmentingLayoutPlugin = () => {
// Provide the plugin to Swagger-UI, and select AugmentingLayout
// as the layout for Swagger-UI
SwaggerUI({
url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://petstore.swagger.io/v2/swagger.json",
plugins: [ AugmentingLayoutPlugin ],
layout: "AugmentingLayout"
})
+10 -10
View File
@@ -68,7 +68,7 @@ There is no dependency management built into the plugin system, so if you create
### Interfaces
#### Actions
##### Actions
```javascript
const MyActionPlugin = () => {
@@ -102,7 +102,7 @@ This action creator function will be exposed to container components as `example
For more information about the concept of actions in Redux, see the [Redux Actions documentation](http://redux.js.org/docs/basics/Actions.html).
#### Reducers
##### Reducers
Reducers take a state (which is an [Immutable.js map](https://facebook.github.io/immutable-js/docs/#/Map)) and an action, and return a new state.
@@ -126,7 +126,7 @@ const MyReducerPlugin = function(system) {
}
```
#### Selectors
##### Selectors
Selectors reach into their namespace's state to retrieve or derive data from the state.
@@ -172,7 +172,7 @@ Once a selector has been defined, you can use it anywhere that you can get a sys
system.exampleSelectors.myFavoriteColor() // gets `favColor` in state for you
```
#### Components
##### Components
You can provide a map of components to be integrated into the system.
@@ -214,7 +214,7 @@ const NeverShowInfoPlugin = function(system) {
}
```
#### Wrap-Actions
##### Wrap-Actions
Wrap Actions allow you to override the behavior of an action in the system.
@@ -262,7 +262,7 @@ const MyWrapActionPlugin = function(system) {
}
```
#### Wrap-Selectors
##### Wrap-Selectors
Wrap Selectors allow you to override the behavior of a selector in the system.
@@ -307,7 +307,7 @@ const MyWrapSelectorsPlugin = function(system) {
}
```
#### Wrap-Components
##### Wrap-Components
Wrap Components allow you to override a component registered within the system.
@@ -381,7 +381,7 @@ const MyWrapComponentPlugin = function(system) {
}
```
#### `rootInjects`
##### `rootInjects`
The `rootInjects` interface allows you to inject values at the top level of the system.
@@ -398,7 +398,7 @@ const MyRootInjectsPlugin = function(system) {
}
```
#### `afterLoad`
##### `afterLoad`
The `afterLoad` plugin method allows you to get a reference to the system after your plugin has been registered.
@@ -431,7 +431,7 @@ const MyMethodProvidingPlugin = function() {
}
```
#### fn
##### fn
The fn interface allows you to add helper functions to the system for use elsewhere.
+1 -1
View File
@@ -17,7 +17,7 @@ Script name | Description
--- | ---
`build` | Build a new set of JS and CSS assets, and output them to `/dist`.
`build-bundle` | Build `swagger-ui-bundle.js` only.
`build-core` | Build `swagger-ui.(js\|css)` only.
`build-core` | Build `swagger-ui.(js|css)` only.
`build-standalone` | Build `swagger-ui-standalone-preset.js` only.
### Testing
+34 -35
View File
@@ -25,13 +25,13 @@ Type notations are formatted like so:
Parameter Name | Description
--- | ---
<a name="configUrl"></a>`configUrl` | `String`. URL to fetch external configuration document from.
<a name="dom_id"></a>`dom_id` |`String`, **REQUIRED** if `domNode` is not provided. The id of a dom element inside which SwaggerUi will put the user interface for swagger.
<a name="domNode"></a>`domNode` | `Element`, **REQUIRED** if `dom_id` is not provided. The HTML DOM element inside which SwaggerUi will put the user interface for swagger. Overrides `dom_id`.
<a name="spec"></a>`spec` | `Object={}`. A JS object describing the OpenAPI Specification. When used, the `url` parameter will not be parsed. This is useful for testing manually-generated specifications without hosting them.
<a name="url"></a>`url` | `String`. The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). Will be ignored if `urls` or `spec` is used.
<a name="urls"></a>`urls` | `Array`. An array of API definition objects (`[{url: "<url1>", name: "<name1>"},{url: "<url2>", name: "<name2>"}]`) used by Topbar plugin. When used and Topbar plugin is enabled, the `url` parameter will not be parsed. Names and URLs must be unique among all items in this array, since they're used as identifiers.
<a name="urls.primaryName"></a>`urls.primaryName` | `String`. When using `urls`, you can use this subparameter. If the value matches the name of a spec provided in `urls`, that spec will be displayed when Swagger-UI loads, instead of defaulting to the first spec in `urls`.
`configUrl` | `String`. URL to fetch external configuration document from.
`dom_id` |`String`, **REQUIRED** if `domNode` is not provided. The id of a dom element inside which SwaggerUi will put the user interface for swagger.
`domNode` | `Element`, **REQUIRED** if `dom_id` is not provided. The HTML DOM element inside which SwaggerUi will put the user interface for swagger. Overrides `dom_id`.
`spec` | `Object={}`. A JS object describing the OpenAPI Specification. When used, the `url` parameter will not be parsed. This is useful for testing manually-generated specifications without hosting them.
`url` | `String`. The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). Will be ignored if `urls` or `spec` is used.
`urls` | `String`. An array of API definition objects (`{url: "<url>", name: "<name>"}`) used by Topbar plugin. When used and Topbar plugin is enabled, the `url` parameter will not be parsed. Names and URLs must be unique among all items in this array, since they're used as identifiers.
`urls.primaryName` | `String`. When using `urls`, you can use this subparameter. If the value matches the name of a spec provided in `urls`, that spec will be displayed when Swagger-UI loads, instead of defaulting to the first spec in `urls`.
##### Plugin system
@@ -39,51 +39,50 @@ Read more about the plugin system in the [Customization documentation](/docs/cus
Parameter Name | Description
--- | ---
<a name="layout"></a>`layout` | `String="BaseLayout"`. The name of a component available via the plugin system to use as the top-level layout for Swagger-UI.
<a name="plugins"></a>`plugins` | `Array=[]`. An array of plugin functions to use in Swagger-UI.
<a name="presets"></a>`presets` | `Array=[SwaggerUI.presets.ApisPreset]`. An array of presets to use in Swagger-UI. Usually, you'll want to include `ApisPreset` if you use this option.
`layout` | `String="BaseLayout"`. The name of a component available via the plugin system to use as the top-level layout for Swagger-UI.
`plugins` | `Array=[]`. An array of plugin functions to use in Swagger-UI.
`presets` | `Array=[SwaggerUI.presets.ApisPreset]`. An array of presets to use in Swagger-UI. Usually, you'll want to include `ApisPreset` if you use this option.
##### Display
Parameter Name | Description
--- | ---
<a name="deepLinking"></a>`deepLinking` | `Boolean=false`. If set to `true`, enables deep linking for tags and operations. See the [Deep Linking documentation](/docs/usage/deep-linking.md) for more information.
<a name="displayOperationId"></a>`displayOperationId` | `Boolean=false`. Controls the display of operationId in operations list. The default is `false`.
<a name="defaultModelsExpandDepth"></a>`defaultModelsExpandDepth` | `Number=1`. The default expansion depth for models (set to -1 completely hide the models).
<a name="defaultModelExpandDepth"></a>`defaultModelExpandDepth` | `Number=1`. The default expansion depth for the model on the model-example section.
<a name="defaultModelRendering"></a>`defaultModelRendering` | `String=["example"*, "model"]`. Controls how the model is shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.)
<a name="displayRequestDuration"></a>`displayRequestDuration` | `Boolean=false`. Controls the display of the request duration (in milliseconds) for Try-It-Out requests.
<a name="docExpansion"></a>`docExpansion` | `String=["list"*, "full", "none"]`. Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing).
<a name="filter"></a>`filter` | `Boolean=false OR String`. If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be Boolean to enable or disable, or a string, in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
<a name="maxDisplayedTags"></a>`maxDisplayedTags` | `Number`. If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
<a name="operationsSorter"></a>`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
<a name="showExtensions"></a>`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
<a name="showCommonExtensions"></a>`showCommonExtensions` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters.
<a name="tagSorter"></a>`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
<a name="onComplete"></a>`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
`deepLinking` | `Boolean=false`. If set to `true`, enables deep linking for tags and operations. See the [Deep Linking documentation](/docs/usage/deep-linking.md) for more information.
`displayOperationId` | `Boolean=false`. Controls the display of operationId in operations list. The default is `false`.
`defaultModelsExpandDepth` | `Number=1`. The default expansion depth for models (set to -1 completely hide the models).
`defaultModelExpandDepth` | `Number=1`. The default expansion depth for the model on the model-example section.
`defaultModelRendering` | `String=["example"*, "model"]`. Controls how the model is shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.)
`displayRequestDuration` | `Boolean=false`. Controls the display of the request duration (in milliseconds) for Try-It-Out requests.
`docExpansion` | `String=["list"*, "full", "none"]`. Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing).
`filter` | `Boolean=false OR String`. If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be Boolean to enable or disable, or a string, in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
`maxDisplayedTags` | `Number`. If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
##### Network
Parameter Name | Description
--- | ---
<a name="oauth2RedirectUrl"></a>`oauth2RedirectUrl` | `String`. OAuth redirect URL.
<a name="requestInteceptor"></a>`requestInterceptor` | `Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the modified request, or a Promise that resolves to the modified request.
<a name="responseInterceptor"></a>`responseInterceptor` |`Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the modified response, or a Promise that resolves to the modified response.
<a name="showMutatedRequest"></a>`showMutatedRequest` | `Boolean=true`. If set to `true`, uses the mutated request returned from a requestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
<a name="supportedSubmitMethods"></a>`supportedSubmitMethods` | `Array=["get", "put", "post", "delete", "options", "head", "patch", "trace"]`. List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. This does not filter the operations from the display.
<a name="validatorUrl"></a>`validatorUrl` | `String="https://online.swagger.io/validator" OR null`. By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation.
`oauth2RedirectUrl` | `String`. OAuth redirect URL.
`requestInterceptor` | `Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the modified request, or a Promise that resolves to the modified request.
`responseInterceptor` |`Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the modified response, or a Promise that resolves to the modified response.
`showMutatedRequest` | `Boolean=true`. If set to `true`, uses the mutated request returned from a requestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
`supportedSubmitMethods` | `Array=["get", "put", "post", "delete", "options", "head", "patch", "trace"]`. List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. This does not filter the operations from the display.
`validatorUrl` | `String="https://online.swagger.io/validator" OR null`. By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation.
##### Macros
Parameter Name | Description
--- | ---
<a name="modelPropertyMacro"></a>`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
<a name="parameterMacro"></a>`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
### Instance methods
Method Name | Description
--- | ---
<a name="initOAuth"></a>`initOAuth` | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
<a name="preauthorizeBasic"></a>`preauthorizeBasic` | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
<a name="preauthorizeApiKey"></a>`preauthorizeApiKey` | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.
`initOAuth` | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
`preauthorizeBasic` | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
`preauthorizeApiKey` | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.
+1 -1
View File
@@ -21,7 +21,7 @@ You can verify CORS support with one of three techniques:
- Curl your API and inspect the headers. For instance:
```bash
$ curl -I "https://petstore.swagger.io/v2/swagger.json"
$ curl -I "http://petstore.swagger.io/v2/swagger.json"
HTTP/1.1 200 OK
Date: Sat, 31 Jan 2015 23:05:44 GMT
Access-Control-Allow-Origin: *
+1 -1
View File
@@ -44,7 +44,7 @@ you could do something like this:
var SwaggerUIBundle = require('swagger-ui-dist').SwaggerUIBundle
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
+3 -3
View File
@@ -3,9 +3,9 @@ You can configure OAuth2 authorization by calling the `initOAuth` method.
Config Name | Description
--- | ---
clientId | Default clientId. MUST be a string
clientSecret | **🚨 Never use this parameter in your production environemnt. It exposes cruicial security information. This feature is intended for dev/test environments only. 🚨** <br>Default clientSecret. MUST be a string
realm | realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl`. MUST be a string
client_id | Default clientId. MUST be a string
client_secret | Default clientSecret. MUST be a string
realm | realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl` . MUST be a string
appName | application name, displayed in authorization popup. MUST be a string
scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
+13 -21
View File
@@ -1,11 +1,10 @@
var path = require("path")
var webpack = require("webpack")
var ExtractTextPlugin = require("extract-text-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
var deepExtend = require("deep-extend")
const {gitDescribeSync} = require("git-describe")
var webpack = require('webpack')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
var deepExtend = require('deep-extend')
const {gitDescribeSync} = require('git-describe')
const os = require("os")
var pkg = require("./package.json")
@@ -29,10 +28,7 @@ var commonRules = [
retainLines: true
}
}],
include: [
path.join(__dirname, "src"),
path.join(__dirname, "node_modules", "object-assign-deep"),
]
include: [ path.join(__dirname, "src") ]
},
{ test: /\.(txt|yaml)(\?.*)?$/,
loader: "raw-loader" },
@@ -52,7 +48,6 @@ module.exports = function(rules, options) {
hot: false,
separateStylesheets: true,
minimize: false,
mangle: false,
longTermCaching: false,
sourcemaps: false,
}, options._special)
@@ -66,15 +61,10 @@ module.exports = function(rules, options) {
}))
}
if( specialOptions.minimize ) { // production mode
if( specialOptions.minimize ) { // production mode
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
mangle: specialOptions.mangle,
beautify: !specialOptions.mangle,
keep_fnames: true
},
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
}),
new webpack.LoaderOptionsPlugin({
@@ -86,8 +76,8 @@ module.exports = function(rules, options) {
plugins.push( new webpack.NoEmitOnErrorsPlugin())
} else { // development mode
plugins.push(new CopyWebpackPlugin([ { from: "test/e2e/specs", to: "test-specs" } ]))
} else { // development mode
plugins.push(new CopyWebpackPlugin([ { from: 'test/e2e/specs', to: 'test-specs' } ]))
}
plugins.push(
@@ -142,7 +132,9 @@ module.exports = function(rules, options) {
"node_modules"
],
extensions: [".web.js", ".js", ".jsx", ".json", ".less"],
alias: {}
alias: {
base: "getbase/src/less/base",
}
},
devtool: specialOptions.sourcemaps ? "nosource-source-map" : false,
-20131
View File
File diff suppressed because it is too large Load Diff
+66 -61
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.17.3",
"version": "3.13.3",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -14,7 +14,7 @@
],
"license": "Apache-2.0",
"scripts": {
"build": "run-p --aggregate-output build-core build-bundle build-standalone",
"build": "npm run build-core && npm run build-bundle && npm run build-standalone",
"build-bundle": "webpack --config webpack-dist-bundle.config.js --colors",
"build-core": "webpack --config webpack-dist.config.js --colors",
"build-standalone": "webpack --config webpack-dist-standalone.config.js --colors",
@@ -24,119 +24,131 @@
"hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/",
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
"deps-size": "webpack -p --config webpack.check.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
"deps-check": "run-s deps-license deps-size",
"deps-check": "npm run deps-license && npm run deps-size",
"lint": "eslint --cache --ext '.js,.jsx' src test",
"lint-errors": "eslint --cache --quiet --ext '.js,.jsx' src test",
"lint-fix": "eslint --cache --ext '.js,.jsx' src test --fix",
"test": "run-s just-test-in-node lint-errors",
"test-in-node": "run-s lint-errors just-test-in-node",
"just-test-in-node": "mocha --require test/setup.js --recursive --compilers js:babel-core/register --require source-map-support test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"test": "npm run lint-errors && npm run just-test-in-node",
"test-in-node": "npm run lint-errors && npm run just-test-in-node",
"just-test": "karma start --config karma.conf.js",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"just-check-coverage": "nyc npm run just-test-in-node",
"test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
"e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render",
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
"hot-e2e-server": "webpack-dev-server --port 3230 --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress",
"e2e": "run-p -r hot-e2e-server mock-api test-e2e",
"open-static": "node -e 'require(\"open\")(\"http://localhost:3002\")'",
"serve-static": "http-server dist/ -i -a 0.0.0.0 -p 3002",
"start": "npm-run-all --parallel serve-static open-static"
"e2e": "npm-run-all --parallel -r hot-e2e-server mock-api test-e2e"
},
"dependencies": {
"@braintree/sanitize-url": "^2.0.2",
"base64-js": "^1.2.0",
"brace": "0.7.0",
"classnames": "^2.2.5",
"commonmark": "^0.28.1",
"core-js": "^2.5.1",
"css.escape": "1.5.1",
"deep-extend": "0.5.1",
"dompurify": "^1.0.4",
"deep-extend": "0.4.1",
"expect": "1.20.2",
"getbase": "^2.8.2",
"ieee754": "^1.1.8",
"immutable": "^3.x.x",
"js-file-download": "^0.4.1",
"js-yaml": "^3.5.5",
"lodash": "^4.17.10",
"memoizee": "^0.4.12",
"lodash": "4.17.5",
"matcher": "^0.1.2",
"memoizee": "0.4.1",
"promise-worker": "^1.1.1",
"prop-types": "^15.5.10",
"react": "^15.6.2",
"react-addons-perf": "^15.4.0",
"react-addons-shallow-compare": "0.14.8",
"react-addons-test-utils": "^15.6.2",
"react-collapse": "^4.0.3",
"react-debounce-input": "^3.2.0",
"react-dom": "^15.6.2",
"react-height": "^2.0.0",
"react-hot-loader": "1.3.1",
"react-immutable-proptypes": "2.1.0",
"react-immutable-pure-component": "^1.1.1",
"react-inspector": "^2.3.0",
"react-markdown": "^2.5.0",
"react-motion": "^0.5.2",
"react-object-inspector": "0.2.1",
"react-redux": "^4.x.x",
"react-split-pane": "^0.1.77",
"react-split-pane": "0.1.70",
"redux": "^3.x.x",
"redux-immutable": "3.0.8",
"redux-logger": "*",
"remarkable": "^1.7.1",
"reselect": "^2.5.4",
"serialize-error": "^2.1.0",
"swagger-client": "^3.8.7",
"reselect": "2.5.3",
"sanitize-html": "^1.14.1",
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.6.0",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
"xml": "1.0.1",
"xml-but-prettier": "^1.0.1",
"yaml-js": "^0.2.3",
"zenscroll": "^4.0.2"
"yaml-js": "0.2.0",
"zenscroll": "4.0.1"
},
"devDependencies": {
"autoprefixer": "^8.4.1",
"autoprefixer": "7.1.1",
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^7.1.0",
"babel-plugin-module-alias": "^1.6.0",
"babel-plugin-transform-es2015-constants": "^6.1.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-es2015-ie": "^6.6.2",
"babel-preset-react": "^6.23.0",
"babel-preset-stage-0": "^6.22.0",
"babel-runtime": "^6.23.0",
"bundlesize": "^0.17.0",
"chromedriver": "^2.38.3",
"chromedriver": "^2.30.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.11",
"deepmerge": "^2.1.0",
"css-loader": "0.28.4",
"deep-extend": "^0.5.0",
"deepmerge": "^1.3.2",
"enzyme": "^2.7.1",
"eslint": "^4.1.1",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-react": "~7.7.0",
"expect": "^1.20.2",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"eslint-plugin-react": "^7.1.0",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "0.11.2",
"file-saver": "^1.3.8",
"git-describe": "^4.0.1",
"imports-loader": "^0.8.0",
"jsdom": "^11.10.0",
"json-loader": "^0.5.7",
"json-server": "^0.12.2",
"less": "^3.0.2",
"license-checker": "^19.0.0",
"mocha": "^5.1.1",
"imports-loader": "0.7.1",
"json-loader": "0.5.4",
"json-server": "^0.11.0",
"karma": "^1.7.0",
"karma-chrome-launcher": "^2.2.0",
"karma-mocha": "^1.3.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "2.0.3",
"less": "2.7.2",
"license-checker": "^11.0.0",
"mocha": "^3.4.2",
"nightwatch": "^0.9.16",
"node-sass": "^4.5.0",
"npm-run-all": "^4.1.2",
"npm-run-all": "4.0.2",
"null-loader": "0.1.1",
"nyc": "^11.3.0",
"open": "0.0.5",
"postcss-loader": "^2.1.5",
"postcss-loader": "2.0.6",
"raw-loader": "0.5.1",
"react-hot-loader": "^1.3.1",
"react-test-renderer": "^15.5.4",
"rimraf": "^2.6.0",
"sass-loader": "^7.0.1",
"selenium-server-standalone-jar": "3.12.0",
"standard": "^11.0.1",
"sass-loader": "^6.0.2",
"selenium-server-standalone-jar": "3.4.0",
"standard": "^10.0.2",
"standard-loader": "^6.0.1",
"style-loader": "^0.21.0",
"style-loader": "0.18.2",
"tachyons-sass": "^4.9.2",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "^1.0.1",
"webpack": "^3.1.0",
"webpack-bundle-size-analyzer": "^2.5.0",
"webpack-cli": "^2.0.4",
"worker-loader": "^1.1.1"
"url-loader": "0.5.9",
"webpack": "^2.6.1",
"webpack-bundle-size-analyzer": "^2.5.0"
},
"config": {
"deps_check_dir": ".deps_check"
@@ -147,19 +159,12 @@
"IE 11"
],
"optionalDependencies": {
"webpack-dev-server": "^2.11.1"
"webpack-dev-server": "2.5.0"
},
"nyc": {
"all": true,
"include": [
"**/src/core/plugins/**.js"
]
},
"bundlesize": [
{
"path": "./dist/swagger-ui-bundle.js",
"maxSize": "1.5 MB",
"compression": "none"
}
]
}
}
-10
View File
@@ -1,10 +0,0 @@
{
"rules": {
"import/no-extraneous-dependencies": [
2,
{
"devDependencies": false
}
]
}
}
+2 -3
View File
@@ -11,7 +11,6 @@ export default class ArrayModel extends Component {
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
specPath: ImPropTypes.list.isRequired,
@@ -19,10 +18,10 @@ export default class ArrayModel extends Component {
}
render(){
let { getComponent, getConfigs, schema, depth, expandDepth, name, displayName, specPath } = this.props
let { getComponent, getConfigs, schema, depth, expandDepth, name, specPath } = this.props
let description = schema.get("description")
let items = schema.get("items")
let title = schema.get("title") || displayName || name
let title = schema.get("title") || name
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
const Markdown = getComponent("Markdown")
+1 -1
View File
@@ -2,7 +2,7 @@ import React from "react"
import PropTypes from "prop-types"
import { Collapse } from "react-collapse"
import { presets } from "react-motion"
import ObjectInspector from "react-inspector"
import ObjectInspector from "react-object-inspector"
import Perf from "react-addons-perf"
export default class Debug extends React.Component {
+7 -13
View File
@@ -1,14 +1,12 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import { highlight } from "core/utils"
import saveAs from "js-file-download"
import { saveAs } from "file-saver"
export default class HighlightCode extends Component {
static propTypes = {
value: PropTypes.string.isRequired,
className: PropTypes.string,
downloadable: PropTypes.bool,
fileName: PropTypes.string
className: PropTypes.string
}
componentDidMount() {
@@ -24,7 +22,8 @@ export default class HighlightCode extends Component {
}
downloadText = () => {
saveAs(this.props.value, this.props.fileName || "response.txt")
let fileToSave = new Blob([this.props.value], {type: "text/plain"})
saveAs(fileToSave, "response")
}
preventYScrollingBeyondElement = (e) => {
@@ -37,26 +36,21 @@ export default class HighlightCode extends Component {
const scrollOffset = visibleHeight + scrollTop
const isElementScrollable = contentHeight > visibleHeight
const isScrollingPastTop = scrollTop === 0 && deltaY < 0
const isScrollingPastBottom = scrollOffset >= contentHeight && deltaY > 0
if (isElementScrollable && (isScrollingPastTop || isScrollingPastBottom)) {
if (isScrollingPastTop || isScrollingPastBottom) {
e.preventDefault()
}
}
render () {
let { value, className, downloadable } = this.props
let { value, className } = this.props
className = className || ""
return (
<div className="highlight-code">
{ !downloadable ? null :
<div className="download-contents" onClick={this.downloadText}>
Download
</div>
}
<div className="download-contents" onClick={this.downloadText}>Download</div>
<pre
ref={this.initializeComponent}
onWheel={this.preventYScrollingBeyondElement}
+81 -15
View File
@@ -6,35 +6,63 @@ export default class BaseLayout extends React.Component {
static propTypes = {
errSelectors: PropTypes.object.isRequired,
errActions: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
}
onFilterChange =(e) => {
let {target: {value}} = e
this.props.layoutActions.updateFilter(value)
}
render() {
let {specSelectors, getComponent} = this.props
let {
specSelectors,
specActions,
getComponent,
layoutSelectors,
oas3Selectors,
oas3Actions
} = this.props
let info = specSelectors.info()
let url = specSelectors.url()
let basePath = specSelectors.basePath()
let host = specSelectors.host()
let securityDefinitions = specSelectors.securityDefinitions()
let externalDocs = specSelectors.externalDocs()
let schemes = specSelectors.schemes()
let servers = specSelectors.servers()
let SvgAssets = getComponent("SvgAssets")
let InfoContainer = getComponent("InfoContainer", true)
let VersionPragmaFilter = getComponent("VersionPragmaFilter")
let Info = getComponent("info")
let Operations = getComponent("operations", true)
let Models = getComponent("Models", true)
let AuthorizeBtn = getComponent("authorizeBtn", true)
let Row = getComponent("Row")
let Col = getComponent("Col")
let ServersContainer = getComponent("ServersContainer", true)
let Servers = getComponent("Servers")
let Errors = getComponent("errors", true)
const SchemesContainer = getComponent("SchemesContainer", true)
const FilterContainer = getComponent("FilterContainer", true)
let isSwagger2 = specSelectors.isSwagger2()
let isOAS3 = specSelectors.isOAS3()
let isLoading = specSelectors.loadingStatus() === "loading"
let isFailed = specSelectors.loadingStatus() === "failed"
let filter = layoutSelectors.currentFilter()
let inputStyle = {}
if(isFailed) inputStyle.color = "red"
if(isLoading) inputStyle.color = "#aaa"
const Schemes = getComponent("schemes")
const isSpecEmpty = !specSelectors.specStr()
if(isSpecEmpty) {
let loadingMessage
let isLoading = specSelectors.loadingStatus() === "loading"
if(isLoading) {
loadingMessage = <div className="loading"></div>
} else {
@@ -52,19 +80,57 @@ export default class BaseLayout extends React.Component {
<div className='swagger-ui'>
<SvgAssets />
<VersionPragmaFilter isSwagger2={isSwagger2} isOAS3={isOAS3} alsoShow={<Errors/>}>
<div>
<Errors/>
<Row className="information-container">
<Col mobile={12}>
<InfoContainer/>
{ info.count() ? (
<Info info={ info } url={ url } host={ host } basePath={ basePath } externalDocs={externalDocs} getComponent={getComponent}/>
) : null }
</Col>
</Row>
{ schemes && schemes.size || securityDefinitions ? (
<div className="scheme-container">
<Col className="schemes wrapper" mobile={12}>
{ schemes && schemes.size ? (
<Schemes
currentScheme={specSelectors.operationScheme()}
schemes={ schemes }
specActions={ specActions } />
) : null }
<SchemesContainer/>
{ securityDefinitions ? (
<AuthorizeBtn />
) : null }
</Col>
</div>
) : null }
<ServersContainer/>
{ servers && servers.size ? (
<div className="global-server-container">
<Col className="servers wrapper" mobile={12}>
<span className="servers-title">Server</span>
<Servers
servers={servers}
currentServer={oas3Selectors.selectedServer()}
setSelectedServer={oas3Actions.setSelectedServer}
setServerVariableValue={oas3Actions.setServerVariableValue}
getServerVariable={oas3Selectors.serverVariableValue}
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
/>
</Col>
</div>
<FilterContainer/>
) : null}
{
filter === null || filter === false ? null :
<div className="filter-container">
<Col className="filter wrapper" mobile={12}>
<input className="operation-filter-input" placeholder="Filter by tag" type="text" onChange={this.onFilterChange} value={filter === true || filter === "true" ? "" : filter} disabled={isLoading} style={inputStyle} />
</Col>
</div>
}
<Row>
<Col mobile={12} desktop={12} >
@@ -76,7 +142,7 @@ export default class BaseLayout extends React.Component {
<Models/>
</Col>
</Row>
</VersionPragmaFilter>
</div>
</div>
)
}
-1
View File
@@ -9,7 +9,6 @@ export default class ModelWrapper extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
+2 -3
View File
@@ -10,7 +10,6 @@ export default class Model extends ImmutablePureComponent {
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
@@ -34,7 +33,7 @@ export default class Model extends ImmutablePureComponent {
}
render () {
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath, displayName } = this.props
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath } = this.props
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
@@ -52,7 +51,7 @@ export default class Model extends ImmutablePureComponent {
if(!schema) {
return <span className="model model-title">
<span className="model-title__text">{ displayName || name }</span>
<span className="model-title__text">{ name }</span>
<img src={require("core/../img/rolling-load.svg")} height={"20px"} width={"20px"} style={{
marginLeft: "1em",
position: "relative",
+5 -16
View File
@@ -41,7 +41,6 @@ export default class Models extends Component {
const ModelWrapper = getComponent("ModelWrapper")
const Collapse = getComponent("Collapse")
const ModelCollapse = getComponent("ModelCollapse")
const JumpToPath = getComponent("JumpToPath")
return <section className={ showModels ? "models is-open" : "models"}>
<h4 onClick={() => layoutActions.show("models", !showModels)}>
@@ -52,12 +51,9 @@ export default class Models extends Component {
</h4>
<Collapse isOpened={showModels}>
{
definitions.entrySeq().map(([name])=>{
definitions.entrySeq().map( ( [ name ])=>{
const fullPath = [...specPathBase, name]
const schema = specSelectors.specResolvedSubtree(fullPath)|| Im.Map()
const rawSchema = specSelectors.specJson().getIn(fullPath, Im.Map())
const displayName = schema.get("title") || rawSchema.get("title") || name
const schema = specSelectors.specResolvedSubtree([...specPathBase, name])
if(layoutSelectors.isShown(["models", name], false) && schema === undefined) {
// Firing an action in a container render is not great,
@@ -65,13 +61,10 @@ export default class Models extends Component {
this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])
}
const specPath = Im.List([...specPathBase, name])
const content = <ModelWrapper name={ name }
expandDepth={ defaultModelsExpandDepth }
schema={ schema || Im.Map() }
displayName={displayName}
specPath={specPath}
schema={ schema || Im.Map() }
specPath={Im.List([...specPathBase, name])}
getComponent={ getComponent }
specSelectors={ specSelectors }
getConfigs = {getConfigs}
@@ -79,19 +72,15 @@ export default class Models extends Component {
layoutActions = {layoutActions}/>
const title = <span className="model-box">
<span className="model model-title">
{displayName}
</span>
<span className="model model-title">{name}</span>
</span>
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
<span className="models-jump-to-path"><JumpToPath specPath={specPath} /></span>
<ModelCollapse
classes="model-box"
collapsedContent={this.getCollapsedContent(name)}
onToggle={this.handleToggle}
title={title}
displayName={displayName}
modelName={name}
hideSelfOnExpand={true}
expanded={defaultModelsExpandDepth > 1}
+3 -4
View File
@@ -15,7 +15,6 @@ export default class ObjectModel extends Component {
onToggle: PropTypes.func,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number,
@@ -23,7 +22,7 @@ export default class ObjectModel extends Component {
}
render(){
let { schema, name, displayName, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { specSelectors,expandDepth } = otherProps
const { isOAS3 } = specSelectors
@@ -36,7 +35,7 @@ export default class ObjectModel extends Component {
let description = schema.get("description")
let properties = schema.get("properties")
let additionalProperties = schema.get("additionalProperties")
let title = schema.get("title") || displayName || name
let title = schema.get("title") || name
let requiredProperties = schema.get("required")
const JumpToPath = getComponent("JumpToPath", true)
@@ -79,7 +78,7 @@ export default class ObjectModel extends Component {
{
<table className="model"><tbody>
{
!description ? null : <tr style={{ color: "#666", fontStyle: "italic" }}>
!description ? null : <tr style={{ color: "#999", fontStyle: "italic" }}>
<td>description:</td>
<td>
<Markdown source={ description } />
+4 -15
View File
@@ -1,9 +1,6 @@
import React from "react"
import URL from "url-parse"
import PropTypes from "prop-types"
import { sanitizeUrl } from "core/utils"
import win from "core/window"
export default class OnlineValidatorBadge extends React.Component {
static propTypes = {
@@ -14,28 +11,20 @@ export default class OnlineValidatorBadge extends React.Component {
constructor(props, context) {
super(props, context)
let { getConfigs } = props
let { specSelectors, getConfigs } = props
let { validatorUrl } = getConfigs()
this.state = {
url: this.getDefinitionUrl(),
url: specSelectors.url(),
validatorUrl: validatorUrl === undefined ? "https://online.swagger.io/validator" : validatorUrl
}
}
getDefinitionUrl = () => {
// TODO: test this behavior by stubbing `window.location` in an Enzyme/JSDom env
let { specSelectors } = this.props
const urlObject = new URL(specSelectors.url(), win.location)
return urlObject.toString()
}
componentWillReceiveProps(nextProps) {
let { getConfigs } = nextProps
let { specSelectors, getConfigs } = nextProps
let { validatorUrl } = getConfigs()
this.setState({
url: this.getDefinitionUrl(),
url: specSelectors.url(),
validatorUrl: validatorUrl === undefined ? "https://online.swagger.io/validator" : validatorUrl
})
}
-108
View File
@@ -1,108 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import Im from "immutable"
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
export default class OperationTag extends React.Component {
static defaultProps = {
tagObj: Im.fromJS({}),
tag: "",
}
static propTypes = {
tagObj: ImPropTypes.map.isRequired,
tag: PropTypes.string.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired,
getComponent: PropTypes.func.isRequired,
children: PropTypes.element,
}
render() {
const {
tagObj,
tag,
children,
layoutSelectors,
layoutActions,
getConfigs,
getComponent,
} = this.props
let {
docExpansion,
deepLinking,
} = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
const DeepLink = getComponent("DeepLink")
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
let isShownKey = ["operations-tag", createDeepLinkPath(tag)]
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
return (
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} >
<h4
onClick={() => layoutActions.show(isShownKey, !showTag)}
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
id={isShownKey.join("-")}>
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={showTag}
path={tag}
text={tag} />
{ !tagDescription ? <small></small> :
<small>
<Markdown source={tagDescription} />
</small>
}
<div>
{ !tagExternalDocsDescription ? null :
<small>
{ tagExternalDocsDescription }
{ tagExternalDocsUrl ? ": " : null }
{ tagExternalDocsUrl ?
<a
href={sanitizeUrl(tagExternalDocsUrl)}
onClick={(e) => e.stopPropagation()}
target={"_blank"}
>{tagExternalDocsUrl}</a> : null
}
</small>
}
</div>
<button
className="expand-operation"
title={showTag ? "Collapse operation": "Expand operation"}
onClick={() => layoutActions.show(isShownKey, !showTag)}>
<svg className="arrow" width="20" height="20">
<use href={showTag ? "#large-arrow-down" : "#large-arrow"} xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
</svg>
</button>
</h4>
<Collapse isOpened={showTag}>
{children}
</Collapse>
</div>
)
}
}
+85 -39
View File
@@ -1,6 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import Im from "immutable"
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
const SWAGGER2_OPERATION_METHODS = [
"get", "put", "post", "delete", "options", "head", "patch"
@@ -37,12 +38,18 @@ export default class Operations extends React.Component {
let taggedOps = specSelectors.taggedOperations()
const OperationContainer = getComponent("OperationContainer", true)
const OperationTag = getComponent("OperationTag")
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
const DeepLink = getComponent("DeepLink")
let {
docExpansion,
maxDisplayedTags,
deepLinking
} = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
let filter = layoutSelectors.currentFilter()
if (filter) {
@@ -59,49 +66,88 @@ export default class Operations extends React.Component {
<div>
{
taggedOps.map( (tagObj, tag) => {
const operations = tagObj.get("operations")
let operations = tagObj.get("operations")
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
let isShownKey = ["operations-tag", createDeepLinkPath(tag)]
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
return (
<OperationTag
key={"operation-" + tag}
tagObj={tagObj}
tag={tag}
layoutSelectors={layoutSelectors}
layoutActions={layoutActions}
getConfigs={getConfigs}
getComponent={getComponent}>
{
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const specPath = Im.List(["paths", path, method])
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
<h4
onClick={() => layoutActions.show(isShownKey, !showTag)}
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
id={isShownKey.join("-")}>
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={showTag}
path={tag}
text={tag} />
{ !tagDescription ? <small></small> :
<small>
<Markdown source={tagDescription} />
</small>
}
<div>
{ !tagExternalDocsDescription ? null :
<small>
{ tagExternalDocsDescription }
{ tagExternalDocsUrl ? ": " : null }
{ tagExternalDocsUrl ?
<a
href={sanitizeUrl(tagExternalDocsUrl)}
onClick={(e) => e.stopPropagation()}
target={"_blank"}
>{tagExternalDocsUrl}</a> : null
}
</small>
}
</div>
<button className="expand-operation" title={showTag ? "Collapse operation": "Expand operation"} onClick={() => layoutActions.show(isShownKey, !showTag)}>
<svg className="arrow" width="20" height="20">
<use href={showTag ? "#large-arrow-down" : "#large-arrow"} xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
</svg>
</button>
</h4>
<Collapse isOpened={showTag}>
{
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const specPath = Im.List(["paths", path, method])
// FIXME: (someday) this logic should probably be in a selector,
// but doing so would require further opening up
// selectors to the plugin system, to allow for dynamic
// overriding of low-level selectors that other selectors
// rely on. --KS, 12/17
const validMethods = specSelectors.isOAS3() ?
OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS
// FIXME: (someday) this logic should probably be in a selector,
// but doing so would require further opening up
// selectors to the plugin system, to allow for dynamic
// overriding of low-level selectors that other selectors
// rely on. --KS, 12/17
const validMethods = specSelectors.isOAS3() ?
OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS
if(validMethods.indexOf(method) === -1) {
return null
}
if(validMethods.indexOf(method) === -1) {
return null
}
return <OperationContainer
key={`${path}-${method}`}
specPath={specPath}
op={op}
path={path}
method={method}
tag={tag}
/>
}).toArray()
}
</OperationTag>
)
return <OperationContainer
key={`${path}-${method}`}
specPath={specPath}
op={op}
path={path}
method={method}
tag={tag}
/>
}).toArray()
}
</Collapse>
</div>
)
}).toArray()
}
+5 -4
View File
@@ -46,10 +46,11 @@ export default class ParamBody extends PureComponent {
}
updateValues = (props) => {
let { param, isExecute, consumesValue="" } = props
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})
let isXml = /xml/i.test(consumesValue)
let isJson = /json/i.test(consumesValue)
let paramValue = isXml ? param.get("value_xml") : param.get("value")
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
if ( paramValue !== undefined ) {
let val = !paramValue && isJson ? "{}" : paramValue
@@ -78,7 +79,7 @@ export default class ParamBody extends PureComponent {
this._onChange(value, isXml)
}
_onChange = (val, isXml) => { (this.props.onChange || NOOP)(val, isXml) }
_onChange = (val, isXml) => { (this.props.onChange || NOOP)(this.props.param, val, isXml) }
handleOnChange = e => {
const {consumesValue} = this.props
@@ -106,7 +107,7 @@ export default class ParamBody extends PureComponent {
const HighlightCode = getComponent("highlightCode")
const ContentType = getComponent("contentType")
// for domains where specSelectors not passed
let parameter = specSelectors ? specSelectors.parameterWithMetaByIdentity(pathMethod, param) : param
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : param
let errors = parameter.get("errors", List())
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
+31 -46
View File
@@ -3,13 +3,12 @@ import { Map } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import win from "core/window"
import { getExtensions, getCommonExtensions } from "core/utils"
import { getExtensions } from "core/utils"
export default class ParameterRow extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
param: PropTypes.object.isRequired,
rawParam: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
fn: PropTypes.object.isRequired,
isExecute: PropTypes.bool,
@@ -23,7 +22,20 @@ export default class ParameterRow extends Component {
constructor(props, context) {
super(props, context)
this.setDefaultValue()
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let xExampleValue = param.get("x-example")
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
if( param.get("in") !== "body" ) {
if ( xExampleValue !== undefined && value === undefined && specSelectors.isSwagger2() ) {
this.onChangeWrapper(xExampleValue)
} else if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
}
}
}
componentWillReceiveProps(props) {
@@ -31,7 +43,8 @@ export default class ParameterRow extends Component {
let { isOAS3 } = specSelectors
let example = param.get("example")
let parameter = specSelectors.parameterWithMetaByIdentity(pathMethod, param) || param
let defaultValue = param.get("default")
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
let enumValue
if(isOAS3()) {
@@ -48,6 +61,8 @@ export default class ParameterRow extends Component {
value = paramValue
} else if ( example !== undefined ) {
value = example
} else if ( defaultValue !== undefined) {
value = defaultValue
} else if ( param.get("required") && enumValue && enumValue.size ) {
value = enumValue.first()
}
@@ -57,40 +72,17 @@ export default class ParameterRow extends Component {
}
}
onChangeWrapper = (value, isXml = false) => {
let { onChange, rawParam } = this.props
return onChange(rawParam, value, isXml)
}
setDefaultValue = () => {
let { specSelectors, pathMethod, param } = this.props
if (param.get("value") !== undefined) {
return
}
let schema = specSelectors.isOAS3() ? param.get("schema", Map({})) : param
let defaultValue = schema.get("default")
let xExampleValue = param.get("x-example") // Swagger 2 only
let parameter = specSelectors.parameterWithMetaByIdentity(pathMethod, param)
let value = parameter ? parameter.get("value") : ""
if( param.get("in") !== "body" ) {
if ( xExampleValue !== undefined && value === undefined && specSelectors.isSwagger2() ) {
this.onChangeWrapper(xExampleValue)
} else if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
}
}
onChangeWrapper = (value) => {
let { onChange, param } = this.props
return onChange(param, value)
}
render() {
let {param, rawParam, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath} = this.props
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath} = this.props
let { isOAS3 } = specSelectors
const { showExtensions, showCommonExtensions } = getConfigs()
const { showExtensions } = getConfigs()
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
@@ -102,7 +94,7 @@ export default class ParameterRow extends Component {
param={param}
consumes={ specSelectors.operationConsumes(pathMethod) }
consumesValue={ specSelectors.contentTypeValues(pathMethod).get("requestContentType") }
onChange={this.onChangeWrapper}
onChange={onChange}
onChangeConsumes={onChangeConsumes}
isExecute={ isExecute }
specSelectors={ specSelectors }
@@ -113,18 +105,16 @@ export default class ParameterRow extends Component {
const Markdown = getComponent("Markdown")
const ParameterExt = getComponent("ParameterExt")
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam)
let format = param.get("format")
let paramWithMeta = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
let schema = isOAS3 && isOAS3() ? param.get("schema") : param
let type = schema.get("type")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
let required = param.get("required")
let itemType = schema.getIn(["items", "type"])
let value = paramWithMeta ? paramWithMeta.get("value") : ""
let commonExt = showCommonExtensions ? getCommonExtensions(param) : null
let extensions = showExtensions ? getExtensions(param) : null
let extensions = getExtensions(param)
let paramItems // undefined
let paramEnum // undefined
@@ -157,22 +147,17 @@ export default class ParameterRow extends Component {
}
return (
<tr className="parameters">
<tr>
<td className="col parameters-col_name">
<div className={required ? "parameter__name required" : "parameter__name"}>
{ param.get("name") }
{ !required ? null : <span style={{color: "red"}}>&nbsp;*</span> }
</div>
<div className="parameter__type">
{ type }
{ itemType && `[${itemType}]` }
{ format && <span className="prop-format">(${format})</span>}
</div>
<div className="parameter__type">{ type } { itemType && `[${itemType}]` }</div>
<div className="parameter__deprecated">
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
</div>
<div className="parameter__in">({ param.get("in") })</div>
{ !showCommonExtensions || !commonExt.size ? null : commonExt.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
{ !showExtensions || !extensions.size ? null : extensions.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
</td>
@@ -212,7 +197,7 @@ export default class ParameterRow extends Component {
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ param.get("schema") }
schema={ schema }
example={ bodyParam }/>
: null
}
+3 -4
View File
@@ -36,11 +36,11 @@ export default class Parameters extends Component {
onChange = ( param, value, isXml ) => {
let {
specActions: { changeParamByIdentity },
specActions: { changeParam },
onChangeKey,
} = this.props
changeParamByIdentity(onChangeKey, param, value, isXml)
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -101,8 +101,7 @@ export default class Parameters extends Component {
specPath={specPath.push(i.toString())}
getComponent={ getComponent }
getConfigs={ getConfigs }
rawParam={ parameter }
param={ specSelectors.parameterWithMetaByIdentity(pathMethod, parameter) }
param={ specSelectors.parameterWithMeta(pathMethod, parameter.get("name"), parameter.get("in")) }
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
+2 -3
View File
@@ -10,12 +10,11 @@ export default class Primitive extends Component {
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
depth: PropTypes.number
}
render(){
let { schema, getComponent, getConfigs, name, displayName, depth } = this.props
let { schema, getComponent, getConfigs, name, depth } = this.props
const { showExtensions } = getConfigs()
@@ -28,7 +27,7 @@ export default class Primitive extends Component {
let format = schema.get("format")
let xml = schema.get("xml")
let enumArray = schema.get("enum")
let title = schema.get("title") || displayName || name
let title = schema.get("title") || name
let description = schema.get("description")
let extensions = getExtensions(schema)
let properties = schema
+17 -5
View File
@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import Remarkable from "remarkable"
import DomPurify from "dompurify"
import sanitize from "sanitize-html"
import cx from "classnames"
// eslint-disable-next-line no-useless-escape
@@ -35,13 +35,25 @@ function Markdown({ source, className = "" }) {
Markdown.propTypes = {
source: PropTypes.string.isRequired,
className: PropTypes.string
className: PropTypes.string.isRequired
}
export default Markdown
const sanitizeOptions = {
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img", "span" ]),
allowedAttributes: {
...sanitize.defaults.allowedAttributes,
"img": sanitize.defaults.allowedAttributes.img.concat(["title"]),
"td": [ "colspan" ],
"*": [ "class" ]
},
allowedSchemesByTag: { img: [ "http", "https", "data" ] },
textFilter: function(text) {
return text.replace(/&quot;/g, "\"")
}
}
export function sanitizer(str) {
return DomPurify.sanitize(str, {
ADD_ATTR: ["target"]
})
return sanitize(str, sanitizeOptions)
}
+9 -57
View File
@@ -5,10 +5,7 @@ import lowerCase from "lodash/lowerCase"
import { extractFileNameFromContentDispositionHeader } from "core/utils"
import win from "core/window"
export default class ResponseBody extends React.PureComponent {
state = {
parsedContent: null
}
export default class ResponseBody extends React.Component {
static propTypes = {
content: PropTypes.any.isRequired,
@@ -18,41 +15,9 @@ export default class ResponseBody extends React.PureComponent {
url: PropTypes.string
}
updateParsedContent = (prevContent) => {
const { content } = this.props
if(prevContent === content) {
return
}
if(content && content instanceof Blob) {
var reader = new FileReader()
reader.onload = () => {
this.setState({
parsedContent: reader.result
})
}
reader.readAsText(content)
} else {
this.setState({
parsedContent: content.toString()
})
}
}
componentDidMount() {
this.updateParsedContent(null)
}
componentDidUpdate(prevProps) {
this.updateParsedContent(prevProps.content)
}
render() {
let { content, contentType, url, headers={}, getComponent } = this.props
const { parsedContent } = this.state
const HighlightCode = getComponent("highlightCode")
const downloadName = "response_" + new Date().getTime()
let body, bodyEl
url = url || ""
@@ -64,7 +29,9 @@ export default class ResponseBody extends React.PureComponent {
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
// Download
if ("Blob" in window) {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (!isSafari && "Blob" in window) {
let type = contentType || "text/html"
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
let href = window.URL.createObjectURL(blob)
@@ -99,7 +66,7 @@ export default class ResponseBody extends React.PureComponent {
body = "can't parse JSON. Raw result:\n\n" + content
}
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.json`} value={ body } />
bodyEl = <HighlightCode value={ body } />
// XML
} else if (/xml/i.test(contentType)) {
@@ -107,11 +74,11 @@ export default class ResponseBody extends React.PureComponent {
textNodesOnSameLine: true,
indentor: " "
})
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.xml`} value={ body } />
bodyEl = <HighlightCode value={ body } />
// HTML or Plain Text
} else if (lowerCase(contentType) === "text/html" || /text\/plain/.test(contentType)) {
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.html`} value={ content } />
bodyEl = <HighlightCode value={ content } />
// Image
} else if (/^image\//i.test(contentType)) {
@@ -125,25 +92,10 @@ export default class ResponseBody extends React.PureComponent {
} else if (/^audio\//i.test(contentType)) {
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
} else if (typeof content === "string") {
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.txt`} value={ content } />
bodyEl = <HighlightCode value={ content } />
} else if ( content.size > 0 ) {
// We don't know the contentType, but there was some content returned
if(parsedContent) {
// We were able to squeeze something out of content
// in `updateParsedContent`, so let's display it
bodyEl = <div>
<p className="i">
Unrecognized response type; displaying content as text.
</p>
<HighlightCode downloadable fileName={`${downloadName}.txt`} value={ parsedContent } />
</div>
} else {
// Give up
bodyEl = <p className="i">
Unrecognized response type; unable to display.
</p>
}
bodyEl = <div>Unknown response type</div>
} else {
// We don't know the contentType and there was no content returned
bodyEl = null
+2 -2
View File
@@ -37,12 +37,12 @@ export default class Schemes extends React.Component {
}
render() {
let { schemes, currentScheme } = this.props
let { schemes } = this.props
return (
<label htmlFor="schemes">
<span className="schemes-title">Schemes</span>
<select onChange={ this.onChange } value={currentScheme}>
<select onChange={ this.onChange }>
{ schemes.valueSeq().map(
( scheme ) => <option value={ scheme } key={ scheme }>{ scheme }</option>
).toArray()}
+2 -2
View File
@@ -21,8 +21,8 @@ export default class TryItOutButton extends React.Component {
return (
<div className="try-out">
{
enabled ? <button className="btn try-out__btn cancel" onClick={ onCancelClick }>Cancel</button>
: <button className="btn try-out__btn" onClick={ onTryoutClick }>Try it out </button>
enabled ? <button className="btn try-out__btn cancel" onClick={ onTryoutClick }>Cancel</button>
: <button className="btn try-out__btn" onClick={ onCancelClick }>Try it out </button>
}
</div>
)
@@ -1,54 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
export default class VersionPragmaFilter extends React.PureComponent {
static propTypes = {
isSwagger2: PropTypes.bool.isRequired,
isOAS3: PropTypes.bool.isRequired,
bypass: PropTypes.bool,
alsoShow: PropTypes.element,
children: PropTypes.any,
}
static defaultProps = {
alsoShow: null,
children: null,
bypass: false,
}
render() {
const { bypass, isSwagger2, isOAS3, alsoShow } = this.props
if(bypass) {
return <div>{ this.props.children }</div>
}
if(isSwagger2 && isOAS3) {
return <div className="version-pragma">
{alsoShow}
<div className="version-pragma__message version-pragma__message--ambiguous">
<div>
<h3>Unable to render this definition</h3>
<p><code>swagger</code> and <code>openapi</code> fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields.</p>
<p>Supported version fields are <code>swagger: {"\"2.0\""}</code> and those that match <code>openapi: 3.0.n</code> (for example, <code>openapi: 3.0.0</code>).</p>
</div>
</div>
</div>
}
if(!isSwagger2 && !isOAS3) {
return <div className="version-pragma">
{alsoShow}
<div className="version-pragma__message version-pragma__message--missing">
<div>
<h3>Unable to render this definition</h3>
<p>The provided definition does not specify a valid version field.</p>
<p>Please indicate a valid Swagger or OpenAPI version field. Supported version fields are <code>swagger: {"\"2.0\""}</code> and those that match <code>openapi: 3.0.n</code> (for example, <code>openapi: 3.0.0</code>).</p>
</div>
</div>
</div>
}
return <div>{ this.props.children }</div>
}
}
+2 -2
View File
@@ -113,11 +113,11 @@ export default class OperationContainer extends PureComponent {
layoutActions.show(["operations", tag, operationId], !isShown)
}
onCancelClick=() => {
onTryoutClick =() => {
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
}
onTryoutClick =() => {
onCancelClick =() => {
let { specActions, path, method } = this.props
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
specActions.clearValidateParams([path, method])
-44
View File
@@ -1,44 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
export default class FilterContainer extends React.Component {
static propTypes = {
specSelectors: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
}
onFilterChange = (e) => {
const {target: {value}} = e
this.props.layoutActions.updateFilter(value)
}
render () {
const {specSelectors, layoutSelectors, getComponent} = this.props
const Col = getComponent("Col")
const isLoading = specSelectors.loadingStatus() === "loading"
const isFailed = specSelectors.loadingStatus() === "failed"
const filter = layoutSelectors.currentFilter()
const inputStyle = {}
if (isFailed) inputStyle.color = "red"
if (isLoading) inputStyle.color = "#aaa"
return (
<div>
{filter === null || filter === false ? null :
<div className="filter-container">
<Col className="filter wrapper" mobile={12}>
<input className="operation-filter-input" placeholder="Filter by tag" type="text"
onChange={this.onFilterChange} value={filter === true || filter === "true" ? "" : filter}
disabled={isLoading} style={inputStyle}/>
</Col>
</div>
}
</div>
)
}
}
-32
View File
@@ -1,32 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
export default class InfoContainer extends React.Component {
static propTypes = {
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
}
render () {
const {specSelectors, getComponent} = this.props
const info = specSelectors.info()
const url = specSelectors.url()
const basePath = specSelectors.basePath()
const host = specSelectors.host()
const externalDocs = specSelectors.externalDocs()
const Info = getComponent("info")
return (
<div>
{info && info.count() ? (
<Info info={info} url={url} host={host} basePath={basePath} externalDocs={externalDocs}
getComponent={getComponent}/>
) : null}
</div>
)
}
}
-43
View File
@@ -1,43 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
export default class SchemesContainer extends React.Component {
static propTypes = {
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
}
render () {
const {specActions, specSelectors, getComponent} = this.props
const currentScheme = specSelectors.operationScheme()
const schemes = specSelectors.schemes()
const securityDefinitions = specSelectors.securityDefinitions()
const Col = getComponent("Col")
const AuthorizeBtn = getComponent("authorizeBtn", true)
const Schemes = getComponent("schemes")
return (
<div>
{schemes && schemes.size || securityDefinitions ? (
<div className="scheme-container">
<Col className="schemes wrapper" mobile={12}>
{schemes && schemes.size ? (
<Schemes
currentScheme={currentScheme}
schemes={schemes}
specActions={specActions}
/>
) : null}
{securityDefinitions ? (
<AuthorizeBtn/>
) : null}
</Col>
</div>
) : null}
</div>
)
}
}
+7 -12
View File
@@ -27,7 +27,7 @@ module.exports = function SwaggerUI(opts) {
const defaults = {
// Some general settings, that we floated to the top
dom_id: null, // eslint-disable-line camelcase
dom_id: null,
domNode: null,
spec: {},
url: "",
@@ -49,7 +49,6 @@ module.exports = function SwaggerUI(opts) {
defaultModelExpandDepth: 1,
defaultModelsExpandDepth: 1,
showExtensions: false,
showCommonExtensions: false,
supportedSubmitMethods: [
"get",
"put",
@@ -131,6 +130,10 @@ module.exports = function SwaggerUI(opts) {
var system = store.getSystem()
const downloadSpec = (fetchedConfig) => {
if(typeof constructorConfig !== "object") {
return system
}
let localConfig = system.specSelectors.getLocalConfig ? system.specSelectors.getLocalConfig() : {}
let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig)
@@ -140,7 +143,6 @@ module.exports = function SwaggerUI(opts) {
}
store.setConfigs(mergedConfig)
system.configsActions.loaded()
if (fetchedConfig !== null) {
if (!queryConfig.url && typeof mergedConfig.spec === "object" && Object.keys(mergedConfig.spec).length) {
@@ -168,17 +170,10 @@ module.exports = function SwaggerUI(opts) {
return system
}
const configUrl = queryConfig.config || constructorConfig.configUrl
let configUrl = queryConfig.config || constructorConfig.configUrl
if (!configUrl || !system.specActions || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl({
url: configUrl,
loadRemoteConfig: true,
requestInterceptor: constructorConfig.requestInterceptor,
responseInterceptor: constructorConfig.responseInterceptor,
}, downloadSpec)) {
if (!configUrl || !system.specActions.getConfigByUrl || system.specActions.getConfigByUrl && !system.specActions.getConfigByUrl(configUrl, downloadSpec)) {
return downloadSpec()
} else {
system.specActions.getConfigByUrl(configUrl, downloadSpec)
}
return system
+5 -69
View File
@@ -1,10 +1,8 @@
import React, { PureComponent, Component } from "react"
import PropTypes from "prop-types"
import { List, fromJS } from "immutable"
import cx from "classnames"
import ImPropTypes from "react-immutable-proptypes"
import DebounceInput from "react-debounce-input"
import { getSampleSchema } from "core/utils"
//import "less/json-schema-form"
const noop = ()=> {}
@@ -17,7 +15,6 @@ const JsonSchemaPropShape = {
schema: PropTypes.object,
errors: ImPropTypes.list,
required: PropTypes.bool,
dispatchInitialValue: PropTypes.bool,
description: PropTypes.any
}
@@ -35,13 +32,6 @@ export class JsonSchemaForm extends Component {
static propTypes = JsonSchemaPropShape
static defaultProps = JsonSchemaDefaultProps
componentDidMount() {
const { dispatchInitialValue, value, onChange } = this.props
if(dispatchInitialValue) {
onChange(value)
}
}
render() {
let { schema, errors, value, onChange, getComponent, fn } = this.props
@@ -111,7 +101,7 @@ export class JsonSchema_array extends PureComponent {
constructor(props, context) {
super(props, context)
this.state = { value: valueOrEmptyList(props.value)}
this.state = {value: props.value}
}
componentWillReceiveProps(props) {
@@ -135,7 +125,7 @@ export class JsonSchema_array extends PureComponent {
addItem = () => {
this.setState(state => {
state.value = valueOrEmptyList(state.value)
state.value = state.value || List()
return {
value: state.value.push("")
}
@@ -174,7 +164,7 @@ export class JsonSchema_array extends PureComponent {
return (
<div>
{ !value || !value.count || value.count() < 1 ? null :
{ !value || value.count() < 1 ? null :
value.map( (item,i) => {
let schema = Object.assign({}, itemSchema)
if ( errors.length ) {
@@ -201,7 +191,7 @@ export class JsonSchema_boolean extends Component {
onEnumChange = (val) => this.props.onChange(val)
render() {
let { getComponent, value, errors, schema, required } = this.props
let { getComponent, value, errors, schema } = this.props
errors = errors.toJS ? errors.toJS() : []
const Select = getComponent("Select")
@@ -210,61 +200,7 @@ export class JsonSchema_boolean extends Component {
title={ errors.length ? errors : ""}
value={ String(value) }
allowedValues={ fromJS(schema.enum || ["true", "false"]) }
allowEmptyValue={ !schema.enum || !required }
allowEmptyValue={ !this.props.required }
onChange={ this.onEnumChange }/>)
}
}
export class JsonSchema_object extends PureComponent {
constructor() {
super()
}
static propTypes = JsonSchemaPropShape
static defaultProps = JsonSchemaDefaultProps
componentDidMount() {
if(!this.props.value && this.props.schema) {
this.resetValueToSample()
}
}
resetValueToSample = () => {
this.onChange(getSampleSchema(this.props.schema) )
}
onChange = (value) => {
this.props.onChange(value)
}
handleOnChange = e => {
const inputValue = e.target.value
this.onChange(inputValue)
}
render() {
let {
getComponent,
value,
errors
} = this.props
const TextArea = getComponent("TextArea")
return (
<div>
<TextArea
className={cx({ invalid: errors.size })}
title={ errors.size ? errors.join(", ") : ""}
value={value}
onChange={ this.handleOnChange }/>
</div>
)
}
}
function valueOrEmptyList(value) {
return List.isList(value) ? value : List()
}
-4
View File
@@ -18,7 +18,3 @@ export function toggle(configName) {
payload: configName,
}
}
// Hook
export const loaded = () => () => {}
-12
View File
@@ -1,12 +0,0 @@
import YAML from "js-yaml"
export const parseYamlConfig = (yaml, system) => {
try {
return YAML.safeLoad(yaml)
} catch(e) {
if (system) {
system.errActions.newThrownErr( new Error(e) )
}
return {}
}
}
+38 -2
View File
@@ -1,10 +1,46 @@
import YAML from "js-yaml"
import yamlConfig from "root/swagger-config.yaml"
import { parseYamlConfig } from "./helpers"
import * as actions from "./actions"
import * as specActions from "./spec-actions"
import * as selectors from "./selectors"
import reducers from "./reducers"
const parseYamlConfig = (yaml, system) => {
try {
return YAML.safeLoad(yaml)
} catch(e) {
if (system) {
system.errActions.newThrownErr( new Error(e) )
}
return {}
}
}
const specActions = {
downloadConfig: (url) => ({fn}) => {
let {fetch} = fn
return fetch(url)
},
getConfigByUrl: (configUrl, cb)=> ({ specActions }) => {
if (configUrl) {
return specActions.downloadConfig(configUrl).then(next, next)
}
function next(res) {
if (res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failedConfig")
specActions.updateLoadingStatus("failedConfig")
specActions.updateUrl("")
console.error(res.statusText + " " + configUrl)
cb(null)
} else {
cb(parseYamlConfig(res.text))
}
}
}
}
const specSelectors = {
getLocalConfig: () => {
return parseYamlConfig(yamlConfig)
-25
View File
@@ -1,25 +0,0 @@
import { parseYamlConfig } from "./helpers"
export const downloadConfig = (req) => (system) => {
const {fn: { fetch }} = system
return fetch(req)
}
export const getConfigByUrl = (req, cb)=> ({ specActions }) => {
if (req) {
return specActions.downloadConfig(req).then(next, next)
}
function next(res) {
if (res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failedConfig")
specActions.updateLoadingStatus("failedConfig")
specActions.updateUrl("")
console.error(res.statusText + " " + req.url)
cb(null)
} else {
cb(parseYamlConfig(res.text))
}
}
}
+13 -18
View File
@@ -1,23 +1,18 @@
import layout from "./layout"
import OperationWrapper from "./operation-wrapper"
import OperationTagWrapper from "./operation-tag-wrapper"
// import reducers from "./reducers"
// import * as actions from "./actions"
// import * as selectors from "./selectors"
import * as specWrapActions from "./spec-wrap-actions"
import * as layoutWrapActions from "./layout-wrap-actions"
export default function() {
return [layout, {
return {
statePlugins: {
configs: {
wrapActions: {
loaded: (ori, system) => (...args) => {
ori(...args)
const hash = window.location.hash
system.layoutActions.parseDeepLinkHash(hash)
}
}
spec: {
wrapActions: specWrapActions
},
layout: {
wrapActions: layoutWrapActions
}
},
wrapComponents: {
operation: OperationWrapper,
OperationTag: OperationTagWrapper,
},
}]
}
}
}
@@ -0,0 +1,37 @@
import { setHash } from "./helpers"
import { createDeepLinkPath } from "core/utils"
export const show = (ori, { getConfigs }) => (...args) => {
ori(...args)
const isDeepLinkingEnabled = getConfigs().deepLinking
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
return
}
try {
let [thing, shown] = args
let [type] = thing
if(type === "operations-tag" || type === "operations") {
if(!shown) {
return setHash("/")
}
if(type === "operations") {
let [, tag, operationId] = thing
setHash(`/${createDeepLinkPath(tag)}/${createDeepLinkPath(operationId)}`)
}
if(type === "operations-tag") {
let [, tag] = thing
setHash(`/${createDeepLinkPath(tag)}`)
}
}
} catch(e) {
// This functionality is not mission critical, so if something goes wrong
// we'll just move on
console.error(e)
}
}
-181
View File
@@ -1,181 +0,0 @@
import { setHash } from "./helpers"
import zenscroll from "zenscroll"
import Im, { fromJS } from "immutable"
const SCROLL_TO = "layout_scroll_to"
const CLEAR_SCROLL_TO = "layout_clear_scroll"
export const show = (ori, { getConfigs, layoutSelectors }) => (...args) => {
ori(...args)
if(!getConfigs().deepLinking) {
return
}
try {
let [tokenArray, shown] = args
//Coerce in to array
tokenArray = Array.isArray(tokenArray) ? tokenArray : [tokenArray]
// Convert into something we can put in the URL hash
// Or return empty, if we cannot
const urlHashArray = layoutSelectors.urlHashArrayFromIsShownKey(tokenArray) // Will convert
// No hash friendly list?
if(!urlHashArray.length)
return
const [type, assetName] = urlHashArray
if (!shown) {
return setHash("/")
}
if (urlHashArray.length === 2) {
setHash(`/${type}/${assetName}`)
} else if (urlHashArray.length === 1) {
setHash(`/${type}`)
}
} catch (e) {
// This functionality is not mission critical, so if something goes wrong
// we'll just move on
console.error(e) // eslint-disable-line no-console
}
}
export const scrollTo = (path) => {
return {
type: SCROLL_TO,
payload: Array.isArray(path) ? path : [path]
}
}
export const parseDeepLinkHash = (rawHash) => ({ layoutActions, layoutSelectors, getConfigs }) => {
if(!getConfigs().deepLinking) {
return
}
if(rawHash) {
let hash = rawHash.slice(1) // # is first character
if(hash[0] === "!") {
// Parse UI 2.x shebangs
hash = hash.slice(1)
}
if(hash[0] === "/") {
// "/pet/addPet" => "pet/addPet"
// makes the split result cleaner
// also handles forgotten leading slash
hash = hash.slice(1)
}
const isShownKey = layoutSelectors.isShownKeyFromUrlHashArray(hash.split("/"))
layoutActions.show(isShownKey, true) // TODO: 'show' operation tag
layoutActions.scrollTo(isShownKey)
}
}
export const readyToScroll = (isShownKey, ref) => (system) => {
const scrollToKey = system.layoutSelectors.getScrollToKey()
if(Im.is(scrollToKey, fromJS(isShownKey))) {
system.layoutActions.scrollToElement(ref)
system.layoutActions.clearScrollTo()
}
}
// Scroll to "ref" (dom node) with the scrollbar on "container" or the nearest parent
export const scrollToElement = (ref, container) => (system) => {
try {
container = container || system.fn.getScrollParent(ref)
let myScroller = zenscroll.createScroller(container)
myScroller.to(ref)
} catch(e) {
console.error(e) // eslint-disable-line no-console
}
}
export const clearScrollTo = () => {
return {
type: CLEAR_SCROLL_TO,
}
}
// From: https://stackoverflow.com/a/42543908/3933724
// Modified to return html instead of body element as last resort
function getScrollParent(element, includeHidden) {
const LAST_RESORT = document.documentElement
let style = getComputedStyle(element)
const excludeStaticParent = style.position === "absolute"
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/
if (style.position === "fixed")
return LAST_RESORT
for (let parent = element; (parent = parent.parentElement);) {
style = getComputedStyle(parent)
if (excludeStaticParent && style.position === "static") {
continue
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX))
return parent
}
return LAST_RESORT
}
export default {
fn: {
getScrollParent,
},
statePlugins: {
layout: {
actions: {
scrollToElement,
scrollTo,
clearScrollTo,
readyToScroll,
parseDeepLinkHash
},
selectors: {
getScrollToKey(state) {
return state.get("scrollToKey")
},
isShownKeyFromUrlHashArray(state, urlHashArray) {
const [tag, operationId] = urlHashArray
// We only put operations in the URL
if(operationId) {
return ["operations", tag, operationId]
} else if (tag) {
return ["operations-tag", tag]
}
return []
},
urlHashArrayFromIsShownKey(state, isShownKey) {
let [type, tag, operationId] = isShownKey
// We only put operations in the URL
if(type == "operations") {
return [tag, operationId]
} else if (type == "operations-tag") {
return [tag]
}
return []
},
},
reducers: {
[SCROLL_TO](state, action) {
return state.set("scrollToKey", Im.fromJS(action.payload))
},
[CLEAR_SCROLL_TO](state) {
return state.delete("scrollToKey")
}
},
wrapActions: {
show
}
}
}
}
@@ -1,25 +0,0 @@
import React from "react"
import { PropTypes } from "prop-types"
const Wrapper = (Ori, system) => class OperationTagWrapper extends React.Component {
static propTypes = {
tag: PropTypes.object.isRequired,
}
onLoad = (ref) => {
const { tag } = this.props
const isShownKey = ["operations-tag", tag]
system.layoutActions.readyToScroll(isShownKey, ref)
}
render() {
return (
<span ref={this.onLoad}>
<Ori {...this.props} />
</span>
)
}
}
export default Wrapper
@@ -1,26 +0,0 @@
import React from "react"
import ImPropTypes from "react-immutable-proptypes"
const Wrapper = (Ori, system) => class OperationWrapper extends React.Component {
static propTypes = {
operation: ImPropTypes.map.isRequired,
}
onLoad = (ref) => {
const { operation } = this.props
const { tag, operationId } = operation.toObject()
const isShownKey = ["operations", tag, operationId]
system.layoutActions.readyToScroll(isShownKey, ref)
}
render() {
return (
<span ref={this.onLoad}>
<Ori {...this.props} />
</span>
)
}
}
export default Wrapper
@@ -0,0 +1,64 @@
import zenscroll from "zenscroll"
import { escapeDeepLinkPath } from "core/utils"
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
export const updateJsonSpec = (ori, { layoutActions, getConfigs }) => (...args) => {
ori(...args)
const isDeepLinkingEnabled = getConfigs().deepLinking
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
return
}
if(window.location.hash && !hasHashBeenParsed ) {
let hash = window.location.hash.slice(1) // # is first character
if(hash[0] === "!") {
// Parse UI 2.x shebangs
hash = hash.slice(1)
}
if(hash[0] === "/") {
// "/pet/addPet" => "pet/addPet"
// makes the split result cleaner
// also handles forgotten leading slash
hash = hash.slice(1)
}
let [tag, operationId] = hash.split("/")
let swaggerUI = document.querySelector(".swagger-ui")
let myScroller = zenscroll.createScroller(swaggerUI)
let target
if(tag && operationId) {
// Pre-expand and scroll to the operation
layoutActions.show(["operations-tag", tag], true)
layoutActions.show(["operations", tag, operationId], true)
target = document
.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`)
} else if(tag) {
// Pre-expand and scroll to the tag
layoutActions.show(["operations-tag", tag], true)
target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`)
}
if(target) {
myScroller.to(target)
setTimeout(() => {
// Backup functionality: if we're still at the top of the document,
// scroll on the entire page (not within the Swagger-UI container)
if(zenscroll.getY() === 0) {
zenscroll.to(target)
}
}, 50)
}
}
hasHashBeenParsed = true
}
@@ -16,14 +16,8 @@ const Callbacks = (props) => {
return <div key={callbackName}>
<h2>{callbackName}</h2>
{ callback.map((pathItem, pathItemName) => {
if(pathItemName === "$$ref") {
return null
}
return <div key={pathItemName}>
{ pathItem.map((operation, method) => {
if(method === "$$ref") {
return null
}
let op = fromJS({
operation
})
@@ -33,8 +33,7 @@ export default class HttpAuth extends React.Component {
let { onChange } = this.props
let { value, name } = e.target
let newValue = Object.assign({}, this.state.value)
let newValue = this.state.value || {}
if(name) {
newValue[name] = value
} else {
@@ -54,7 +53,7 @@ export default class HttpAuth extends React.Component {
const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true)
const scheme = (schema.get("scheme") || "").toLowerCase()
const scheme = schema.get("scheme")
let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
@@ -126,7 +125,7 @@ export default class HttpAuth extends React.Component {
)
}
return <div>
<em><b>{name}</b> HTTP authentication: unsupported scheme {`'${scheme}'`}</em>
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
</div>
}
}
@@ -2,7 +2,6 @@ import Callbacks from "./callbacks"
import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx"
import Servers from "./servers"
import ServersContainer from "./servers-container"
import RequestBodyEditor from "./request-body-editor"
import HttpAuth from "./http-auth"
import OperationServers from "./operation-servers"
@@ -12,7 +11,6 @@ export default {
HttpAuth,
RequestBody,
Servers,
ServersContainer,
RequestBodyEditor,
OperationServers,
operationLink: OperationLink
@@ -1,25 +1,18 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { getSampleSchema } from "core/utils"
import { Map, OrderedMap } from "immutable"
import { OrderedMap } from "immutable"
const RequestBody = ({
requestBody,
requestBodyValue,
getComponent,
getConfigs,
specSelectors,
fn,
contentType,
isExecute,
specPath,
onChange
}) => {
const handleFile = (e) => {
onChange(e.target.files[0])
}
const Markdown = getComponent("Markdown")
const ModelExample = getComponent("modelExample")
const RequestBodyEditor = getComponent("RequestBodyEditor")
@@ -30,86 +23,10 @@ const RequestBody = ({
const mediaTypeValue = requestBodyContent.get(contentType)
const isObjectContent = mediaTypeValue.getIn(["schema", "type"]) === "object"
if(!mediaTypeValue) {
return null
}
if(
contentType === "application/octet-stream"
|| contentType.indexOf("image/") === 0
|| contentType.indexOf("audio/") === 0
|| contentType.indexOf("video/") === 0
) {
const Input = getComponent("Input")
if(!isExecute) {
return <i>
Example values are not available for <code>application/octet-stream</code> media types.
</i>
}
return <Input type={"file"} onChange={handleFile} />
}
if(
isObjectContent &&
(contentType === "application/x-www-form-urlencoded"
|| contentType.indexOf("multipart/") === 0))
{
const JsonSchemaForm = getComponent("JsonSchemaForm")
const HighlightCode = getComponent("highlightCode")
const bodyProperties = requestBody.getIn(["content", contentType, "schema", "properties"], OrderedMap())
requestBodyValue = Map.isMap(requestBodyValue) ? requestBodyValue : OrderedMap()
return <div className="table-container">
<table>
<tbody>
{
bodyProperties.map((prop, key) => {
const required = prop.get("required")
const type = prop.get("type")
const format = prop.get("format")
const isFile = type === "string" && (format === "binary" || format === "base64")
return <tr key={key} className="parameters">
<td className="col parameters-col_name">
<div className={required ? "parameter__name required" : "parameter__name"}>
{ key }
{ !required ? null : <span style={{color: "red"}}>&nbsp;*</span> }
</div>
<div className="parameter__type">
{ type }
{ format && <span className="prop-format">(${format})</span>}
</div>
<div className="parameter__deprecated">
{ prop.get("deprecated") ? "deprecated": null }
</div>
</td>
<td className="col parameters-col_description">
{isExecute ?
<JsonSchemaForm
fn={fn}
dispatchInitialValue={!isFile}
schema={prop}
getComponent={getComponent}
value={requestBodyValue.get(key) || getSampleSchema(prop)}
onChange={(value) => {
onChange(value, [key])
}}
/>
: <HighlightCode className="example" value={ getSampleSchema(prop) } />}
</td>
</tr>
})
}
</tbody>
</table>
</div>
}
return <div>
{ requestBodyDescription &&
<Markdown source={requestBodyDescription} />
@@ -136,10 +53,8 @@ const RequestBody = ({
RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired,
requestBodyValue: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
fn: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string,
isExecute: PropTypes.bool.isRequired,
@@ -1,42 +0,0 @@
import React from "react"
import PropTypes from "prop-types"
export default class ServersContainer extends React.Component {
static propTypes = {
specSelectors: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
}
render () {
const {specSelectors, oas3Selectors, oas3Actions, getComponent} = this.props
const servers = specSelectors.servers()
const Col = getComponent("Col")
const Servers = getComponent("Servers")
return (
<div>
{servers && servers.size ? (
<div className="global-server-container">
<Col className="servers wrapper" mobile={12}>
<span className="servers-title">Server</span>
<Servers
servers={servers}
currentServer={oas3Selectors.selectedServer()}
setSelectedServer={oas3Actions.setSelectedServer}
setServerVariableValue={oas3Actions.setServerVariableValue}
getServerVariable={oas3Selectors.serverVariableValue}
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
/>
</Col>
</div>
) : null}
</div>
)
}
}
+4 -8
View File
@@ -2,24 +2,20 @@ import React from "react"
export function isOAS3(jsSpec) {
const oasVersion = jsSpec.get("openapi")
if(typeof oasVersion !== "string") {
if(!oasVersion) {
return false
}
// we gate against `3.1` becasue we want to explicitly opt into supporting it
// at some point in the future -- KS, 7/2018
// starts with, but is not `3.0.` exactly
return oasVersion.startsWith("3.0.") && oasVersion.length > 4
return oasVersion.startsWith("3")
}
export function isSwagger2(jsSpec) {
const swaggerVersion = jsSpec.get("swagger")
if(typeof swaggerVersion !== "string") {
if(!swaggerVersion) {
return false
}
return swaggerVersion.startsWith("2.0")
return swaggerVersion.startsWith("2")
}
export function OAS3ComponentWrapFactory(Component) {
@@ -4,13 +4,11 @@ import parameters from "./parameters"
import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge"
import Model from "./model"
import JsonSchema_string from "./json-schema-string"
export default {
Markdown,
AuthItem,
parameters,
JsonSchema_string,
VersionStamp,
model: Model,
onlineValidatorBadge: OnlineValidatorBadge,
@@ -1,26 +0,0 @@
import React from "react"
import { OAS3ComponentWrapFactory } from "../helpers"
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
const {
schema,
getComponent,
errors,
onChange
} = props
const { type, format } = schema
const Input = getComponent("Input")
if(type === "string" && (format === "binary" || format === "base64")) {
return <Input type="file"
className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
onChange={(e) => {
onChange(e.target.files[0])
}}
disabled={Ori.isDisabled}/>
} else {
return <Ori {...props} />
}
})
@@ -47,11 +47,11 @@ class Parameters extends Component {
onChange = ( param, value, isXml ) => {
let {
specActions: { changeParamByIdentity },
specActions: { changeParam },
onChangeKey,
} = this.props
changeParamByIdentity( onChangeKey, param, value, isXml)
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -145,8 +145,7 @@ class Parameters extends Component {
getComponent={ getComponent }
specPath={specPath.push(i)}
getConfigs={ getConfigs }
rawParam={ parameter }
param={ specSelectors.parameterWithMetaByIdentity(pathMethod, parameter) }
param={ parameter }
key={ parameter.get( "name" ) }
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
@@ -186,17 +185,8 @@ class Parameters extends Component {
<RequestBody
specPath={requestBodySpecPath}
requestBody={requestBody}
requestBodyValue={oas3Selectors.requestBodyValue(...pathMethod) || Map()}
isExecute={isExecute}
onChange={(value, path) => {
if(path) {
const lastValue = oas3Selectors.requestBodyValue(...pathMethod)
const usableValue = Map.isMap(lastValue) ? lastValue : Map()
return oas3Actions.setRequestBodyValue({
pathMethod,
value: usableValue.setIn(path, value)
})
}
onChange={(value) => {
oas3Actions.setRequestBodyValue({ value, pathMethod })
}}
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
-1
View File
@@ -29,7 +29,6 @@ export const sampleFromSchema = (schema, config={}) => {
let { type, example, properties, additionalProperties, items } = objectify(schema)
let { includeReadOnly, includeWriteOnly } = config
if(example !== undefined) {
return deeplyStripKey(example, "$$ref", (val) => {
// do a couple of quick sanity tests to ensure the value
+1 -10
View File
@@ -234,13 +234,6 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
}
}
export function changeParamByIdentity( pathMethod, param, value, isXml ){
return {
type: UPDATE_PARAM,
payload:{ path: pathMethod, param, value, isXml }
}
}
export const updateResolvedSubtree = (path, value) => {
return {
type: UPDATE_RESOLVED_SUBTREE,
@@ -355,9 +348,7 @@ export const executeRequest = (req) =>
if(isJSONObject(requestBody)) {
req.requestBody = JSON.parse(requestBody)
} else if(requestBody && requestBody.toJS) {
req.requestBody = requestBody.toJS()
} else{
} else {
req.requestBody = requestBody
}
}
+3 -16
View File
@@ -51,21 +51,12 @@ export default {
},
[UPDATE_PARAM]: ( state, {payload} ) => {
let { path: pathMethod, paramName, paramIn, param, value, isXml } = payload
let paramKey
// `hashCode` is an Immutable.js Map method
if(param && param.hashCode && !paramIn && !paramName) {
paramKey = `${param.get("name")}.${param.get("in")}.hash-${param.hashCode()}`
} else {
paramKey = `${paramName}.${paramIn}`
}
let { path: pathMethod, paramName, paramIn, value, isXml } = payload
const valueKey = isXml ? "value_xml" : "value"
return state.setIn(
["meta", "paths", ...pathMethod, "parameters", paramKey, valueKey],
["meta", "paths", ...pathMethod, "parameters", `${paramName}.${paramIn}`, valueKey],
value
)
},
@@ -127,11 +118,7 @@ export default {
let operationPath = ["paths", ...path]
let metaPath = ["meta", "paths", ...path]
if(
!state.getIn(["json", ...operationPath])
&& !state.getIn(["resolved", ...operationPath])
&& !state.getIn(["resolvedSubtrees", ...operationPath])
) {
if(!state.getIn(["json", ...operationPath]) && !state.getIn(["resolved", ...operationPath])) {
// do nothing if the operation does not exist
return state
}
+22 -30
View File
@@ -294,44 +294,36 @@ export const allowTryItOutFor = () => {
return true
}
export const parameterWithMetaByIdentity = (state, pathMethod, param) => {
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], OrderedMap())
const metaParams = state.getIn(["meta", "paths", ...pathMethod, "parameters"], OrderedMap())
export const operationWithMeta = (state, path, method) => {
const op = specJsonWithResolvedSubtrees(state).getIn(["paths", path, method], Map())
const meta = state.getIn(["meta", "paths", path, method], Map())
const mergedParams = opParams.map((currentParam) => {
const nameInKeyedMeta = metaParams.get(`${param.get("name")}.${param.get("in")}`)
const hashKeyedMeta = metaParams.get(`${param.get("name")}.${param.get("in")}.hash-${param.hashCode()}`)
return OrderedMap().merge(
currentParam,
nameInKeyedMeta,
hashKeyedMeta
const mergedParams = op.get("parameters", List()).map((param) => {
return Map().merge(
param,
meta.getIn(["parameters", `${param.get("name")}.${param.get("in")}`])
)
})
return mergedParams.find(curr => curr.get("in") === param.get("in") && curr.get("name") === param.get("name"), OrderedMap())
}
export const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], OrderedMap())
const currentParam = opParams.find(param => param.get("in") === paramIn && param.get("name") === paramName, OrderedMap())
return parameterWithMetaByIdentity(state, pathMethod, currentParam)
}
export const operationWithMeta = (state, path, method) => {
const op = specJsonWithResolvedSubtrees(state).getIn(["paths", path, method], OrderedMap())
const meta = state.getIn(["meta", "paths", path, method], OrderedMap())
const mergedParams = op.get("parameters", List()).map((param) => {
return parameterWithMetaByIdentity(state, [path, method], param)
})
return OrderedMap()
return Map()
.merge(op, meta)
.set("parameters", mergedParams)
}
export const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], Map())
const metaParams = state.getIn(["meta", "paths", ...pathMethod, "parameters"], Map())
const mergedParams = opParams.map((param) => {
return Map().merge(
param,
metaParams.get(`${param.get("name")}.${param.get("in")}`)
)
})
return mergedParams.find(param => param.get("in") === paramIn && param.get("name") === paramName, Map())
}
// Get the parameter value by parameter name
export function getParameter(state, pathMethod, name, inType) {
pathMethod = pathMethod || []
-10
View File
@@ -31,7 +31,6 @@ import Clear from "core/components/clear"
import LiveResponse from "core/components/live-response"
import OnlineValidatorBadge from "core/components/online-validator-badge"
import Operations from "core/components/operations"
import OperationTag from "core/components/operation-tag"
import Operation from "core/components/operation"
import OperationExt from "core/components/operation-extensions"
import OperationExtRow from "core/components/operation-extension-row"
@@ -51,13 +50,10 @@ import Info, {
InfoUrl,
InfoBasePath
} from "core/components/info"
import InfoContainer from "core/containers/info"
import Footer from "core/components/footer"
import FilterContainer from "core/containers/filter"
import ParamBody from "core/components/param-body"
import Curl from "core/components/curl"
import Schemes from "core/components/schemes"
import SchemesContainer from "core/containers/schemes"
import ModelCollapse from "core/components/model-collapse"
import ModelExample from "core/components/model-example"
import ModelWrapper from "core/components/model-wrapper"
@@ -69,7 +65,6 @@ import ArrayModel from "core/components/array-model"
import PrimitiveModel from "core/components/primitive-model"
import Property from "core/components/property"
import TryItOutButton from "core/components/try-it-out-button"
import VersionPragmaFilter from "core/components/version-pragma-filter"
import VersionStamp from "core/components/version-stamp"
import DeepLink from "core/components/deep-link"
import SvgAssets from "core/components/svg-assets"
@@ -98,7 +93,6 @@ export default function() {
clear: Clear,
liveResponse: LiveResponse,
info: Info,
InfoContainer,
onlineValidatorBadge: OnlineValidatorBadge,
operations: Operations,
operation: Operation,
@@ -114,11 +108,9 @@ export default function() {
contentType: ContentType,
overview: Overview,
footer: Footer,
FilterContainer,
ParamBody: ParamBody,
curl: Curl,
schemes: Schemes,
SchemesContainer,
modelExample: ModelExample,
ModelWrapper,
ModelCollapse,
@@ -132,12 +124,10 @@ export default function() {
TryItOutButton,
Markdown,
BaseLayout,
VersionPragmaFilter,
VersionStamp,
OperationExt,
OperationExtRow,
ParameterExt,
OperationTag,
OperationContainer,
DeepLink,
InfoUrl,
+4 -30
View File
@@ -37,7 +37,7 @@ export function objectify (thing) {
if(!isObject(thing))
return {}
if(isImmutable(thing))
return thing.toJS()
return thing.toObject()
return thing
}
@@ -503,30 +503,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
let numberCheck = type === "number" && (value || value === 0)
let integerCheck = type === "integer" && (value || value === 0)
let oas3ObjectCheck = false
if(false || isOAS3 && type === "object") {
if(typeof value === "object") {
oas3ObjectCheck = true
} else if(typeof value === "string") {
try {
JSON.parse(value)
oas3ObjectCheck = true
} catch(e) {
errors.push("Parameter string value must be valid JSON")
return errors
}
}
}
const allChecks = [
stringCheck, arrayCheck, listCheck, fileCheck, booleanCheck,
numberCheck, integerCheck, oas3ObjectCheck
]
const passedAnyCheck = allChecks.some(v => !!v)
if ( required && !passedAnyCheck ) {
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
errors.push("Required field is not provided")
return errors
}
@@ -628,9 +605,7 @@ export const getSampleSchema = (schema, contentType="", config={}) => {
return memoizedCreateXMLExample(schema, config)
}
const res = memoizedSampleFromSchema(schema, config)
return typeof res === "object" ? JSON.stringify(res, null, 2) : res
return JSON.stringify(memoizedSampleFromSchema(schema, config), null, 2)
}
export const parseSearch = () => {
@@ -737,14 +712,13 @@ export const createDeepLinkPath = (str) => typeof str == "string" || str instanc
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
export const getExtensions = (defObj) => defObj.filter((v, k) => /^x-/.test(k))
export const getCommonExtensions = (defObj) => defObj.filter((v, k) => /^pattern|maxLength|minLength|maximum|minimum/.test(k))
// Deeply strips a specific key from an object.
//
// `predicate` can be used to discriminate the stripping further,
// by preserving the key's place in the object based on its value.
export function deeplyStripKey(input, keyToStrip, predicate = () => true) {
if(typeof input !== "object" || Array.isArray(input) || input === null || !keyToStrip) {
if(typeof input !== "object" || Array.isArray(input) || !keyToStrip) {
return input
}
+1 -1
View File
@@ -28,7 +28,7 @@ Here is an example of each `type`
```js
// A contrived, but quite full example....
export function SomePlugin(toolbox) {
export function SomePlugin( toolbox ) {
const UPDATE_SOMETHING = "some_namespace_update_something" // strings just need to be uniuqe... see below
+1 -1
View File
@@ -140,7 +140,7 @@ export default class Topbar extends React.Component {
<div className="topbar">
<div className="wrapper">
<div className="topbar-wrapper">
<Link>
<Link href="#">
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
<span>swagger</span>
</Link>
+1 -2
View File
@@ -51,8 +51,7 @@ input[type=text],
input[type=password],
input[type=search],
input[type=email],
input[type=file],
textarea
input[type=file]
{
min-width: 100px;
margin: 5px 0;
-28
View File
@@ -632,7 +632,6 @@
> .microlight {
overflow-y: auto;
max-height: 400px;
min-height: 6em;
}
}
@@ -796,30 +795,3 @@ a.nostyle {
cursor: pointer;
}
}
.version-pragma {
height: 100%;
padding: 5em 0px;
&__message {
display: flex;
justify-content: center;
height: 100%;
font-size: 1.2em;
text-align: center;
line-height: 1.5em;
padding: 0px .6em;
> div {
max-width: 55ch;
flex: 1;
}
code {
background-color: #dedede;
padding: 4px 4px 2px;
white-space: pre;
}
}
}
-8
View File
@@ -166,7 +166,6 @@ section.models
.model-container
{
margin: 0 20px 15px;
position: relative;
transition: all .5s;
@@ -187,13 +186,6 @@ section.models
{
margin: 0 20px;
}
.models-jump-to-path {
position: absolute;
top: 8px;
right: 5px;
opacity: 0.65;
}
}
.model-box
+3 -3
View File
@@ -1,13 +1,13 @@
@mixin text_body($color: $text-body-default-font-color)
{
font-family: sans-serif;
font-family: 'Open Sans', sans-serif;
color: $color;
}
@mixin text_code($color: $text-code-default-font-color)
{
font-family: monospace;
font-family: 'Source Code Pro', monospace;
font-weight: 600;
color: $color;
@@ -15,7 +15,7 @@
@mixin text_headline($color: $text-headline-default-font-color)
{
font-family: sans-serif;
font-family: 'Titillium Web', sans-serif;
color: $color;
}
+1 -1
View File
@@ -1,5 +1,5 @@
---
url: "https://petstore.swagger.io/v2/swagger.json"
url: "http://petstore.swagger.io/v2/swagger.json"
dom_id: "#swagger-ui"
validatorUrl: "https://online.swagger.io/validator"
oauth2RedirectUrl: "http://localhost:3200/oauth2-redirect.html"
-1
View File
@@ -3,4 +3,3 @@ env:
rules:
"react/prop-types": 1 # bah humbug
"no-unused-vars": 1 # unused vars in tests can be useful for indicating a full signature
"no-global-assign": 1
@@ -1,73 +0,0 @@
/* eslint-env mocha */
import React from "react"
import { List, fromJS } from "immutable"
import expect, { createSpy } from "expect"
import { render } from "enzyme"
import ParameterRow from "components/parameter-row"
describe("bug #4557: default parameter values", function(){
it("should apply a Swagger 2.0 default value", function(){
const paramValue = fromJS({
description: "a pet",
type: "string",
default: "MyDefaultValue"
})
let props = {
getComponent: ()=> "div",
specSelectors: {
security(){},
parameterWithMetaByIdentity(){ return paramValue },
isOAS3(){ return false }
},
fn: {},
operation: {get: ()=>{}},
onChange: createSpy(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => {},
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props}/>)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "MyDefaultValue", false)
})
it("should apply an OpenAPI 3.0 default value", function(){
const paramValue = fromJS({
description: "a pet",
schema: {
type: "string",
default: "MyDefaultValue"
}
})
let props = {
getComponent: ()=> "div",
specSelectors: {
security(){},
parameterWithMetaByIdentity(){ return paramValue },
isOAS3(){ return true }
},
fn: {},
operation: {get: ()=>{}},
onChange: createSpy(),
param: paramValue,
rawParam: paramValue,
onChangeConsumes: () => {},
pathMethod: [],
getConfigs: () => { return {} },
specPath: List([])
}
render(<ParameterRow {...props}/>)
expect(props.onChange).toHaveBeenCalled()
expect(props.onChange).toHaveBeenCalledWith(paramValue, "MyDefaultValue", false)
})
})
-65
View File
@@ -1,65 +0,0 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import { mount } from "enzyme"
import FilterContainer from "containers/filter"
import { Col } from "components/layout-utils"
describe("<FilterContainer/>", function(){
const mockedProps = {
specSelectors: {
loadingStatus() {}
},
layoutSelectors: {
currentFilter() {}
},
getComponent: () => {return Col}
}
it("renders FilterContainer if filter is provided", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return true}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(1)
})
it("does not render FilterContainer if filter is null", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return null}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(0)
})
it("does not render FilterContainer if filter is false", function(){
// Given
let props = {...mockedProps}
props.layoutSelectors = {...mockedProps.specSelectors}
props.layoutSelectors.currentFilter = function() {return false}
// When
let wrapper = mount(<FilterContainer {...props}/>)
// Then
const renderedColInsideFilter = wrapper.find(Col)
expect(renderedColInsideFilter.length).toEqual(0)
})
})
-67
View File
@@ -1,67 +0,0 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import { mount } from "enzyme"
import { fromJS } from "immutable"
import InfoContainer from "containers/info"
describe("<InfoContainer/>", function () {
const components = {
info: () => <span className="mocked-info"/>
}
const mockedProps = {
specSelectors: {
info () {},
url () {},
basePath () {},
host () {},
externalDocs () {}
},
getComponent: c => components[c]
}
it("renders Info inside InfoContainer if info is provided", function () {
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.info = function () {return fromJS(["info1", "info2"])}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(1)
})
it("does not render Info inside InfoContainer if no info is provided", function () {
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.info = function () {return fromJS([])}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(0)
})
it("does not render Info inside InfoContainer if info is undefined", function () {
// Given
let props = {...mockedProps}
// When
let wrapper = mount(<InfoContainer {...props}/>)
// Then
const renderedInfo = wrapper.find("span.mocked-info")
expect(renderedInfo.length).toEqual(0)
})
})
+4 -86
View File
@@ -1,13 +1,12 @@
/* eslint-env mocha */
import React from "react"
import { List } from "immutable"
import expect, { createSpy } from "expect"
import { Select, Input, TextArea } from "components/layout-utils"
import { mount, render } from "enzyme"
import { Select, Input } from "components/layout-utils"
import { render } from "enzyme"
import * as JsonSchemaComponents from "core/json-schema-components"
import { JsonSchemaForm } from "core/json-schema-components"
const components = {...JsonSchemaComponents, Select, Input, TextArea}
const components = {...JsonSchemaComponents, Select, Input}
const getComponentStub = (name) => {
if(components[name]) return components[name]
@@ -86,54 +85,6 @@ describe("<JsonSchemaForm/>", function(){
expect(wrapper.find("select option").eq(2).text()).toEqual("false")
})
it("should render the correct options for an enum boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: {
type: "boolean",
enum: ["true"]
}
}
let wrapper = render(<JsonSchemaForm {...props}/>)
expect(wrapper.find("select").length).toEqual(1)
expect(wrapper.find("select option").length).toEqual(2)
expect(wrapper.find("select option").eq(0).text()).toEqual("--")
expect(wrapper.find("select option").eq(1).text()).toEqual("true")
expect(wrapper.find("select option:checked").first().text()).toEqual("--")
})
it("should render the correct options for a required boolean parameter", function(){
let props = {
getComponent: getComponentStub,
value: "",
onChange: () => {},
keyName: "",
fn: {},
schema: {
type: "boolean",
required: true
}
}
let wrapper = render(<JsonSchemaForm {...props}/>)
expect(wrapper.find("select").length).toEqual(1)
expect(wrapper.find("select option").length).toEqual(3)
expect(wrapper.find("select option").eq(0).text()).toEqual("--")
expect(wrapper.find("select option").eq(1).text()).toEqual("true")
expect(wrapper.find("select option").eq(2).text()).toEqual("false")
expect(wrapper.find("select option:checked").first().text()).toEqual("--")
})
it("should render the correct options for a required enum boolean parameter", function(){
let props = {
@@ -153,40 +104,7 @@ describe("<JsonSchemaForm/>", function(){
expect(wrapper.find("select").length).toEqual(1)
expect(wrapper.find("select option").length).toEqual(1)
expect(wrapper.find("select option").eq(0).text()).toEqual("true")
expect(wrapper.find("select option:checked").first().text()).toEqual("true")
})
})
describe("objects", function() {
it("should render the correct editor for an OAS3 object parameter", function(){
let updateQueue = []
let props = {
getComponent: getComponentStub,
value: "",
onChange: (value) => {
updateQueue.push({ value })
},
keyName: "",
fn: {},
errors: List(),
schema: {
type: "object",
properties: {
id: {
type: "string",
example: "abc123"
}
}
}
}
let wrapper = mount(<JsonSchemaForm {...props}/>)
updateQueue.forEach(newProps => wrapper.setProps(newProps))
expect(wrapper.find("textarea").length).toEqual(1)
expect(wrapper.find("textarea").text()).toEqual(`{\n "id": "abc123"\n}`)
expect(wrapper.find("select option").first().text()).toEqual("true")
})
})
describe("unknown types", function() {
+7 -7
View File
@@ -16,19 +16,19 @@ describe("Markdown component", function() {
it("allows td elements with colspan attrib", function() {
const str = `<table><tr><td>ABC</td></tr></table>`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><table><tbody><tr><td>ABC</td></tr></tbody></table></div>`)
expect(el.html()).toEqual(`<div class="markdown"><table><tr><td>ABC</td></tr></table></div>`)
})
it("allows image elements", function() {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="http://image.source"></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img title="Image title" alt="Image alt text" src="https://image.source"></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="https://image.source" title="Image title"></p>\n</div>`)
})
it("allows image elements with data scheme", function() {
@@ -52,7 +52,7 @@ describe("Markdown component", function() {
it("allows links", function() {
const str = `[Link](https://example.com/)`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><a target="_blank" href="https://example.com/">Link</a></p>\n</div>`)
expect(el.html()).toEqual(`<div class="markdown"><p><a href="https://example.com/" target="_blank">Link</a></p>\n</div>`)
})
})
@@ -60,13 +60,13 @@ describe("Markdown component", function() {
it("allows image elements", function() {
const str = `![Image alt text](http://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img title="Image title" alt="Image alt text" src="http://image.source"></p></div></div>`)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img title="Image title" alt="Image alt text" src="https://image.source"></p></div></div>`)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="https://image.source" title="Image title"></p></div></div>`)
})
it("allows image elements with data scheme", function() {
+1 -2
View File
@@ -2,7 +2,7 @@
import React from "react"
import expect, { createSpy } from "expect"
import { shallow } from "enzyme"
import { fromJS, Map } from "immutable"
import { fromJS } from "immutable"
import Models from "components/models"
import ModelCollpase from "components/model-collapse"
import ModelComponent from "components/model-wrapper"
@@ -19,7 +19,6 @@ describe("<Models/>", function(){
},
specSelectors: {
isOAS3: () => false,
specJson: () => Map(),
definitions: function() {
return fromJS({
def1: {},
+2 -3
View File
@@ -1,6 +1,6 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import expect, { createSpy } from "expect"
import { render } from "enzyme"
import { fromJS } from "immutable"
import DeepLink from "components/deep-link"
@@ -10,8 +10,7 @@ import {Collapse} from "components/layout-utils"
const components = {
Collapse,
DeepLink,
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />,
OperationTag: "div",
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />
}
describe("<Operations/>", function(){
-119
View File
@@ -1,119 +0,0 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import { mount, render } from "enzyme"
import { fromJS } from "immutable"
import SchemesContainer from "containers/schemes"
import Schemes from "components/schemes"
import { Col } from "components/layout-utils"
describe("<SchemesContainer/>", function(){
const components = {
schemes: Schemes,
Col,
authorizeBtn: () => <span className="mocked-button" id="mocked-button" />
}
const mockedProps = {
specSelectors: {
securityDefinitions() {},
operationScheme() {},
schemes() {}
},
specActions: {
setScheme() {}
},
getComponent: c => components[c]
}
const twoSecurityDefinitions = {
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
"flow": "implicit",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
}
it("renders Schemes inside SchemesContainer if schemes are provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.operationScheme = function() {return "http"}
props.specSelectors.schemes = function() {return fromJS(["http", "https"])}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(1)
})
it("does not render Schemes inside SchemeWrapper if empty array of schemes is provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.schemes = function() {return fromJS([])}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(0)
})
it("does not render Schemes inside SchemeWrapper if provided schemes are undefined", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.schemes = function() {return undefined}
// When
let wrapper = mount(<SchemesContainer {...props}/>)
// Then
const renderedSchemes = wrapper.find(Schemes)
expect(renderedSchemes.length).toEqual(0)
})
it("renders AuthorizeBtn inside SchemesContainer if security definition is provided", function(){
// Given
let props = {...mockedProps}
props.specSelectors = {...mockedProps.specSelectors}
props.specSelectors.securityDefinitions = function () {return fromJS(twoSecurityDefinitions)}
// When
let wrapper = render(<SchemesContainer {...props}/>)
// Then
const renderedAuthorizeBtn = wrapper.find("span.mocked-button")
expect(renderedAuthorizeBtn.length).toEqual(1)
})
it("does not render AuthorizeBtn if security definition is not provided", function(){
// Given
let props = {...mockedProps}
// When
let wrapper = render(<SchemesContainer {...props}/>)
// Then
const renderedAuthorizeBtn = wrapper.find("span.mocked-button")
expect(renderedAuthorizeBtn.length).toEqual(0)
})
})
-70
View File
@@ -1,70 +0,0 @@
/* eslint-env mocha */
import React from "react"
import expect, { createSpy } from "expect"
import { shallow } from "enzyme"
import { fromJS, Map } from "immutable"
import VersionPragmaFilter from "components/version-pragma-filter"
describe("<VersionPragmaFilter/>", function(){
it("renders children for a Swagger 2 definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={true} isOAS3={false}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders children for an OpenAPI 3 definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={false} isOAS3={true}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders children when a bypass prop is set", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter bypass>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div").length).toEqual(1)
expect(wrapper.find("div").text()).toEqual("hello!")
})
it("renders the correct message for an ambiguous-version definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={true} isOAS3={true}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div.version-pragma__message--ambiguous").length).toEqual(1)
expect(wrapper.find("div.version-pragma__message--missing").length).toEqual(0)
})
it("renders the correct message for a missing-version definition", function(){
// When
let wrapper = shallow(
<VersionPragmaFilter isSwagger2={false} isOAS3={false}>
hello!
</VersionPragmaFilter>
)
// Then
expect(wrapper.find("div.version-pragma__message--missing").length).toEqual(1)
expect(wrapper.find("div.version-pragma__message--ambiguous").length).toEqual(0)
})
})
-27
View File
@@ -1,27 +0,0 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { downloadConfig } from "corePlugins/configs/spec-actions"
describe("configs plugin - actions", () => {
describe("downloadConfig", () => {
it("should call the system fetch helper with a provided request", () => {
const fetchSpy = createSpy(async () => {}).andCallThrough()
const system = {
fn: {
fetch: fetchSpy
}
}
const req = {
url: "http://swagger.io/one",
requestInterceptor: a => a,
responseInterceptor: a => a,
}
downloadConfig(req)(system)
expect(fetchSpy).toHaveBeenCalledWith(req)
})
})
})

Some files were not shown because too many files have changed in this diff Show More