Merge branch 'master' of github.com:swagger-api/swagger-ui into ft/docs
This commit is contained in:
@@ -16,7 +16,8 @@
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
|
||||
"plugins": [
|
||||
"react"
|
||||
"react",
|
||||
"mocha"
|
||||
],
|
||||
|
||||
"rules": {
|
||||
@@ -32,6 +33,7 @@
|
||||
"comma-dangle": 0,
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
"react/jsx-no-bind": 1,
|
||||
"react/display-name": 0
|
||||
"react/display-name": 0,
|
||||
"mocha/no-exclusive-tests": "error"
|
||||
}
|
||||
}
|
||||
|
||||
3
.github/issue_template.md
vendored
3
.github/issue_template.md
vendored
@@ -1,6 +1,9 @@
|
||||
<!---
|
||||
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
|
||||
|
||||
42
.github/pull_request_template.md
vendored
Normal file
42
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
### Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
|
||||
|
||||
### Motivation and Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
<!--- Use the magic "Fixes #1234" format, so the issues are -->
|
||||
<!--- automatically closed when this PR is merged. -->
|
||||
|
||||
|
||||
|
||||
### How Has This Been Tested?
|
||||
<!--- Please describe in detail how you manually tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
|
||||
|
||||
### Screenshots (if appropriate):
|
||||
|
||||
|
||||
|
||||
### Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] No code changes (changes to documentation, CI, metadata, etc)
|
||||
- [ ] Dependency changes (any modification to dependencies in `package.json`)
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
### Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I have added tests to cover my changes.
|
||||
- [ ] All new and existing tests passed.
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@ node_modules
|
||||
.idea
|
||||
.deps_check
|
||||
.DS_Store
|
||||
.nyc_output
|
||||
npm-debug.log*
|
||||
.eslintcache
|
||||
package-lock.json
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
!dist/swagger-ui.js.map
|
||||
!dist/swagger-ui.css
|
||||
!dist/swagger-ui.css.map
|
||||
!dist/oauth2-redirect.html
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6.9'
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
services:
|
||||
- docker
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
install: "npm i && npm update"
|
||||
before_deploy:
|
||||
- npm run build
|
||||
env:
|
||||
|
||||
@@ -4,7 +4,7 @@ We love contributions from our community of users! This document explains our gu
|
||||
|
||||
#### Environment setup
|
||||
|
||||
0. Install Node.js (4 or newer) and npm (3 or newer).
|
||||
0. Install Node.js (6 or newer) and npm (3 or newer).
|
||||
1. Make a fork of Swagger-UI on GitHub, then clone your fork to your machine.
|
||||
2. Run `npm install` in your Swagger-UI directory.
|
||||
3. Run `npm run dev`. `localhost:3200` should open automatically.
|
||||
|
||||
@@ -13,6 +13,7 @@ ENV OAUTH_APP_NAME "**None**"
|
||||
ENV OAUTH_ADDITIONAL_PARAMS "**None**"
|
||||
ENV SWAGGER_JSON "/app/swagger.json"
|
||||
ENV PORT 80
|
||||
ENV BASE_URL ""
|
||||
|
||||
RUN apk add --update nginx
|
||||
RUN mkdir -p /run/nginx
|
||||
|
||||
47
README.md
47
README.md
@@ -6,7 +6,7 @@
|
||||
|
||||
**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 contribution](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+contribution%22) label.
|
||||
**👉🏼 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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
|
||||
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.3.1 | 2017-10-02 | 2.0, 3.0 | [tag v3.3.1](https://github.com/swagger-api/swagger-ui/tree/v3.3.1)
|
||||
3.6.0 | 2017-12-01 | 2.0, 3.0 | [tag v3.5=6.0](https://github.com/swagger-api/swagger-ui/tree/v3.6.0)
|
||||
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)
|
||||
@@ -49,6 +49,14 @@ Or you can provide your own swagger.json on your host
|
||||
docker run -p 80:8080 -e SWAGGER_JSON=/foo/swagger.json -v /bar:/foo swaggerapi/swagger-ui
|
||||
```
|
||||
|
||||
The base URL of the web application can be changed by specifying the `BASE_URL` environment variable:
|
||||
|
||||
```
|
||||
docker run -p 80:8080 -e BASE_URL=/swagger -e SWAGGER_JSON=/foo/swagger.json -v /bar:/foo swaggerapi/swagger-ui
|
||||
```
|
||||
|
||||
This will serve Swagger UI at `/swagger` instead of `/`.
|
||||
|
||||
##### Prerequisites
|
||||
- Node 6.x
|
||||
- NPM 3.x
|
||||
@@ -135,6 +143,41 @@ ui.initOAuth({
|
||||
|
||||
If you'd like to use the bundle files via npm, check out the [`swagger-ui-dist` package](https://www.npmjs.com/package/swagger-ui-dist).
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
#### Parameters
|
||||
|
||||
Parameters with dots in their names are single strings used to organize subordinate parameters, and are not indicative of a nested structure.
|
||||
|
||||
Parameter Name | Description
|
||||
--- | ---
|
||||
url | The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). Will be ignored if `urls` or `spec` is used.
|
||||
urls | 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 | 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`.
|
||||
spec | A JSON 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.
|
||||
validatorUrl | 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.
|
||||
dom_id | The id of a dom element inside which SwaggerUi will put the user interface for swagger.
|
||||
domNode | The HTML DOM element inside which SwaggerUi will put the user interface for swagger. Overrides `dom_id`.
|
||||
oauth2RedirectUrl | OAuth redirect URL
|
||||
tagsSorter | 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.
|
||||
operationsSorter | 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.
|
||||
defaultModelRendering | Controls how models are 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.) It can be set to 'model' or 'example', and the default is 'example'.
|
||||
defaultModelExpandDepth | The default expansion depth for models. The default value is 1.
|
||||
configUrl | Configs URL
|
||||
parameterMacro | MUST be a 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 | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
|
||||
docExpansion | 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). The default is 'list'.
|
||||
displayOperationId | Controls the display of operationId in operations list. The default is `false`.
|
||||
displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`.
|
||||
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
|
||||
filter | 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 true/false to enable or disable, or an explicit filter 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.
|
||||
deepLinking | If set to `true`, enables dynamic deep linking for tags and operations. [Docs](https://github.com/swagger-api/swagger-ui/blob/master/docs/deep-linking.md)
|
||||
requestInterceptor | MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
|
||||
responseInterceptor | MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
|
||||
showMutatedRequest | If set to `true` (the default), uses the mutated request returned from a rquestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
|
||||
showExtensions | Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema. The default is `false`.
|
||||
|
||||
>>>>>>> f1f29aa7e1f363461ce085f936ba6aca70d873bb
|
||||
### Plugins
|
||||
|
||||
#### Topbar plugin
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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" >
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
|
||||
isValid = qp.state === sentState
|
||||
|
||||
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
|
||||
5
dist/oauth2-redirect.html
vendored
5
dist/oauth2-redirect.html
vendored
@@ -27,7 +27,10 @@
|
||||
|
||||
isValid = qp.state === sentState
|
||||
|
||||
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
|
||||
130
dist/swagger-ui-bundle.js
vendored
130
dist/swagger-ui-bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui-bundle.js.map
vendored
2
dist/swagger-ui-bundle.js.map
vendored
File diff suppressed because one or more lines are too long
6
dist/swagger-ui-standalone-preset.js
vendored
6
dist/swagger-ui-standalone-preset.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui-standalone-preset.js.map
vendored
2
dist/swagger-ui-standalone-preset.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui.css
vendored
2
dist/swagger-ui.css
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui.css.map
vendored
2
dist/swagger-ui.css.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"swagger-ui.css","sources":[],"mappings":"","sourceRoot":""}
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
|
||||
23231
dist/swagger-ui.js
vendored
23231
dist/swagger-ui.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui.js.map
vendored
2
dist/swagger-ui.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -21,6 +21,16 @@ replace_or_delete_in_index () {
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "${BASE_URL}" ]; then
|
||||
NGINX_WITH_BASE_URL="${NGINX_ROOT}${BASE_URL}"
|
||||
|
||||
mkdir -p ${NGINX_WITH_BASE_URL}
|
||||
mv ${NGINX_ROOT}/*.* ${NGINX_WITH_BASE_URL}/
|
||||
|
||||
INDEX_FILE=$NGINX_WITH_BASE_URL/index.html
|
||||
NGINX_ROOT=$NGINX_WITH_BASE_URL
|
||||
fi
|
||||
|
||||
replace_in_index myApiKeyXXXX123456789 $API_KEY
|
||||
replace_or_delete_in_index your-client-id $OAUTH_CLIENT_ID
|
||||
replace_or_delete_in_index your-client-secret-if-required $OAUTH_CLIENT_SECRET
|
||||
@@ -32,7 +42,7 @@ fi
|
||||
|
||||
if [[ -f $SWAGGER_JSON ]]; then
|
||||
cp $SWAGGER_JSON $NGINX_ROOT
|
||||
REL_PATH="/$(basename $SWAGGER_JSON)"
|
||||
REL_PATH="./$(basename $SWAGGER_JSON)"
|
||||
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
|
||||
|
||||
@@ -25,6 +25,7 @@ displayRequestDuration | Controls the display of the request duration (in millis
|
||||
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
|
||||
filter | 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 true/false to enable or disable, or an explicit filter 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.
|
||||
deepLinking | If set to `true`, enables dynamic deep linking for tags and operations. [Docs](https://github.com/swagger-api/swagger-ui/blob/master/docs/deep-linking.md)
|
||||
requestInterceptor | MUST be a function. Function to intercept try-it-out requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
|
||||
responseInterceptor | MUST be a function. Function to intercept try-it-out responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
|
||||
requestInterceptor | MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
|
||||
responseInterceptor | MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
|
||||
showMutatedRequest | If set to `true` (the default), uses the mutated request returned from a rquestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
|
||||
showExtensions | Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema. The default is `false`.
|
||||
|
||||
@@ -20,8 +20,8 @@ Some distinct identifiers to Swagger-UI 3.X:
|
||||
|
||||
If you've determined this is the version you have, to find the exact version:
|
||||
- Open your browser's web console (changes between browsers)
|
||||
- Type `versions` in the console and execute the call.
|
||||
- You might need to expand the result, until you get a string similar to `swaggerUi : Object { version: "3.1.6", gitRevision: "g786cd47", gitDirty: true, … }`.
|
||||
- Type `JSON.stringify(versions)` in the console and execute the call.
|
||||
- The result should look similar to `swaggerUi : Object { version: "3.1.6", gitRevision: "g786cd47", gitDirty: true, … }`.
|
||||
- The version taken from that example would be `3.1.6`.
|
||||
|
||||
Note: This functionality was added in 3.0.8. If you're unable to execute it, you're likely to use an older version, and in that case the first step would be to upgrade.
|
||||
|
||||
@@ -137,7 +137,7 @@ module.exports = function(rules, options) {
|
||||
}
|
||||
},
|
||||
|
||||
devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null,
|
||||
devtool: specialOptions.sourcemaps ? "nosource-source-map" : null,
|
||||
|
||||
plugins,
|
||||
|
||||
|
||||
30
package.json
30
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.3.1",
|
||||
"version": "3.6.0",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
@@ -32,17 +32,20 @@
|
||||
"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",
|
||||
"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",
|
||||
"e2e": "npm-run-all --parallel -r hot-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.4.1",
|
||||
"expect": "1.20.2",
|
||||
@@ -55,20 +58,20 @@
|
||||
"memoizee": "0.4.1",
|
||||
"promise-worker": "^1.1.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^15.4.0",
|
||||
"react": "^15.6.2",
|
||||
"react-addons-perf": "^15.4.0",
|
||||
"react-addons-shallow-compare": "0.14.8",
|
||||
"react-addons-test-utils": "^15.4.0",
|
||||
"react-collapse": "2.3.1",
|
||||
"react-dom": "^15.4.0",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-collapse": "^4.0.3",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-height": "^2.0.0",
|
||||
"react-hot-loader": "1.3.1",
|
||||
"react-immutable-proptypes": "2.1.0",
|
||||
"react-markdown": "^2.5.0",
|
||||
"react-motion": "0.4.4",
|
||||
"react-motion": "^0.5.2",
|
||||
"react-object-inspector": "0.2.1",
|
||||
"react-redux": "^4.x.x",
|
||||
"react-split-pane": "0.1.57",
|
||||
"react-split-pane": "0.1.70",
|
||||
"redux": "^3.x.x",
|
||||
"redux-immutable": "3.0.8",
|
||||
"redux-logger": "*",
|
||||
@@ -78,11 +81,12 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "^3.2.0",
|
||||
"swagger-client": "^3.4.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.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -105,6 +109,7 @@
|
||||
"enzyme": "^2.7.1",
|
||||
"eslint": "^4.1.1",
|
||||
"eslint-plugin-import": "^2.6.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-react": "^7.1.0",
|
||||
"extract-text-webpack-plugin": "^2.1.2",
|
||||
"file-loader": "0.11.2",
|
||||
@@ -124,6 +129,7 @@
|
||||
"node-sass": "^4.5.0",
|
||||
"npm-run-all": "4.0.2",
|
||||
"null-loader": "0.1.1",
|
||||
"nyc": "^11.3.0",
|
||||
"open": "0.0.5",
|
||||
"postcss-loader": "2.0.6",
|
||||
"raw-loader": "0.5.1",
|
||||
@@ -149,5 +155,11 @@
|
||||
],
|
||||
"optionalDependencies": {
|
||||
"webpack-dev-server": "2.5.0"
|
||||
},
|
||||
"nyc": {
|
||||
"all": true,
|
||||
"include": [
|
||||
"**/src/core/plugins/**.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export default class ArrayModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
@@ -15,7 +16,7 @@ export default class ArrayModel extends Component {
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent, schema, depth, expandDepth, name } = this.props
|
||||
let { getComponent, getConfigs, schema, depth, expandDepth, name } = this.props
|
||||
let description = schema.get("description")
|
||||
let items = schema.get("items")
|
||||
let title = schema.get("title") || name
|
||||
@@ -24,6 +25,7 @@ export default class ArrayModel extends Component {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
const Model = getComponent("Model")
|
||||
const Property = getComponent("Property")
|
||||
|
||||
const titleEl = title &&
|
||||
<span className="model-title">
|
||||
@@ -39,15 +41,13 @@ export default class ArrayModel extends Component {
|
||||
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
|
||||
[
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
<Markdown source={ description } />
|
||||
}
|
||||
<span><Model { ...this.props } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
|
||||
<span><Model { ...this.props } getConfigs={ getConfigs } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
|
||||
]
|
||||
</ModelCollapse>
|
||||
</span>
|
||||
|
||||
@@ -51,7 +51,11 @@ export default class ApiKeyAuth extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(apiKey)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ value && <h6>Authorized</h6>}
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
|
||||
62
src/core/components/auth/auth-item.jsx
Normal file
62
src/core/components/auth/auth-item.jsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class Auths extends React.Component {
|
||||
static propTypes = {
|
||||
schema: ImPropTypes.orderedMap.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onAuthChange: PropTypes.func.isRequired,
|
||||
authorized: ImPropTypes.orderedMap.isRequired
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
schema,
|
||||
name,
|
||||
getComponent,
|
||||
onAuthChange,
|
||||
authorized,
|
||||
errSelectors
|
||||
} = this.props
|
||||
const ApiKeyAuth = getComponent("apiKeyAuth")
|
||||
const BasicAuth = getComponent("basicAuth")
|
||||
|
||||
let authEl
|
||||
|
||||
const type = schema.get("type")
|
||||
|
||||
switch(type) {
|
||||
case "apiKey": authEl = <ApiKeyAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange } />
|
||||
break
|
||||
case "basic": authEl = <BasicAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange } />
|
||||
break
|
||||
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
|
||||
}
|
||||
|
||||
return (<div key={`${name}-jump`}>
|
||||
{ authEl }
|
||||
</div>)
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
definitions: ImPropTypes.iterable.isRequired
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class AuthorizeOperationBtn extends React.Component {
|
||||
static propTypes = {
|
||||
isAuthorized: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
onClick =(e) => {
|
||||
e.stopPropagation()
|
||||
let { onClick } = this.props
|
||||
|
||||
let { security, authActions, authSelectors } = this.props
|
||||
let definitions = authSelectors.getDefinitionsByNames(security)
|
||||
|
||||
authActions.showDefinitions(definitions)
|
||||
if(onClick) {
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let { security, authSelectors } = this.props
|
||||
|
||||
let isAuthorized = authSelectors.isAuthorized(security)
|
||||
|
||||
if(isAuthorized === null) {
|
||||
return null
|
||||
}
|
||||
let { isAuthorized } = this.props
|
||||
|
||||
return (
|
||||
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
|
||||
@@ -30,10 +28,4 @@ export default class AuthorizeOperationBtn extends React.Component {
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
security: ImPropTypes.iterable.isRequired
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ export default class Auths extends React.Component {
|
||||
e.preventDefault()
|
||||
|
||||
let { authActions } = this.props
|
||||
|
||||
authActions.authorize(this.state)
|
||||
}
|
||||
|
||||
@@ -42,10 +41,16 @@ export default class Auths extends React.Component {
|
||||
authActions.logout(auths)
|
||||
}
|
||||
|
||||
close =(e) => {
|
||||
e.preventDefault()
|
||||
let { authActions } = this.props
|
||||
|
||||
authActions.showDefinitions(false)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { definitions, getComponent, authSelectors, errSelectors } = this.props
|
||||
const ApiKeyAuth = getComponent("apiKeyAuth")
|
||||
const BasicAuth = getComponent("basicAuth")
|
||||
const AuthItem = getComponent("AuthItem")
|
||||
const Oauth2 = getComponent("oauth2", true)
|
||||
const Button = getComponent("Button")
|
||||
|
||||
@@ -64,36 +69,19 @@ export default class Auths extends React.Component {
|
||||
!!nonOauthDefinitions.size && <form onSubmit={ this.submitAuth }>
|
||||
{
|
||||
nonOauthDefinitions.map( (schema, name) => {
|
||||
let type = schema.get("type")
|
||||
let authEl
|
||||
|
||||
switch(type) {
|
||||
case "apiKey": authEl = <ApiKeyAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ this.onAuthChange } />
|
||||
break
|
||||
case "basic": authEl = <BasicAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ this.onAuthChange } />
|
||||
break
|
||||
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
|
||||
}
|
||||
|
||||
return (<div key={`${name}-jump`}>
|
||||
{ authEl }
|
||||
</div>)
|
||||
|
||||
return <AuthItem
|
||||
key={name}
|
||||
schema={schema}
|
||||
name={name}
|
||||
getComponent={getComponent}
|
||||
onAuthChange={this.onAuthChange}
|
||||
authorized={authorized}
|
||||
errSelectors={errSelectors}
|
||||
/>
|
||||
}).toArray()
|
||||
}
|
||||
<div className="auth-btn-wrapper">
|
||||
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Done</Button>
|
||||
{
|
||||
nonOauthDefinitions.size === authorizedAuth.size ? <Button className="btn modal-btn auth" onClick={ this.logoutClick }>Logout</Button>
|
||||
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>
|
||||
|
||||
@@ -2,11 +2,6 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import oauth2Authorize from "core/oauth2-authorize"
|
||||
|
||||
const IMPLICIT = "implicit"
|
||||
const ACCESS_CODE = "accessCode"
|
||||
const PASSWORD = "password"
|
||||
const APPLICATION = "application"
|
||||
|
||||
export default class Oauth2 extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string,
|
||||
@@ -16,6 +11,7 @@ export default class Oauth2 extends React.Component {
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
errActions: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.any
|
||||
}
|
||||
@@ -83,7 +79,9 @@ export default class Oauth2 extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
|
||||
let {
|
||||
schema, getComponent, authSelectors, errSelectors, name, specSelectors
|
||||
} = this.props
|
||||
const Input = getComponent("Input")
|
||||
const Row = getComponent("Row")
|
||||
const Col = getComponent("Col")
|
||||
@@ -92,6 +90,14 @@ export default class Oauth2 extends React.Component {
|
||||
const JumpToPath = getComponent("JumpToPath", true)
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
|
||||
const { isOAS3 } = specSelectors
|
||||
|
||||
// Auth type consts
|
||||
const IMPLICIT = "implicit"
|
||||
const PASSWORD = "password"
|
||||
const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
|
||||
const APPLICATION = isOAS3() ? "clientCredentials" : "application"
|
||||
|
||||
let flow = schema.get("flow")
|
||||
let scopes = schema.get("allowedScopes") || schema.get("scopes")
|
||||
let authorizedAuth = authSelectors.authorized().get(name)
|
||||
@@ -102,7 +108,7 @@ export default class Oauth2 extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
<h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
{ !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
|
||||
{ description && <Markdown source={ schema.get("description") } /> }
|
||||
|
||||
@@ -194,11 +200,11 @@ export default class Oauth2 extends React.Component {
|
||||
<Row key={ name }>
|
||||
<div className="checkbox">
|
||||
<Input data-value={ name }
|
||||
id={`${name}-checkbox-${this.state.name}`}
|
||||
id={`${name}-${flow}-checkbox-${this.state.name}`}
|
||||
disabled={ isAuthorized }
|
||||
type="checkbox"
|
||||
onChange={ this.onScopeChange }/>
|
||||
<label htmlFor={`${name}-checkbox-${this.state.name}`}>
|
||||
<label htmlFor={`${name}-${flow}-checkbox-${this.state.name}`}>
|
||||
<span className="item"></span>
|
||||
<div className="text">
|
||||
<p className="name">{name}</p>
|
||||
|
||||
@@ -27,6 +27,16 @@ export default class ContentType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(!nextProps.contentTypes || !nextProps.contentTypes.size) {
|
||||
return
|
||||
}
|
||||
|
||||
if(!nextProps.contentTypes.includes(nextProps.value)) {
|
||||
nextProps.onChange(nextProps.contentTypes.first())
|
||||
}
|
||||
}
|
||||
|
||||
onChangeWrapper = e => this.props.onChange(e.target.value)
|
||||
|
||||
render() {
|
||||
@@ -37,7 +47,7 @@ export default class ContentType extends React.Component {
|
||||
|
||||
return (
|
||||
<div className={ "content-type-wrapper " + ( className || "" ) }>
|
||||
<select className="content-type" value={value} onChange={this.onChangeWrapper} >
|
||||
<select className="content-type" value={value || ""} onChange={this.onChangeWrapper} >
|
||||
{ contentTypes.map( (val) => {
|
||||
return <option key={ val } value={ val }>{ val }</option>
|
||||
}).toArray()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Collapse from "react-collapse"
|
||||
import { Collapse } from "react-collapse"
|
||||
import { presets } from "react-motion"
|
||||
import ObjectInspector from "react-object-inspector"
|
||||
import Perf from "react-addons-perf"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import Collapse from "react-collapse"
|
||||
import { Collapse } from "react-collapse"
|
||||
|
||||
export default class Errors extends React.Component {
|
||||
|
||||
@@ -113,7 +113,7 @@ const SpecErrorItem = ( { error, jumpToLine } ) => {
|
||||
}
|
||||
|
||||
function toTitleCase(str) {
|
||||
return str
|
||||
return (str || "")
|
||||
.split(" ")
|
||||
.map(substr => substr[0].toUpperCase() + substr.slice(1))
|
||||
.join(" ")
|
||||
|
||||
@@ -8,7 +8,6 @@ export default class Execute extends Component {
|
||||
specActions: PropTypes.object.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
onExecute: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -2,20 +2,24 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Im from "immutable"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
export default class Headers extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
headers: PropTypes.object.isRequired
|
||||
headers: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
let { headers } = this.props
|
||||
let { headers, getComponent } = this.props
|
||||
const Property = getComponent("Property")
|
||||
|
||||
if ( !headers || !headers.size )
|
||||
return null
|
||||
|
||||
return (
|
||||
return (
|
||||
<div className="headers-wrapper">
|
||||
<h4 className="headers__title">Headers:</h4>
|
||||
<table className="headers">
|
||||
@@ -32,10 +36,13 @@ export default class Headers extends React.Component {
|
||||
if(!Im.Map.isMap(header)) {
|
||||
return null
|
||||
}
|
||||
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
|
||||
const schemaExample = header.getIn(["schema", "example"])
|
||||
|
||||
return (<tr key={ key }>
|
||||
<td className="header-col">{ key }</td>
|
||||
<td className="header-col">{ header.get( "description" ) }</td>
|
||||
<td className="header-col">{ header.get( "type" ) }</td>
|
||||
<td className="header-col">{ type } { schemaExample ? <Property propKey={ "Example" } propVal={ schemaExample } propStyle={ propStyle } /> : null }</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
|
||||
class Path extends React.Component {
|
||||
@@ -35,9 +36,9 @@ class Contact extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
|
||||
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
|
||||
{ email &&
|
||||
<a href={`mailto:${email}`}>
|
||||
<a href={sanitizeUrl(`mailto:${email}`)}>
|
||||
{ url ? `Send email to ${name}` : `Contact ${name}`}
|
||||
</a>
|
||||
}
|
||||
@@ -59,7 +60,7 @@ class License extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
url ? <a target="_blank" href={ url }>{ name }</a>
|
||||
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
|
||||
: <span>{ name }</span>
|
||||
}
|
||||
</div>
|
||||
@@ -97,7 +98,7 @@ export default class Info extends React.Component {
|
||||
{ version && <VersionStamp version={version}></VersionStamp> }
|
||||
</h2>
|
||||
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
|
||||
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
|
||||
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
|
||||
</hgroup>
|
||||
|
||||
<div className="description">
|
||||
@@ -106,14 +107,14 @@ export default class Info extends React.Component {
|
||||
|
||||
{
|
||||
termsOfService && <div>
|
||||
<a target="_blank" href={ termsOfService }>Terms of service</a>
|
||||
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
{ contact && contact.size ? <Contact data={ contact } /> : null }
|
||||
{ license && license.size ? <License license={ license } /> : null }
|
||||
{ externalDocsUrl ?
|
||||
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
|
||||
<a target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
|
||||
: null }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import OriCollapse from "react-collapse"
|
||||
import { Collapse as OriCollapse } from "react-collapse"
|
||||
|
||||
function xclass(...args) {
|
||||
return args.filter(a => !!a).join(" ").trim()
|
||||
@@ -183,7 +183,7 @@ export class Select extends React.Component {
|
||||
{ allowEmptyValue ? <option value="">--</option> : null }
|
||||
{
|
||||
allowedValues.map(function (item, key) {
|
||||
return <option key={ key } value={ String(item) }>{ item }</option>
|
||||
return <option key={ key } value={ String(item) }>{ String(item) }</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
|
||||
@@ -94,8 +94,9 @@ export default class BaseLayout extends React.Component {
|
||||
) : null }
|
||||
|
||||
{ servers && servers.size ? (
|
||||
<div className="server-container">
|
||||
<div className="global-server-container">
|
||||
<Col className="servers wrapper" mobile={12}>
|
||||
<span className="servers-title">Server</span>
|
||||
<Servers
|
||||
servers={servers}
|
||||
currentServer={oas3Selectors.selectedServer()}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { Iterable } from "immutable"
|
||||
|
||||
const Headers = ( { headers } )=>{
|
||||
return (
|
||||
@@ -28,19 +29,29 @@ Duration.propTypes = {
|
||||
|
||||
export default class LiveResponse extends React.Component {
|
||||
static propTypes = {
|
||||
response: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
pathMethod: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
response: PropTypes.instanceOf(Iterable).isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// BUG: props.response is always coming back as a new Immutable instance
|
||||
// same issue as responses.jsx (tryItOutResponse)
|
||||
return this.props.response !== nextProps.response
|
||||
|| this.props.path !== nextProps.path
|
||||
|| this.props.method !== nextProps.method
|
||||
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|
||||
}
|
||||
|
||||
render() {
|
||||
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
|
||||
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props
|
||||
const { showMutatedRequest } = getConfigs()
|
||||
|
||||
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
|
||||
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)
|
||||
const status = response.get("status")
|
||||
const url = response.get("url")
|
||||
const headers = response.get("headers").toJS()
|
||||
@@ -118,7 +129,6 @@ export default class LiveResponse extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
request: ImPropTypes.map,
|
||||
response: ImPropTypes.map
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export default class ModelExample extends React.Component {
|
||||
{
|
||||
!isExecute && this.state.activeTab === "model" && <ModelWrapper schema={ schema }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={ defaultModelExpandDepth } />
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@ export default class ModelComponent extends Component {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent } = this.props
|
||||
let { getComponent, getConfigs } = this.props
|
||||
const Model = getComponent("Model")
|
||||
|
||||
return <div className="model-box">
|
||||
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
<Model { ...this.props } getConfigs={ getConfigs } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { Component } from "react"
|
||||
import React, { PureComponent } from "react"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class Model extends Component {
|
||||
export default class Model extends PureComponent {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
schema: ImPropTypes.orderedMap.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
@@ -29,7 +31,7 @@ export default class Model extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
|
||||
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef } = this.props
|
||||
const ObjectModel = getComponent("ObjectModel")
|
||||
const ArrayModel = getComponent("ArrayModel")
|
||||
const PrimitiveModel = getComponent("PrimitiveModel")
|
||||
@@ -53,6 +55,7 @@ export default class Model extends Component {
|
||||
case "object":
|
||||
return <ObjectModel
|
||||
className="object" { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
@@ -60,6 +63,7 @@ export default class Model extends Component {
|
||||
case "array":
|
||||
return <ArrayModel
|
||||
className="array" { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
@@ -72,6 +76,7 @@ export default class Model extends Component {
|
||||
return <PrimitiveModel
|
||||
{ ...this.props }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
|
||||
@@ -36,6 +36,7 @@ export default class Models extends Component {
|
||||
expandDepth={ defaultModelExpandDepth }
|
||||
schema={ model }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }/>
|
||||
</div>
|
||||
}).toArray()
|
||||
|
||||
@@ -9,6 +9,7 @@ export default class ObjectModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
@@ -17,10 +18,16 @@ export default class ObjectModel extends Component {
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, name, isRef, getComponent, depth, expandDepth, ...otherProps } = this.props
|
||||
let { schema, name, isRef, getComponent, getConfigs, depth, expandDepth, ...otherProps } = this.props
|
||||
let { specSelectors } = otherProps
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
if(!schema) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
let description = schema.get("description")
|
||||
let properties = schema.get("properties")
|
||||
let additionalProperties = schema.get("additionalProperties")
|
||||
@@ -72,13 +79,14 @@ export default class ObjectModel extends Component {
|
||||
{
|
||||
!(properties && properties.size) ? null : properties.entrySeq().map(
|
||||
([key, value]) => {
|
||||
let isDeprecated = isOAS3() && value.get("deprecated")
|
||||
let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)
|
||||
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
|
||||
if ( isRequired ) {
|
||||
propertyStyle.fontWeight = "bold"
|
||||
}
|
||||
|
||||
return (<tr key={key}>
|
||||
return (<tr key={key} className={isDeprecated && "deprecated"}>
|
||||
<td style={ propertyStyle }>
|
||||
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> }
|
||||
</td>
|
||||
@@ -86,12 +94,37 @@ export default class ObjectModel extends Component {
|
||||
<Model key={ `object-${name}-${key}_${value}` } { ...otherProps }
|
||||
required={ isRequired }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ value }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
// empty row befor extensions...
|
||||
!showExtensions ? null : <tr> </tr>
|
||||
}
|
||||
{
|
||||
!showExtensions ? null :
|
||||
schema.entrySeq().map(
|
||||
([key, value]) => {
|
||||
if(key.slice(0,2) !== "x-") {
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedValue = !value ? null : value.toJS ? value.toJS() : value
|
||||
|
||||
return (<tr key={key} style={{ color: "#777" }}>
|
||||
<td>
|
||||
{ key }
|
||||
</td>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
{ JSON.stringify(normalizedValue) }
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
!additionalProperties || !additionalProperties.size ? null
|
||||
: <tr>
|
||||
@@ -99,6 +132,7 @@ export default class ObjectModel extends Component {
|
||||
<td>
|
||||
<Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ additionalProperties }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
@@ -112,6 +146,7 @@ export default class ObjectModel extends Component {
|
||||
{anyOf.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
@@ -126,6 +161,7 @@ export default class ObjectModel extends Component {
|
||||
{oneOf.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
@@ -137,12 +173,14 @@ export default class ObjectModel extends Component {
|
||||
: <tr>
|
||||
<td>{ "not ->" }</td>
|
||||
<td>
|
||||
{not.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
<div>
|
||||
<Model { ...otherProps }
|
||||
required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ not }
|
||||
depth={ depth + 1 } />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
export default class OnlineValidatorBadge extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -32,6 +33,8 @@ export default class OnlineValidatorBadge extends React.Component {
|
||||
let { getConfigs } = this.props
|
||||
let { spec } = getConfigs()
|
||||
|
||||
let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)
|
||||
|
||||
if ( typeof spec === "object" && Object.keys(spec).length) return null
|
||||
|
||||
if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf("localhost") >= 0
|
||||
@@ -40,8 +43,8 @@ export default class OnlineValidatorBadge extends React.Component {
|
||||
}
|
||||
|
||||
return (<span style={{ float: "right"}}>
|
||||
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
|
||||
<ValidatorImage src={`${ this.state.validatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
|
||||
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
|
||||
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
|
||||
</a>
|
||||
</span>)
|
||||
}
|
||||
|
||||
17
src/core/components/operation-extension-row.jsx
Normal file
17
src/core/components/operation-extension-row.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const OperationExtRow = ({ xKey, xVal }) => {
|
||||
const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal
|
||||
|
||||
return (<tr>
|
||||
<td>{ xKey }</td>
|
||||
<td>{ JSON.stringify(xNormalizedValue) }</td>
|
||||
</tr>)
|
||||
}
|
||||
OperationExtRow.propTypes = {
|
||||
xKey: PropTypes.string,
|
||||
xVal: PropTypes.any
|
||||
}
|
||||
|
||||
export default OperationExtRow
|
||||
35
src/core/components/operation-extensions.jsx
Normal file
35
src/core/components/operation-extensions.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const OperationExt = ({ extensions, getComponent }) => {
|
||||
let OperationExtRow = getComponent("OperationExtRow")
|
||||
return (
|
||||
<div className="opblock-section">
|
||||
<div className="opblock-section-header">
|
||||
<h4>Extensions</h4>
|
||||
</div>
|
||||
<div className="table-container">
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="col col_header">Field</td>
|
||||
<td className="col col_header">Value</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
extensions.entrySeq().map(([k, v]) => <OperationExtRow key={`${k}-${v}`} xKey={k} xVal={v} />)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
OperationExt.propTypes = {
|
||||
extensions: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default OperationExt
|
||||
@@ -1,138 +1,93 @@
|
||||
import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { getList } from "core/utils"
|
||||
import * as CustomPropTypes from "core/proptypes"
|
||||
|
||||
//import "less/opblock"
|
||||
import { getExtensions, sanitizeUrl } from "core/utils"
|
||||
import { Iterable } from "immutable"
|
||||
|
||||
export default class Operation extends PureComponent {
|
||||
static propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
showSummary: PropTypes.bool,
|
||||
operation: PropTypes.instanceOf(Iterable).isRequired,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
|
||||
isShownKey: CustomPropTypes.arrayOrString.isRequired,
|
||||
jumpToKey: CustomPropTypes.arrayOrString.isRequired,
|
||||
|
||||
allowTryItOut: PropTypes.bool,
|
||||
|
||||
displayOperationId: PropTypes.bool,
|
||||
displayRequestDuration: PropTypes.bool,
|
||||
|
||||
response: PropTypes.object,
|
||||
request: PropTypes.object,
|
||||
toggleShown: PropTypes.func.isRequired,
|
||||
onTryoutClick: PropTypes.func.isRequired,
|
||||
onCancelClick: PropTypes.func.isRequired,
|
||||
onExecute: PropTypes.func.isRequired,
|
||||
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
authActions: PropTypes.object,
|
||||
authSelectors: PropTypes.object,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
fn: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
showSummary: true,
|
||||
operation: null,
|
||||
response: null,
|
||||
allowTryItOut: true,
|
||||
displayOperationId: false,
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
tryItOutEnabled: false
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const defaultContentType = "application/json"
|
||||
let { specActions, path, method, operation } = nextProps
|
||||
let producesValue = operation.get("produces_value")
|
||||
let produces = operation.get("produces")
|
||||
let consumes = operation.get("consumes")
|
||||
let consumesValue = operation.get("consumes_value")
|
||||
|
||||
if(nextProps.response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if (producesValue === undefined) {
|
||||
producesValue = produces && produces.size ? produces.first() : defaultContentType
|
||||
specActions.changeProducesValue([path, method], producesValue)
|
||||
}
|
||||
|
||||
if (consumesValue === undefined) {
|
||||
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
|
||||
specActions.changeConsumesValue([path, method], consumesValue)
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, isShownKey } = this.props
|
||||
layoutActions.show(isShownKey, !this.isShown())
|
||||
}
|
||||
|
||||
isShown =() => {
|
||||
let { layoutSelectors, isShownKey, getConfigs } = this.props
|
||||
let { docExpansion } = getConfigs()
|
||||
|
||||
return layoutSelectors.isShown(isShownKey, docExpansion === "full" ) // Here is where we set the default
|
||||
}
|
||||
|
||||
onTryoutClick =() => {
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
}
|
||||
|
||||
onCancelClick =() => {
|
||||
let { specActions, path, method } = this.props
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
specActions.clearValidateParams([path, method])
|
||||
}
|
||||
|
||||
onExecute = () => {
|
||||
this.setState({ executeInProgress: true })
|
||||
request: null
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
isShownKey,
|
||||
jumpToKey,
|
||||
path,
|
||||
method,
|
||||
operation,
|
||||
showSummary,
|
||||
response,
|
||||
request,
|
||||
allowTryItOut,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
toggleShown,
|
||||
onTryoutClick,
|
||||
onCancelClick,
|
||||
onExecute,
|
||||
fn,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
specActions,
|
||||
specSelectors,
|
||||
authActions,
|
||||
authSelectors,
|
||||
getConfigs,
|
||||
oas3Actions
|
||||
oas3Actions,
|
||||
oas3Selectors
|
||||
} = this.props
|
||||
let operationProps = this.props.operation
|
||||
|
||||
let summary = operation.get("summary")
|
||||
let description = operation.get("description")
|
||||
let deprecated = operation.get("deprecated")
|
||||
let externalDocs = operation.get("externalDocs")
|
||||
let {
|
||||
isShown,
|
||||
isAuthorized,
|
||||
jumpToKey,
|
||||
path,
|
||||
method,
|
||||
op,
|
||||
tag,
|
||||
showSummary,
|
||||
operationId,
|
||||
allowTryItOut,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
tryItOutEnabled,
|
||||
executeInProgress
|
||||
} = operationProps.toJS()
|
||||
|
||||
let {
|
||||
summary,
|
||||
description,
|
||||
deprecated,
|
||||
externalDocs,
|
||||
schemes
|
||||
} = op.operation
|
||||
|
||||
let operation = operationProps.getIn(["op", "operation"])
|
||||
let security = operationProps.get("security")
|
||||
let responses = operation.get("responses")
|
||||
let security = operation.get("security") || specSelectors.security()
|
||||
let produces = operation.get("produces")
|
||||
let schemes = operation.get("schemes")
|
||||
let parameters = getList(operation, ["parameters"])
|
||||
let operationId = operation.get("__originalOperationId")
|
||||
let operationScheme = specSelectors.operationScheme(path, method)
|
||||
let isShownKey = ["operations", tag, operationId]
|
||||
let extensions = getExtensions(operation)
|
||||
|
||||
const Responses = getComponent("responses")
|
||||
const Parameters = getComponent( "parameters" )
|
||||
@@ -143,10 +98,10 @@ export default class Operation extends PureComponent {
|
||||
const Collapse = getComponent( "Collapse" )
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
const Schemes = getComponent( "schemes" )
|
||||
const OperationServers = getComponent( "OperationServers" )
|
||||
const OperationExt = getComponent( "OperationExt" )
|
||||
|
||||
const { deepLinking } = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
// Merge in Live Response
|
||||
if(responses && response && response.size > 0) {
|
||||
@@ -154,19 +109,17 @@ export default class Operation extends PureComponent {
|
||||
response = response.set("notDocumented", notDocumented)
|
||||
}
|
||||
|
||||
let { tryItOutEnabled } = this.state
|
||||
let shown = this.isShown()
|
||||
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
|
||||
|
||||
return (
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
|
||||
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
|
||||
href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
|
||||
href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}>
|
||||
<span>{path}</span>
|
||||
</a>
|
||||
<JumpToPath path={jumpToKey} />
|
||||
@@ -182,13 +135,17 @@ export default class Operation extends PureComponent {
|
||||
|
||||
{
|
||||
(!security || !security.count()) ? null :
|
||||
<AuthorizeOperationBtn authActions={ authActions }
|
||||
security={ security }
|
||||
authSelectors={ authSelectors }/>
|
||||
<AuthorizeOperationBtn
|
||||
isAuthorized={ isAuthorized }
|
||||
onClick={() => {
|
||||
const applicableDefinitions = authSelectors.definitionsForRequirements(security)
|
||||
authActions.showDefinitions(applicableDefinitions)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Collapse isOpened={shown}>
|
||||
<Collapse isOpened={isShown}>
|
||||
<div className="opblock-body">
|
||||
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
|
||||
{ description &&
|
||||
@@ -199,23 +156,24 @@ export default class Operation extends PureComponent {
|
||||
</div>
|
||||
}
|
||||
{
|
||||
externalDocs && externalDocs.get("url") ?
|
||||
externalDocs && externalDocs.url ?
|
||||
<div className="opblock-external-docs-wrapper">
|
||||
<h4 className="opblock-title_normal">Find more details</h4>
|
||||
<div className="opblock-external-docs">
|
||||
<span className="opblock-external-docs__description">
|
||||
<Markdown source={ externalDocs.get("description") } />
|
||||
<Markdown source={ externalDocs.description } />
|
||||
</span>
|
||||
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
|
||||
<a target="_blank" className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.url) }>{ externalDocs.url }</a>
|
||||
</div>
|
||||
</div> : null
|
||||
}
|
||||
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { this.onTryoutClick }
|
||||
onCancelClick = { this.onCancelClick }
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
|
||||
@@ -227,6 +185,21 @@ export default class Operation extends PureComponent {
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
|
||||
{ !tryItOutEnabled ? null :
|
||||
<OperationServers
|
||||
getComponent={getComponent}
|
||||
path={path}
|
||||
method={method}
|
||||
operationServers={operation.get("servers")}
|
||||
pathServers={specSelectors.paths().getIn([path, "servers"])}
|
||||
getSelectedServer={oas3Selectors.selectedServer}
|
||||
setSelectedServer={oas3Actions.setSelectedServer}
|
||||
setServerVariableValue={oas3Actions.setServerVariableValue}
|
||||
getServerVariable={oas3Selectors.serverVariableValue}
|
||||
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
|
||||
/>
|
||||
}
|
||||
|
||||
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ? <div className="opblock-schemes">
|
||||
<Schemes schemes={ schemes }
|
||||
path={ path }
|
||||
@@ -240,25 +213,23 @@ export default class Operation extends PureComponent {
|
||||
{ !tryItOutEnabled || !allowTryItOut ? null :
|
||||
|
||||
<Execute
|
||||
getComponent={getComponent}
|
||||
operation={ operation }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
path={ path }
|
||||
method={ method }
|
||||
onExecute={ this.onExecute } />
|
||||
onExecute={ onExecute } />
|
||||
}
|
||||
|
||||
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null :
|
||||
<Clear
|
||||
onClick={ this.onClearClick }
|
||||
specActions={ specActions }
|
||||
path={ path }
|
||||
method={ method }/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{this.state.executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
|
||||
{executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
|
||||
|
||||
{ !responses ? null :
|
||||
<Responses
|
||||
@@ -272,10 +243,15 @@ export default class Operation extends PureComponent {
|
||||
specActions={ specActions }
|
||||
produces={ produces }
|
||||
producesValue={ operation.get("produces_value") }
|
||||
pathMethod={ [path, method] }
|
||||
path={ path }
|
||||
method={ method }
|
||||
displayRequestDuration={ displayRequestDuration }
|
||||
fn={fn} />
|
||||
}
|
||||
|
||||
{ !showExtensions || !extensions.size ? null :
|
||||
<OperationExt extensions={ extensions } getComponent={ getComponent } />
|
||||
}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { helpers } from "swagger-client"
|
||||
import { createDeepLinkPath } from "core/utils"
|
||||
const { opId } = helpers
|
||||
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
|
||||
|
||||
export default class Operations extends React.Component {
|
||||
|
||||
@@ -21,28 +19,20 @@ export default class Operations extends React.Component {
|
||||
render() {
|
||||
let {
|
||||
specSelectors,
|
||||
specActions,
|
||||
oas3Actions,
|
||||
getComponent,
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
authActions,
|
||||
authSelectors,
|
||||
getConfigs,
|
||||
fn
|
||||
getConfigs
|
||||
} = this.props
|
||||
|
||||
let taggedOps = specSelectors.taggedOperations()
|
||||
|
||||
const Operation = getComponent("operation")
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
const Collapse = getComponent("Collapse")
|
||||
const Markdown = getComponent("Markdown")
|
||||
|
||||
let showSummary = layoutSelectors.showSummary()
|
||||
let {
|
||||
docExpansion,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
maxDisplayedTags,
|
||||
deepLinking
|
||||
} = getConfigs()
|
||||
@@ -101,7 +91,7 @@ export default class Operations extends React.Component {
|
||||
{ tagExternalDocsUrl ? ": " : null }
|
||||
{ tagExternalDocsUrl ?
|
||||
<a
|
||||
href={tagExternalDocsUrl}
|
||||
href={sanitizeUrl(tagExternalDocsUrl)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
target={"_blank"}
|
||||
>{tagExternalDocsUrl}</a> : null
|
||||
@@ -120,47 +110,15 @@ export default class Operations extends React.Component {
|
||||
<Collapse isOpened={showTag}>
|
||||
{
|
||||
operations.map( op => {
|
||||
const path = op.get("path")
|
||||
const method = op.get("method")
|
||||
|
||||
const path = op.get("path", "")
|
||||
const method = op.get("method", "")
|
||||
const jumpToKey = `paths.${path}.${method}`
|
||||
|
||||
const operationId =
|
||||
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
|
||||
const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)]
|
||||
|
||||
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
|
||||
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
|
||||
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
|
||||
|
||||
return <Operation
|
||||
{...op.toObject()}
|
||||
|
||||
isShownKey={isShownKey}
|
||||
jumpToKey={jumpToKey}
|
||||
showSummary={showSummary}
|
||||
key={isShownKey}
|
||||
response={ response }
|
||||
request={ request }
|
||||
allowTryItOut={allowTryItOut}
|
||||
|
||||
displayOperationId={displayOperationId}
|
||||
displayRequestDuration={displayRequestDuration}
|
||||
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
|
||||
oas3Actions={oas3Actions}
|
||||
|
||||
layoutActions={ layoutActions }
|
||||
layoutSelectors={ layoutSelectors }
|
||||
|
||||
authActions={ authActions }
|
||||
authSelectors={ authSelectors }
|
||||
|
||||
getComponent={ getComponent }
|
||||
fn={fn}
|
||||
getConfigs={ getConfigs }
|
||||
return <OperationContainer
|
||||
key={`${path}-${method}`}
|
||||
op={op}
|
||||
path={path}
|
||||
method={method}
|
||||
tag={tag}
|
||||
/>
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
12
src/core/components/parameter-extension.jsx
Normal file
12
src/core/components/parameter-extension.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const ParameterExt = ({ xKey, xVal }) => {
|
||||
return <div className="parameter__extension">{ xKey }: { String(xVal) }</div>
|
||||
}
|
||||
ParameterExt.propTypes = {
|
||||
xKey: PropTypes.string,
|
||||
xVal: PropTypes.any
|
||||
}
|
||||
|
||||
export default ParameterExt
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { Component } from "react"
|
||||
import { Map } from "immutable"
|
||||
import PropTypes from "prop-types"
|
||||
import win from "core/window"
|
||||
import { getExtensions } from "core/utils"
|
||||
|
||||
export default class ParameterRow extends Component {
|
||||
static propTypes = {
|
||||
@@ -29,11 +31,21 @@ export default class ParameterRow extends Component {
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
let { specSelectors, pathMethod, param } = props
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
let example = param.get("example")
|
||||
let defaultValue = param.get("default")
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let enumValue
|
||||
|
||||
if(isOAS3()) {
|
||||
let schema = param.get("schema") || Map()
|
||||
enumValue = schema.get("enum")
|
||||
} else {
|
||||
enumValue = parameter ? parameter.get("enum") : undefined
|
||||
}
|
||||
let paramValue = parameter ? parameter.get("value") : undefined
|
||||
let enumValue = parameter ? parameter.get("enum") : undefined
|
||||
|
||||
let value
|
||||
|
||||
if ( paramValue !== undefined ) {
|
||||
@@ -61,6 +73,8 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
// const onChangeWrapper = (value) => onChange(param, value)
|
||||
const JsonSchemaForm = getComponent("JsonSchemaForm")
|
||||
const ParamBody = getComponent("ParamBody")
|
||||
@@ -80,6 +94,7 @@ export default class ParameterRow extends Component {
|
||||
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ParameterExt = getComponent("ParameterExt")
|
||||
|
||||
let schema = param.get("schema")
|
||||
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type")
|
||||
@@ -89,6 +104,7 @@ export default class ParameterRow extends Component {
|
||||
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let value = parameter ? parameter.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
return (
|
||||
<tr>
|
||||
@@ -102,6 +118,7 @@ export default class ParameterRow extends Component {
|
||||
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
|
||||
</div>
|
||||
<div className="parameter__in">({ param.get("in") })</div>
|
||||
{ !showExtensions || !extensions.size ? null : extensions.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
|
||||
</td>
|
||||
|
||||
<td className="col parameters-col_description">
|
||||
@@ -115,7 +132,7 @@ export default class ParameterRow extends Component {
|
||||
required={ required }
|
||||
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
|
||||
onChange={ this.onChangeWrapper }
|
||||
schema={ param }/>
|
||||
schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { getExtensions } from "core/utils"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
@@ -7,12 +8,15 @@ export default class Primitive extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
name: PropTypes.string,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, getComponent, name, depth } = this.props
|
||||
let { schema, getComponent, getConfigs, name, depth } = this.props
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
if(!schema || !schema.get) {
|
||||
// don't render if schema isn't correctly formed
|
||||
@@ -25,9 +29,13 @@ export default class Primitive extends Component {
|
||||
let enumArray = schema.get("enum")
|
||||
let title = schema.get("title") || name
|
||||
let description = schema.get("description")
|
||||
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
let extensions = getExtensions(schema)
|
||||
let properties = schema
|
||||
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
.filterNot( (v, key) => extensions.has(key) )
|
||||
const Markdown = getComponent("Markdown")
|
||||
const EnumModel = getComponent("EnumModel")
|
||||
const Property = getComponent("Property")
|
||||
|
||||
return <span className="model">
|
||||
<span className="prop">
|
||||
@@ -35,9 +43,10 @@ export default class Primitive extends Component {
|
||||
<span className="prop-type">{ type }</span>
|
||||
{ format && <span className="prop-format">(${format})</span>}
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
showExtensions && extensions.size ? extensions.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
|
||||
16
src/core/components/property.jsx
Normal file
16
src/core/components/property.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const Property = ({ propKey, propVal, propStyle }) => {
|
||||
return (
|
||||
<span style={ propStyle }>
|
||||
<br />{ propKey }: { String(propVal) }</span>
|
||||
)
|
||||
}
|
||||
Property.propTypes = {
|
||||
propKey: PropTypes.string,
|
||||
propVal: PropTypes.any,
|
||||
propStyle: PropTypes.object
|
||||
}
|
||||
|
||||
export default Property
|
||||
@@ -29,7 +29,11 @@ Markdown.propTypes = {
|
||||
export default Markdown
|
||||
|
||||
const sanitizeOptions = {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "img" ]),
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img" ]),
|
||||
allowedAttributes: {
|
||||
...sanitize.defaults.allowedAttributes,
|
||||
"img": sanitize.defaults.allowedAttributes.img.concat(["title"])
|
||||
},
|
||||
textFilter: function(text) {
|
||||
return text.replace(/"/g, "\"")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { formatXml } from "core/utils"
|
||||
import formatXml from "xml-but-prettier"
|
||||
import lowerCase from "lodash/lowerCase"
|
||||
|
||||
export default class ResponseBody extends React.Component {
|
||||
@@ -31,7 +31,10 @@ export default class ResponseBody extends React.Component {
|
||||
|
||||
// XML
|
||||
} else if (/xml/i.test(contentType)) {
|
||||
body = formatXml(content)
|
||||
body = formatXml(content, {
|
||||
textNodesOnSameLine: true,
|
||||
indentor: " "
|
||||
})
|
||||
bodyEl = <HighlightCode value={ body } />
|
||||
|
||||
// HTML or Plain Text
|
||||
@@ -54,9 +57,6 @@ export default class ResponseBody extends React.Component {
|
||||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
|
||||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
|
||||
|
||||
let contentLength = headers["content-length"] || headers["Content-Length"]
|
||||
if ( !(+contentLength) ) return null
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
|
||||
if (!isSafari && "Blob" in window) {
|
||||
@@ -83,8 +83,12 @@ export default class ResponseBody extends React.Component {
|
||||
// Anything else (CORS)
|
||||
} else if (typeof content === "string") {
|
||||
bodyEl = <HighlightCode value={ content } />
|
||||
} else {
|
||||
} else if ( content.size > 0 ) {
|
||||
// We don't know the contentType, but there was some content returned
|
||||
bodyEl = <div>Unknown response type</div>
|
||||
} else {
|
||||
// We don't know the contentType and there was no content returned
|
||||
bodyEl = null
|
||||
}
|
||||
|
||||
return ( !bodyEl ? null : <div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import cx from "classnames"
|
||||
import { fromJS, Seq } from "immutable"
|
||||
import { fromJS, Seq, Iterable } from "immutable"
|
||||
import { getSampleSchema, fromJSOrdered } from "core/utils"
|
||||
|
||||
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
|
||||
@@ -42,7 +42,7 @@ export default class Response extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
code: PropTypes.string.isRequired,
|
||||
response: PropTypes.object,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
className: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
@@ -153,7 +153,10 @@ export default class Response extends React.Component {
|
||||
) : null}
|
||||
|
||||
{ headers ? (
|
||||
<Headers headers={ headers }/>
|
||||
<Headers
|
||||
headers={ headers }
|
||||
getComponent={ getComponent }
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +1,52 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import { fromJS, Iterable } from "immutable"
|
||||
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
|
||||
|
||||
export default class Responses extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
request: PropTypes.object,
|
||||
tryItOutResponse: PropTypes.object,
|
||||
responses: PropTypes.object.isRequired,
|
||||
produces: PropTypes.object,
|
||||
tryItOutResponse: PropTypes.instanceOf(Iterable),
|
||||
responses: PropTypes.instanceOf(Iterable).isRequired,
|
||||
produces: PropTypes.instanceOf(Iterable),
|
||||
producesValue: PropTypes.any,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
pathMethod: PropTypes.array.isRequired,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
fn: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
request: null,
|
||||
tryItOutResponse: null,
|
||||
produces: fromJS(["application/json"]),
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// BUG: props.tryItOutResponse is always coming back as a new Immutable instance
|
||||
let render = this.props.tryItOutResponse !== nextProps.tryItOutResponse
|
||||
|| this.props.responses !== nextProps.responses
|
||||
|| this.props.produces !== nextProps.produces
|
||||
|| this.props.producesValue !== nextProps.producesValue
|
||||
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|
||||
|| this.props.path !== nextProps.path
|
||||
|| this.props.method !== nextProps.method
|
||||
return render
|
||||
}
|
||||
|
||||
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)
|
||||
|
||||
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
|
||||
const { oas3Actions, pathMethod } = this.props
|
||||
const { oas3Actions, path, method } = this.props
|
||||
if(controlsAcceptHeader) {
|
||||
oas3Actions.setResponseContentType({
|
||||
value,
|
||||
pathMethod
|
||||
path,
|
||||
method
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -43,7 +54,6 @@ export default class Responses extends React.Component {
|
||||
render() {
|
||||
let {
|
||||
responses,
|
||||
request,
|
||||
tryItOutResponse,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
@@ -81,12 +91,12 @@ export default class Responses extends React.Component {
|
||||
{
|
||||
!tryItOutResponse ? null
|
||||
: <div>
|
||||
<LiveResponse request={ request }
|
||||
response={ tryItOutResponse }
|
||||
<LiveResponse response={ tryItOutResponse }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ this.props.pathMethod }
|
||||
path={ this.props.path }
|
||||
method={ this.props.method }
|
||||
displayRequestDuration={ displayRequestDuration } />
|
||||
<h4>Responses</h4>
|
||||
</div>
|
||||
|
||||
206
src/core/containers/OperationContainer.jsx
Normal file
206
src/core/containers/OperationContainer.jsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { helpers } from "swagger-client"
|
||||
import { Iterable, fromJS } from "immutable"
|
||||
|
||||
const { opId } = helpers
|
||||
|
||||
export default class OperationContainer extends PureComponent {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
tryItOutEnabled: false,
|
||||
executeInProgress: false
|
||||
}
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
op: PropTypes.instanceOf(Iterable).isRequired,
|
||||
tag: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operationId: PropTypes.string.isRequired,
|
||||
showSummary: PropTypes.bool.isRequired,
|
||||
isShown: PropTypes.bool.isRequired,
|
||||
jumpToKey: PropTypes.string.isRequired,
|
||||
allowTryItOut: PropTypes.bool,
|
||||
displayOperationId: PropTypes.bool,
|
||||
isAuthorized: PropTypes.bool,
|
||||
displayRequestDuration: PropTypes.bool,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
security: PropTypes.instanceOf(Iterable),
|
||||
isDeepLinkingEnabled: PropTypes.bool.isRequired,
|
||||
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
authActions: PropTypes.object,
|
||||
oas3Actions: PropTypes.object,
|
||||
oas3Selectors: PropTypes.object,
|
||||
authSelectors: PropTypes.object,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
showSummary: true,
|
||||
response: null,
|
||||
allowTryItOut: true,
|
||||
displayOperationId: false,
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
mapStateToProps(nextState, props) {
|
||||
const { op, layoutSelectors, getConfigs } = props
|
||||
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration } = getConfigs()
|
||||
const showSummary = layoutSelectors.showSummary()
|
||||
const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
|
||||
const isShownKey = ["operations", props.tag, operationId]
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
const allowTryItOut = typeof props.allowTryItOut === "undefined" ?
|
||||
props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut
|
||||
const security = op.getIn(["operation", "security"]) || props.specSelectors.security()
|
||||
|
||||
return {
|
||||
operationId,
|
||||
isDeepLinkingEnabled,
|
||||
showSummary,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
allowTryItOut,
|
||||
security,
|
||||
isAuthorized: props.authSelectors.isAuthorized(security),
|
||||
isShown: layoutSelectors.isShown(isShownKey, docExpansion === "full" ),
|
||||
jumpToKey: `paths.${props.path}.${props.method}`,
|
||||
response: props.specSelectors.responseFor(props.path, props.method),
|
||||
request: props.specSelectors.requestFor(props.path, props.method)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const defaultContentType = "application/json"
|
||||
let { specActions, path, method, op } = nextProps
|
||||
let operation = op.get("operation")
|
||||
let producesValue = operation.get("produces_value")
|
||||
let produces = operation.get("produces")
|
||||
let consumes = operation.get("consumes")
|
||||
let consumesValue = operation.get("consumes_value")
|
||||
|
||||
if(nextProps.response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if (producesValue === undefined) {
|
||||
producesValue = produces && produces.size ? produces.first() : defaultContentType
|
||||
specActions.changeProducesValue([path, method], producesValue)
|
||||
}
|
||||
|
||||
if (consumesValue === undefined) {
|
||||
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
|
||||
specActions.changeConsumesValue([path, method], consumesValue)
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, tag, operationId, isShown } = this.props
|
||||
layoutActions.show(["operations", tag, operationId], !isShown)
|
||||
}
|
||||
|
||||
onTryoutClick =() => {
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
}
|
||||
|
||||
onCancelClick =() => {
|
||||
let { specActions, path, method } = this.props
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
specActions.clearValidateParams([path, method])
|
||||
}
|
||||
|
||||
onExecute = () => {
|
||||
this.setState({ executeInProgress: true })
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
op,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
operationId,
|
||||
showSummary,
|
||||
isShown,
|
||||
jumpToKey,
|
||||
allowTryItOut,
|
||||
response,
|
||||
request,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
specSelectors,
|
||||
specActions,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
authActions,
|
||||
authSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
fn
|
||||
} = this.props
|
||||
|
||||
const Operation = getComponent( "operation" )
|
||||
|
||||
const operationProps = fromJS({
|
||||
op,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
operationId,
|
||||
showSummary,
|
||||
isShown,
|
||||
jumpToKey,
|
||||
allowTryItOut,
|
||||
request,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
executeInProgress: this.state.executeInProgress,
|
||||
tryItOutEnabled: this.state.tryItOutEnabled
|
||||
})
|
||||
|
||||
return (
|
||||
<Operation
|
||||
operation={operationProps}
|
||||
response={response}
|
||||
request={request}
|
||||
isShown={isShown}
|
||||
|
||||
toggleShown={this.toggleShown}
|
||||
onTryoutClick={this.onTryoutClick}
|
||||
onCancelClick={this.onCancelClick}
|
||||
onExecute={this.onExecute}
|
||||
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
oas3Actions={oas3Actions}
|
||||
oas3Selectors={oas3Selectors}
|
||||
layoutActions={ layoutActions }
|
||||
layoutSelectors={ layoutSelectors }
|
||||
authActions={ authActions }
|
||||
authSelectors={ authSelectors }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
fn={fn}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,8 +7,7 @@ import * as AllPlugins from "core/plugins/all"
|
||||
import { parseSearch } from "core/utils"
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
const Perf = require("react-addons-perf")
|
||||
window.Perf = Perf
|
||||
window.Perf = require("react-addons-perf")
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
@@ -47,6 +46,7 @@ module.exports = function SwaggerUI(opts) {
|
||||
showMutatedRequest: true,
|
||||
defaultModelRendering: "example",
|
||||
defaultModelExpandDepth: 1,
|
||||
showExtensions: false,
|
||||
|
||||
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
|
||||
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
|
||||
@@ -58,13 +58,12 @@ module.exports = function SwaggerUI(opts) {
|
||||
plugins: [
|
||||
],
|
||||
|
||||
// Initial state
|
||||
initialState: { },
|
||||
|
||||
// Inline Plugin
|
||||
fn: { },
|
||||
components: { },
|
||||
state: { },
|
||||
|
||||
// Override some core configs... at your own risk
|
||||
store: { },
|
||||
}
|
||||
|
||||
let queryConfig = parseSearch()
|
||||
@@ -74,12 +73,12 @@ module.exports = function SwaggerUI(opts) {
|
||||
|
||||
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
|
||||
|
||||
const storeConfigs = deepExtend({}, constructorConfig.store, {
|
||||
const storeConfigs = {
|
||||
system: {
|
||||
configs: constructorConfig.configs
|
||||
},
|
||||
plugins: constructorConfig.presets,
|
||||
state: {
|
||||
state: deepExtend({
|
||||
layout: {
|
||||
layout: constructorConfig.layout,
|
||||
filter: constructorConfig.filter
|
||||
@@ -88,8 +87,8 @@ module.exports = function SwaggerUI(opts) {
|
||||
spec: "",
|
||||
url: constructorConfig.url
|
||||
}
|
||||
}
|
||||
})
|
||||
}, constructorConfig.initialState)
|
||||
}
|
||||
|
||||
let inlinePlugin = ()=> {
|
||||
return {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class JsonSchemaForm extends Component {
|
||||
|
||||
let { type, format="" } = schema
|
||||
|
||||
let Comp = getComponent(`JsonSchema_${type}_${format}`) || getComponent(`JsonSchema_${type}`) || getComponent("JsonSchema_string")
|
||||
let Comp = (format ? getComponent(`JsonSchema_${type}_${format}`) : getComponent(`JsonSchema_${type}`)) || getComponent("JsonSchema_string")
|
||||
return <Comp { ...this.props } fn={fn} getComponent={getComponent} value={value} onChange={onChange} schema={schema}/>
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ export class JsonSchema_string extends Component {
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
allowedValues={ enumValue }
|
||||
value={ value }
|
||||
allowEmptyValue={ !required }
|
||||
@@ -67,10 +68,20 @@ export class JsonSchema_string extends Component {
|
||||
const isDisabled = schema["in"] === "formData" && !("FormData" in window)
|
||||
const Input = getComponent("Input")
|
||||
if (schema["type"] === "file") {
|
||||
return <Input type="file" className={ errors.length ? "invalid" : ""} onChange={ this.onChange } disabled={isDisabled}/>
|
||||
return (<Input type="file"
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
onChange={ this.onChange }
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
else {
|
||||
return <Input type={ schema.format === "password" ? "password" : "text" } className={ errors.length ? "invalid" : ""} value={value} placeholder={description} onChange={ this.onChange } disabled={isDisabled}/>
|
||||
return (<Input type={ schema.format === "password" ? "password" : "text" }
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={value}
|
||||
placeholder={description}
|
||||
onChange={ this.onChange }
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,11 +145,12 @@ export class JsonSchema_array extends PureComponent {
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
multiple={ true }
|
||||
value={ value }
|
||||
allowedValues={ enumValue }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
title={ errors.length ? errors : ""}
|
||||
multiple={ true }
|
||||
value={ value }
|
||||
allowedValues={ enumValue }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -175,9 +187,10 @@ export class JsonSchema_boolean extends Component {
|
||||
const Select = getComponent("Select")
|
||||
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={ String(value) }
|
||||
allowedValues={ fromJS(["true", "false"]) }
|
||||
allowEmptyValue={ true }
|
||||
allowedValues={ fromJS(schema.enum || ["true", "false"]) }
|
||||
allowEmptyValue={ !this.props.required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ export default function authorize ( { auth, authActions, errActions, configs, au
|
||||
case "implicit":
|
||||
query.push("response_type=token")
|
||||
break
|
||||
|
||||
case "clientCredentials":
|
||||
// OAS3
|
||||
authActions.authorizeApplication(auth)
|
||||
return
|
||||
|
||||
case "authorizationCode":
|
||||
// OAS3
|
||||
query.push("response_type=code")
|
||||
break
|
||||
}
|
||||
|
||||
if (typeof clientId === "string") {
|
||||
|
||||
@@ -73,7 +73,7 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
|
||||
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
|
||||
let form = {
|
||||
grant_type: "password",
|
||||
scope: encodeURIComponent(auth.scopes.join(scopeSeparator))
|
||||
scope: auth.scopes.join(scopeSeparator)
|
||||
}
|
||||
let query = {}
|
||||
let headers = {}
|
||||
@@ -139,7 +139,7 @@ export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl
|
||||
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers})
|
||||
}
|
||||
|
||||
export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, authSelectors } ) => {
|
||||
export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, authSelectors } ) => {
|
||||
let { body, query={}, headers={}, name, url, auth } = data
|
||||
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
|
||||
let fetchUrl = url
|
||||
@@ -158,7 +158,9 @@ export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, aut
|
||||
method: "post",
|
||||
headers: _headers,
|
||||
query: query,
|
||||
body: body
|
||||
body: body,
|
||||
requestInterceptor: getConfigs().requestInterceptor,
|
||||
responseInterceptor: getConfigs().responseInterceptor
|
||||
})
|
||||
.then(function (response) {
|
||||
let token = JSON.parse(response.data)
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
securities.entrySeq().forEach( ([ key, security ]) => {
|
||||
let type = security.getIn(["schema", "type"])
|
||||
|
||||
if ( type === "apiKey" ) {
|
||||
if ( type === "apiKey" || type === "http" ) {
|
||||
map = map.set(key, security)
|
||||
} else if ( type === "basic" ) {
|
||||
let username = security.getIn(["value", "username"])
|
||||
|
||||
@@ -10,8 +10,8 @@ export const shownDefinitions = createSelector(
|
||||
|
||||
export const definitionsToAuthorize = createSelector(
|
||||
state,
|
||||
() =>( { specSelectors } ) => {
|
||||
let definitions = specSelectors.securityDefinitions()
|
||||
() => ( { specSelectors } ) => {
|
||||
let definitions = specSelectors.securityDefinitions() || Map({})
|
||||
let list = List()
|
||||
|
||||
//todo refactor
|
||||
@@ -27,7 +27,8 @@ export const definitionsToAuthorize = createSelector(
|
||||
)
|
||||
|
||||
|
||||
export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors } ) => {
|
||||
export const getDefinitionsByNames = ( state, securities ) => ( { specSelectors } ) => {
|
||||
console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.")
|
||||
let securityDefinitions = specSelectors.securityDefinitions()
|
||||
let result = List()
|
||||
|
||||
@@ -58,13 +59,20 @@ export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors }
|
||||
return result
|
||||
}
|
||||
|
||||
export const definitionsForRequirements = (state, securities = List()) => ({ authSelectors }) => {
|
||||
const allDefinitions = authSelectors.definitionsToAuthorize() || List()
|
||||
return allDefinitions.filter((def) => {
|
||||
return securities.some(sec => sec.get(def.keySeq().first()))
|
||||
})
|
||||
}
|
||||
|
||||
export const authorized = createSelector(
|
||||
state,
|
||||
auth => auth.get("authorized") || Map()
|
||||
)
|
||||
|
||||
|
||||
export const isAuthorized = ( state, securities ) =>( { authSelectors } ) => {
|
||||
export const isAuthorized = ( state, securities ) => ( { authSelectors } ) => {
|
||||
let authorized = authSelectors.authorized()
|
||||
|
||||
if(!List.isList(securities)) {
|
||||
|
||||
@@ -7,13 +7,16 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
let { fn } = toolbox
|
||||
|
||||
const actions = {
|
||||
download: (url)=> ({ errActions, specSelectors, specActions }) => {
|
||||
download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => {
|
||||
let { fetch } = fn
|
||||
const config = getConfigs()
|
||||
url = url || specSelectors.url()
|
||||
specActions.updateLoadingStatus("loading")
|
||||
fetch({
|
||||
url,
|
||||
loadSpec: true,
|
||||
requestInterceptor: config.requestInterceptor || (a => a),
|
||||
responseInterceptor: config.responseInterceptor || (a => a),
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"Accept": "application/json,*/*"
|
||||
|
||||
@@ -3,6 +3,7 @@ import serializeError from "serialize-error"
|
||||
export const NEW_THROWN_ERR = "err_new_thrown_err"
|
||||
export const NEW_THROWN_ERR_BATCH = "err_new_thrown_err_batch"
|
||||
export const NEW_SPEC_ERR = "err_new_spec_err"
|
||||
export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
||||
export const NEW_AUTH_ERR = "err_new_auth_err"
|
||||
export const CLEAR = "err_clear"
|
||||
|
||||
@@ -27,6 +28,13 @@ export function newSpecErr(err) {
|
||||
}
|
||||
}
|
||||
|
||||
export function newSpecErrBatch(errArray) {
|
||||
return {
|
||||
type: NEW_SPEC_ERR_BATCH,
|
||||
payload: errArray
|
||||
}
|
||||
}
|
||||
|
||||
export function newAuthErr(err) {
|
||||
return {
|
||||
type: NEW_AUTH_ERR,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
NEW_THROWN_ERR,
|
||||
NEW_THROWN_ERR_BATCH,
|
||||
NEW_SPEC_ERR,
|
||||
NEW_SPEC_ERR_BATCH,
|
||||
NEW_AUTH_ERR,
|
||||
CLEAR
|
||||
} from "./actions"
|
||||
@@ -45,6 +46,15 @@ export default function(system) {
|
||||
.update("errors", errors => transformErrors(errors, system.getSystem()))
|
||||
},
|
||||
|
||||
[NEW_SPEC_ERR_BATCH]: (state, { payload }) => {
|
||||
payload = payload.map(err => {
|
||||
return fromJS(Object.assign(DEFAULT_ERROR_STRUCTURE, err, { type: "spec" }))
|
||||
})
|
||||
return state
|
||||
.update("errors", errors => (errors || List()).concat( fromJS( payload )) )
|
||||
.update("errors", errors => transformErrors(errors, system.getSystem()))
|
||||
},
|
||||
|
||||
[NEW_AUTH_ERR]: (state, { payload }) => {
|
||||
let error = fromJS(Object.assign({}, payload))
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
|
||||
export const UPDATE_RESPONSE_CONTENT_TYPE = "oas3_set_response_content_type"
|
||||
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
|
||||
|
||||
export function setSelectedServer (selectedServerUrl) {
|
||||
export function setSelectedServer (selectedServerUrl, namespace) {
|
||||
return {
|
||||
type: UPDATE_SELECTED_SERVER,
|
||||
payload: selectedServerUrl
|
||||
payload: {selectedServerUrl, namespace}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ export function setRequestContentType ({ value, pathMethod }) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setResponseContentType ({ value, pathMethod }) {
|
||||
export function setResponseContentType ({ value, path, method }) {
|
||||
return {
|
||||
type: UPDATE_RESPONSE_CONTENT_TYPE,
|
||||
payload: { value, pathMethod }
|
||||
payload: { value, path, method }
|
||||
}
|
||||
}
|
||||
|
||||
export function setServerVariableValue ({ server, key, val }) {
|
||||
export function setServerVariableValue ({ server, namespace, key, val }) {
|
||||
return {
|
||||
type: UPDATE_SERVER_VARIABLE_VALUE,
|
||||
payload: { server, key, val }
|
||||
payload: { server, namespace, key, val }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,60 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { List } from "immutable"
|
||||
import { List, Map, fromJS } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
const state = state => state
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (ori, system) => (...args) => {
|
||||
return (ori, system) => (state, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
return selector(system, ...args)
|
||||
} else {
|
||||
return ori(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
export const definitionsToAuthorize = onlyOAS3(createSelector(
|
||||
state,
|
||||
({specSelectors}) => specSelectors.securityDefinitions(),
|
||||
(system, definitions) => {
|
||||
// Coerce our OpenAPI 3.0 definitions into monoflow definitions
|
||||
// that look like Swagger2 definitions.
|
||||
let list = List()
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
definitions.entrySeq().forEach( ([ defName, definition ]) => {
|
||||
const type = definition.get("type")
|
||||
|
||||
// Hasta la vista, authentication!
|
||||
export const shownDefinitions = OAS3NullSelector
|
||||
export const definitionsToAuthorize = OAS3NullSelector
|
||||
export const getDefinitionsByNames = OAS3NullSelector
|
||||
export const authorized = onlyOAS3(() => List())
|
||||
export const isAuthorized = OAS3NullSelector
|
||||
export const getConfigs = OAS3NullSelector
|
||||
if(type === "oauth2") {
|
||||
definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => {
|
||||
let translatedDef = fromJS({
|
||||
flow: flowKey,
|
||||
authorizationUrl: flowVal.get("authorizationUrl"),
|
||||
tokenUrl: flowVal.get("tokenUrl"),
|
||||
scopes: flowVal.get("scopes"),
|
||||
type: definition.get("type")
|
||||
})
|
||||
|
||||
list = list.push(new Map({
|
||||
[defName]: translatedDef.filter((v) => {
|
||||
// filter out unset values, sometimes `authorizationUrl`
|
||||
// and `tokenUrl` come out as `undefined` in the data
|
||||
return v !== undefined
|
||||
})
|
||||
}))
|
||||
})
|
||||
}
|
||||
if(type === "http" || type === "apiKey") {
|
||||
list = list.push(new Map({
|
||||
[defName]: definition
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
))
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
const Callbacks = (props) => {
|
||||
let { callbacks, getComponent } = props
|
||||
// const Markdown = getComponent("Markdown")
|
||||
const Operation = getComponent("operation", true)
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
|
||||
if(!callbacks) {
|
||||
return <span>No callbacks</span>
|
||||
@@ -16,24 +18,22 @@ const Callbacks = (props) => {
|
||||
{ callback.map((pathItem, pathItemName) => {
|
||||
return <div key={pathItemName}>
|
||||
{ pathItem.map((operation, method) => {
|
||||
return <Operation
|
||||
operation={operation}
|
||||
let op = fromJS({
|
||||
operation
|
||||
})
|
||||
return <OperationContainer
|
||||
{...props}
|
||||
op={op}
|
||||
key={method}
|
||||
tag={""}
|
||||
method={method}
|
||||
isShownKey={["callbacks", operation.get("id"), callbackName]}
|
||||
path={pathItemName}
|
||||
allowTryItOut={false}
|
||||
{...props}></Operation>
|
||||
// return <pre>{JSON.stringify(operation)}</pre>
|
||||
/>
|
||||
}) }
|
||||
</div>
|
||||
}) }
|
||||
</div>
|
||||
// return <div>
|
||||
// <h2>{name}</h2>
|
||||
// {callback.description && <Markdown source={callback.description}/>}
|
||||
// <pre>{JSON.stringify(callback)}</pre>
|
||||
// </div>
|
||||
})
|
||||
return <div>
|
||||
{callbackElements}
|
||||
@@ -42,7 +42,7 @@ const Callbacks = (props) => {
|
||||
|
||||
Callbacks.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
callbacks: PropTypes.array.isRequired
|
||||
callbacks: ImPropTypes.iterable.isRequired
|
||||
|
||||
}
|
||||
|
||||
|
||||
131
src/core/plugins/oas3/components/http-auth.jsx
Normal file
131
src/core/plugins/oas3/components/http-auth.jsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class HttpAuth extends React.Component {
|
||||
static propTypes = {
|
||||
authorized: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
let { name, schema } = this.props
|
||||
let value = this.getValue()
|
||||
|
||||
this.state = {
|
||||
name: name,
|
||||
schema: schema,
|
||||
value: value
|
||||
}
|
||||
}
|
||||
|
||||
getValue () {
|
||||
let { name, authorized } = this.props
|
||||
|
||||
return authorized && authorized.getIn([name, "value"])
|
||||
}
|
||||
|
||||
onChange =(e) => {
|
||||
let { onChange } = this.props
|
||||
let { value, name } = e.target
|
||||
|
||||
let newValue = this.state.value || {}
|
||||
if(name) {
|
||||
newValue[name] = value
|
||||
} else {
|
||||
newValue = value
|
||||
}
|
||||
|
||||
this.setState({ value: newValue }, () => onChange(this.state))
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
let { schema, getComponent, errSelectors, name } = this.props
|
||||
const Input = getComponent("Input")
|
||||
const Row = getComponent("Row")
|
||||
const Col = getComponent("Col")
|
||||
const AuthError = getComponent("authError")
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
const JumpToPath = getComponent("JumpToPath", true)
|
||||
|
||||
const scheme = schema.get("scheme")
|
||||
let value = this.getValue()
|
||||
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
||||
|
||||
if(scheme === "basic") {
|
||||
let username = value ? value.get("username") : null
|
||||
return <div>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(http, Basic)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ username && <h6>Authorized</h6> }
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Username:</label>
|
||||
{
|
||||
username ? <code> { username } </code>
|
||||
: <Col><Input type="text" required="required" name="username" onChange={ this.onChange }/></Col>
|
||||
}
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Password:</label>
|
||||
{
|
||||
username ? <code> ****** </code>
|
||||
: <Col><Input required="required"
|
||||
autoComplete="new-password"
|
||||
name="password"
|
||||
type="password"
|
||||
onChange={ this.onChange }/></Col>
|
||||
}
|
||||
</Row>
|
||||
{
|
||||
errors.valueSeq().map( (error, key) => {
|
||||
return <AuthError error={ error }
|
||||
key={ key }/>
|
||||
} )
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
if(scheme === "bearer") {
|
||||
return (
|
||||
<div>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(http, Bearer)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ value && <h6>Authorized</h6>}
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Value:</label>
|
||||
{
|
||||
value ? <code> ****** </code>
|
||||
: <Col><Input type="text" onChange={ this.onChange }/></Col>
|
||||
}
|
||||
</Row>
|
||||
{
|
||||
errors.valueSeq().map( (error, key) => {
|
||||
return <AuthError error={ error }
|
||||
key={ key }/>
|
||||
} )
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <div>
|
||||
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,15 @@ import RequestBody from "./request-body"
|
||||
import OperationLink from "./operation-link.jsx"
|
||||
import Servers from "./servers"
|
||||
import RequestBodyEditor from "./request-body-editor"
|
||||
import HttpAuth from "./http-auth"
|
||||
import OperationServers from "./operation-servers"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
HttpAuth,
|
||||
RequestBody,
|
||||
Servers,
|
||||
RequestBodyEditor,
|
||||
OperationServers,
|
||||
operationLink: OperationLink
|
||||
}
|
||||
|
||||
102
src/core/plugins/oas3/components/operation-servers.jsx
Normal file
102
src/core/plugins/oas3/components/operation-servers.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class OperationServers extends React.Component {
|
||||
static propTypes = {
|
||||
// for self
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operationServers: ImPropTypes.list,
|
||||
pathServers: ImPropTypes.list,
|
||||
setSelectedServer: PropTypes.func.isRequired,
|
||||
setServerVariableValue: PropTypes.func.isRequired,
|
||||
getSelectedServer: PropTypes.func.isRequired,
|
||||
getServerVariable: PropTypes.func.isRequired,
|
||||
getEffectiveServerValue: PropTypes.func.isRequired,
|
||||
|
||||
// utils
|
||||
getComponent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
setSelectedServer = (server) => {
|
||||
const { path, method } = this.props
|
||||
// FIXME: we should be keeping up with this in props/state upstream of us
|
||||
// instead of cheating™ with `forceUpdate`
|
||||
this.forceUpdate()
|
||||
return this.props.setSelectedServer(server, `${path}:${method}`)
|
||||
}
|
||||
|
||||
setServerVariableValue = (obj) => {
|
||||
const { path, method } = this.props
|
||||
// FIXME: we should be keeping up with this in props/state upstream of us
|
||||
// instead of cheating™ with `forceUpdate`
|
||||
this.forceUpdate()
|
||||
return this.props.setServerVariableValue({
|
||||
...obj,
|
||||
namespace: `${path}:${method}`
|
||||
})
|
||||
}
|
||||
|
||||
getSelectedServer = () => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getSelectedServer(`${path}:${method}`)
|
||||
}
|
||||
|
||||
getServerVariable = (server, key) => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getServerVariable({
|
||||
namespace: `${path}:${method}`,
|
||||
server
|
||||
}, key)
|
||||
}
|
||||
|
||||
getEffectiveServerValue = (server) => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getEffectiveServerValue({
|
||||
server,
|
||||
namespace: `${path}:${method}`
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
// for self
|
||||
operationServers,
|
||||
pathServers,
|
||||
|
||||
// util
|
||||
getComponent
|
||||
} = this.props
|
||||
|
||||
if(!operationServers && !pathServers) {
|
||||
return null
|
||||
}
|
||||
|
||||
const Servers = getComponent("Servers")
|
||||
|
||||
const serversToDisplay = operationServers || pathServers
|
||||
const displaying = operationServers ? "operation" : "path"
|
||||
|
||||
return <div className="opblock-section operation-servers">
|
||||
<div className="opblock-section-header">
|
||||
<div className="tab-header">
|
||||
<h4 className="opblock-title">Servers</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className="opblock-description-wrapper">
|
||||
<h4 className="message">
|
||||
These {displaying}-level options override the global server options.
|
||||
</h4>
|
||||
<Servers
|
||||
servers={serversToDisplay}
|
||||
currentServer={this.getSelectedServer()}
|
||||
setSelectedServer={this.setSelectedServer}
|
||||
setServerVariableValue={this.setServerVariableValue}
|
||||
getServerVariable={this.getServerVariable}
|
||||
getEffectiveServerValue={this.getEffectiveServerValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,13 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if(this.props.requestBody !== prevProps.requestBody) {
|
||||
// force recalc of value if the request body definition has changed
|
||||
this.setValueToSample(this.props.mediaType)
|
||||
}
|
||||
}
|
||||
|
||||
setValueToSample = (explicitMediaType) => {
|
||||
this.onChange(this.sample(explicitMediaType))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ const RequestBody = ({
|
||||
|
||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||
|
||||
if(!mediaTypeValue) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div>
|
||||
{ requestBodyDescription &&
|
||||
<Markdown source={requestBodyDescription} />
|
||||
@@ -50,7 +54,7 @@ RequestBody.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
contentType: PropTypes.string.isRequired,
|
||||
contentType: PropTypes.string,
|
||||
isExecute: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ export default class Servers extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let { servers } = this.props
|
||||
let { servers, currentServer } = this.props
|
||||
|
||||
if(currentServer) {
|
||||
return
|
||||
}
|
||||
|
||||
//fire 'change' event to set default 'value' of select
|
||||
this.setServer(servers.first().get("url"))
|
||||
@@ -93,9 +97,8 @@ export default class Servers extends React.Component {
|
||||
let shouldShowVariableUI = currentServerVariableDefs.size !== 0
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="servers">
|
||||
<label htmlFor="servers">
|
||||
<span className="servers-title">Servers</span>
|
||||
<select onChange={ this.onServerChange }>
|
||||
{ servers.valueSeq().map(
|
||||
( server ) =>
|
||||
@@ -109,13 +112,14 @@ export default class Servers extends React.Component {
|
||||
</label>
|
||||
{ shouldShowVariableUI ?
|
||||
<div>
|
||||
<h4>Server variables</h4>
|
||||
|
||||
<div className={"computed-url"}>
|
||||
Computed URL:
|
||||
<code>
|
||||
{getEffectiveServerValue(currentServer)}
|
||||
</code>
|
||||
</div>
|
||||
<h4>Server variables</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
{
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
} from "./actions"
|
||||
|
||||
export default {
|
||||
[UPDATE_SELECTED_SERVER]: (state, { payload: selectedServerUrl } ) =>{
|
||||
return state.setIn( [ "selectedServer" ], selectedServerUrl)
|
||||
[UPDATE_SELECTED_SERVER]: (state, { payload: { selectedServerUrl, namespace } } ) =>{
|
||||
const path = namespace ? [ namespace, "selectedServer"] : [ "selectedServer"]
|
||||
return state.setIn( path, selectedServerUrl)
|
||||
},
|
||||
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
@@ -18,11 +19,11 @@ export default {
|
||||
let [path, method] = pathMethod
|
||||
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
|
||||
},
|
||||
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, path, method } } ) =>{
|
||||
return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
|
||||
},
|
||||
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
|
||||
return state.setIn( [ "serverVariableValues", server, key ], val)
|
||||
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, namespace, key, val } } ) =>{
|
||||
const path = namespace ? [ namespace, "serverVariableValues", server, key ] : [ "serverVariableValues", server, key ]
|
||||
return state.setIn(path, val)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ function onlyOAS3(selector) {
|
||||
}
|
||||
}
|
||||
|
||||
export const selectedServer = onlyOAS3(state => {
|
||||
return state.getIn(["selectedServer"]) || ""
|
||||
export const selectedServer = onlyOAS3((state, namespace) => {
|
||||
const path = namespace ? [namespace, "selectedServer"] : ["selectedServer"]
|
||||
return state.getIn(path) || ""
|
||||
}
|
||||
)
|
||||
|
||||
@@ -35,19 +36,68 @@ export const responseContentType = onlyOAS3((state, path, method) => {
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariableValue = onlyOAS3((state, server, key) => {
|
||||
return state.getIn(["serverVariableValues", server, key]) || null
|
||||
export const serverVariableValue = onlyOAS3((state, locationData, key) => {
|
||||
let path
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
if(namespace) {
|
||||
path = [namespace, "serverVariableValues", server, key]
|
||||
} else {
|
||||
path = ["serverVariableValues", server, key]
|
||||
}
|
||||
} else {
|
||||
const server = locationData
|
||||
path = ["serverVariableValues", server, key]
|
||||
}
|
||||
|
||||
return state.getIn(path) || null
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariables = onlyOAS3((state, server) => {
|
||||
return state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
export const serverVariables = onlyOAS3((state, locationData) => {
|
||||
let path
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
if(namespace) {
|
||||
path = [namespace, "serverVariableValues", server]
|
||||
} else {
|
||||
path = ["serverVariableValues", server]
|
||||
}
|
||||
} else {
|
||||
const server = locationData
|
||||
path = ["serverVariableValues", server]
|
||||
}
|
||||
|
||||
return state.getIn(path) || OrderedMap()
|
||||
}
|
||||
)
|
||||
|
||||
export const serverEffectiveValue = onlyOAS3((state, server) => {
|
||||
let varValues = state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
let str = server
|
||||
export const serverEffectiveValue = onlyOAS3((state, locationData) => {
|
||||
var varValues, serverValue
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
serverValue = server
|
||||
if(namespace) {
|
||||
varValues = state.getIn([namespace, "serverVariableValues", serverValue])
|
||||
} else {
|
||||
varValues = state.getIn(["serverVariableValues", serverValue])
|
||||
}
|
||||
} else {
|
||||
serverValue = locationData
|
||||
varValues = state.getIn(["serverVariableValues", serverValue])
|
||||
}
|
||||
|
||||
varValues = varValues || OrderedMap()
|
||||
let str = serverValue
|
||||
|
||||
varValues.map((val, key) => {
|
||||
str = str.replace(new RegExp(`{${key}}`, "g"), val)
|
||||
|
||||
@@ -48,12 +48,20 @@ export const definitions = onlyOAS3(createSelector(
|
||||
spec => spec.getIn(["components", "schemas"]) || Map()
|
||||
))
|
||||
|
||||
export const hasHost = onlyOAS3((state) => {
|
||||
return spec(state).hasIn(["servers", 0])
|
||||
})
|
||||
|
||||
export const securityDefinitions = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["components", "securitySchemes"]) || null
|
||||
))
|
||||
|
||||
export const host = OAS3NullSelector
|
||||
export const basePath = OAS3NullSelector
|
||||
export const consumes = OAS3NullSelector
|
||||
export const produces = OAS3NullSelector
|
||||
export const schemes = OAS3NullSelector
|
||||
export const securityDefinitions = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
|
||||
23
src/core/plugins/oas3/wrap-components/auth-item.jsx
Normal file
23
src/core/plugins/oas3/wrap-components/auth-item.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from "react"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
|
||||
const {
|
||||
schema, getComponent, errSelectors, authorized, onAuthChange, name
|
||||
} = props
|
||||
|
||||
const HttpAuth = getComponent("HttpAuth")
|
||||
const type = schema.get("type")
|
||||
|
||||
if(type === "http") {
|
||||
return <HttpAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange }/>
|
||||
} else {
|
||||
return <Ori {...props} />
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import Markdown from "./markdown"
|
||||
import AuthItem from "./auth-item"
|
||||
import parameters from "./parameters"
|
||||
import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
@@ -6,6 +7,7 @@ import Model from "./model"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
AuthItem,
|
||||
parameters,
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ReactMarkdown from "react-markdown"
|
||||
import { Parser, HtmlRenderer } from "commonmark"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
import { sanitizer } from "core/components/providers/markdown"
|
||||
|
||||
export default OAS3ComponentWrapFactory(({ source }) => {
|
||||
export const Markdown = ({ source }) => {
|
||||
if ( source ) {
|
||||
const parser = new Parser()
|
||||
const writer = new HtmlRenderer()
|
||||
@@ -23,4 +24,9 @@ export default OAS3ComponentWrapFactory(({ source }) => {
|
||||
)
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
||||
Markdown.propTypes = {
|
||||
source: PropTypes.string
|
||||
}
|
||||
|
||||
export default OAS3ComponentWrapFactory(Markdown)
|
||||
@@ -8,12 +8,13 @@ class ModelComponent extends Component {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema } = this.props
|
||||
let { getConfigs, schema } = this.props
|
||||
let classes = ["model-box"]
|
||||
let isDeprecated = schema.get("deprecated") === true
|
||||
let message = null
|
||||
@@ -26,6 +27,7 @@ class ModelComponent extends Component {
|
||||
return <div className={classes.join(" ")}>
|
||||
{message}
|
||||
<Model { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
depth={ 1 }
|
||||
expandDepth={ this.props.expandDepth || 0 }
|
||||
/>
|
||||
|
||||
@@ -22,6 +22,7 @@ class Parameters extends Component {
|
||||
specActions: PropTypes.object.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
@@ -86,6 +87,7 @@ class Parameters extends Component {
|
||||
|
||||
fn,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
specSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
@@ -137,6 +139,7 @@ class Parameters extends Component {
|
||||
eachMap(parameters, (parameter) => (
|
||||
<ParameterRow fn={ fn }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
param={ parameter }
|
||||
key={ parameter.get( "name" ) }
|
||||
onChange={ this.onChange }
|
||||
|
||||
@@ -80,7 +80,12 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
|
||||
}
|
||||
|
||||
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
|
||||
const { modelPropertyMacro, parameterMacro } = getConfigs()
|
||||
const {
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
} = getConfigs()
|
||||
|
||||
if(typeof(json) === "undefined") {
|
||||
json = specSelectors.specJson()
|
||||
@@ -93,8 +98,15 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
|
||||
|
||||
let specStr = specSelectors.specStr()
|
||||
|
||||
return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro })
|
||||
.then( ({spec, errors}) => {
|
||||
return resolve({
|
||||
fetch,
|
||||
spec: json,
|
||||
baseDoc: url,
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
}).then( ({spec, errors}) => {
|
||||
errActions.clear({
|
||||
type: "thrown"
|
||||
})
|
||||
@@ -137,10 +149,13 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||
}
|
||||
}
|
||||
|
||||
export function validateParams( payload ){
|
||||
export const validateParams = ( payload, isOAS3 ) =>{
|
||||
return {
|
||||
type: VALIDATE_PARAMS,
|
||||
payload:{ pathMethod: payload }
|
||||
payload:{
|
||||
pathMethod: payload,
|
||||
isOAS3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +228,18 @@ export const executeRequest = (req) =>
|
||||
}
|
||||
|
||||
if(specSelectors.isOAS3()) {
|
||||
// OAS3 request feature support
|
||||
req.server = oas3Selectors.selectedServer()
|
||||
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
|
||||
const namespace = `${pathName}:${method}`
|
||||
|
||||
req.server = oas3Selectors.selectedServer(namespace) || oas3Selectors.selectedServer()
|
||||
|
||||
const namespaceVariables = oas3Selectors.serverVariables({
|
||||
server: req.server,
|
||||
namespace
|
||||
}).toJS()
|
||||
const globalVariables = oas3Selectors.serverVariables({ server: req.server }).toJS()
|
||||
|
||||
req.serverVariables = Object.keys(namespaceVariables).length ? namespaceVariables : globalVariables
|
||||
|
||||
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
|
||||
req.responseContentType = oas3Selectors.responseContentType(pathName, method) || "*/*"
|
||||
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
|
||||
|
||||
@@ -51,14 +51,14 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
|
||||
let isXml = /xml/i.test(operation.get("consumes_value"))
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
let errors = validateParam(parameters.get(i), isXml)
|
||||
let errors = validateParam(parameters.get(i), isXml, isOAS3)
|
||||
parameters.setIn([i, "errors"], fromJS(errors))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,3 +13,7 @@ export const executeRequest = (ori, { specActions }) => (req) => {
|
||||
specActions.logRequest(req)
|
||||
return ori(req)
|
||||
}
|
||||
|
||||
export const validateParams = (ori, { specSelectors }) => (req) => {
|
||||
return ori(req, specSelectors.isOAS3())
|
||||
}
|
||||
@@ -20,8 +20,14 @@ const RootWrapper = (reduxStore, ComponentToWrap) => class extends Component {
|
||||
}
|
||||
|
||||
const makeContainer = (getSystem, component, reduxStore) => {
|
||||
const mapStateToProps = function(state, ownProps) {
|
||||
const propsForContainerComponent = Object.assign({}, ownProps, getSystem())
|
||||
const ori = component.prototype.mapStateToProps || (state => { return {state} })
|
||||
return ori(state, propsForContainerComponent)
|
||||
}
|
||||
|
||||
let wrappedWithSystem = SystemWrapper(getSystem, component, reduxStore)
|
||||
let connected = connect(state => ({state}))(wrappedWithSystem)
|
||||
let connected = connect( mapStateToProps )(wrappedWithSystem)
|
||||
if(reduxStore)
|
||||
return RootWrapper(reduxStore, connected)
|
||||
return connected
|
||||
@@ -114,5 +120,5 @@ export const getComponent = (getSystem, getStore, getComponents, componentName,
|
||||
return makeContainer(getSystem, component, getStore())
|
||||
|
||||
// container == truthy
|
||||
return makeContainer(getSystem, component)
|
||||
return makeContainer(getSystem, wrapRender(component))
|
||||
}
|
||||
|
||||
@@ -10,13 +10,17 @@ import auth from "core/plugins/auth"
|
||||
import util from "core/plugins/util"
|
||||
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
||||
import downloadUrlPlugin from "core/plugins/download-url"
|
||||
import configsPlugin from "plugins/configs"
|
||||
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||
|
||||
import OperationContainer from "core/containers/OperationContainer"
|
||||
|
||||
import App from "core/components/app"
|
||||
import AuthorizationPopup from "core/components/auth/authorization-popup"
|
||||
import AuthorizeBtn from "core/components/auth/authorize-btn"
|
||||
import AuthorizeOperationBtn from "core/components/auth/authorize-operation-btn"
|
||||
import Auths from "core/components/auth/auths"
|
||||
import AuthItem from "core/components/auth/auth-item"
|
||||
import AuthError from "core/components/auth/error"
|
||||
import ApiKeyAuth from "core/components/auth/api-key-auth"
|
||||
import BasicAuth from "core/components/auth/basic-auth"
|
||||
@@ -26,11 +30,14 @@ import LiveResponse from "core/components/live-response"
|
||||
import OnlineValidatorBadge from "core/components/online-validator-badge"
|
||||
import Operations from "core/components/operations"
|
||||
import Operation from "core/components/operation"
|
||||
import OperationExt from "core/components/operation-extensions"
|
||||
import OperationExtRow from "core/components/operation-extension-row"
|
||||
import HighlightCode from "core/components/highlight-code"
|
||||
import Responses from "core/components/responses"
|
||||
import Response from "core/components/response"
|
||||
import ResponseBody from "core/components/response-body"
|
||||
import Parameters from "core/components/parameters"
|
||||
import ParameterExt from "core/components/parameter-extension"
|
||||
import ParameterRow from "core/components/parameter-row"
|
||||
import Execute from "core/components/execute"
|
||||
import Headers from "core/components/headers"
|
||||
@@ -51,6 +58,7 @@ import EnumModel from "core/components/enum-model"
|
||||
import ObjectModel from "core/components/object-model"
|
||||
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 VersionStamp from "core/components/version-stamp"
|
||||
|
||||
@@ -70,6 +78,7 @@ export default function() {
|
||||
authorizeBtn: AuthorizeBtn,
|
||||
authorizeOperationBtn: AuthorizeOperationBtn,
|
||||
auths: Auths,
|
||||
AuthItem: AuthItem,
|
||||
authError: AuthError,
|
||||
oauth2: Oauth2,
|
||||
apiKeyAuth: ApiKeyAuth,
|
||||
@@ -104,10 +113,15 @@ export default function() {
|
||||
ObjectModel,
|
||||
ArrayModel,
|
||||
PrimitiveModel,
|
||||
Property,
|
||||
TryItOutButton,
|
||||
Markdown,
|
||||
BaseLayout,
|
||||
VersionStamp
|
||||
VersionStamp,
|
||||
OperationExt,
|
||||
OperationExtRow,
|
||||
ParameterExt,
|
||||
OperationContainer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +134,7 @@ export default function() {
|
||||
}
|
||||
|
||||
return [
|
||||
configsPlugin,
|
||||
util,
|
||||
logs,
|
||||
view,
|
||||
|
||||
@@ -294,8 +294,7 @@ export default class Store {
|
||||
|
||||
getMapStateToProps() {
|
||||
return () => {
|
||||
let obj = Object.assign({}, this.getSystem())
|
||||
return obj
|
||||
return Object.assign({}, this.getSystem())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Im from "immutable"
|
||||
|
||||
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url"
|
||||
import camelCase from "lodash/camelCase"
|
||||
import upperFirst from "lodash/upperFirst"
|
||||
import _memoize from "lodash/memoize"
|
||||
@@ -155,83 +155,6 @@ export function getList(iterable, keys) {
|
||||
return Im.List.isList(val) ? val : Im.List()
|
||||
}
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/2893259/454004
|
||||
// Note: directly ported from CoffeeScript
|
||||
export function formatXml (xml) {
|
||||
var contexp, fn, formatted, indent, l, lastType, len, lines, ln, reg, transitions, wsexp
|
||||
reg = /(>)(<)(\/*)/g
|
||||
wsexp = /[ ]*(.*)[ ]+\n/g
|
||||
contexp = /(<.+>)(.+\n)/g
|
||||
xml = xml.replace(/\r\n/g, "\n").replace(reg, "$1\n$2$3").replace(wsexp, "$1\n").replace(contexp, "$1\n$2")
|
||||
formatted = ""
|
||||
lines = xml.split("\n")
|
||||
indent = 0
|
||||
lastType = "other"
|
||||
transitions = {
|
||||
"single->single": 0,
|
||||
"single->closing": -1,
|
||||
"single->opening": 0,
|
||||
"single->other": 0,
|
||||
"closing->single": 0,
|
||||
"closing->closing": -1,
|
||||
"closing->opening": 0,
|
||||
"closing->other": 0,
|
||||
"opening->single": 1,
|
||||
"opening->closing": 0,
|
||||
"opening->opening": 1,
|
||||
"opening->other": 1,
|
||||
"other->single": 0,
|
||||
"other->closing": -1,
|
||||
"other->opening": 0,
|
||||
"other->other": 0
|
||||
}
|
||||
fn = function(ln) {
|
||||
var fromTo, key, padding, type, types, value
|
||||
types = {
|
||||
single: Boolean(ln.match(/<.+\/>/)),
|
||||
closing: Boolean(ln.match(/<\/.+>/)),
|
||||
opening: Boolean(ln.match(/<[^!?].*>/))
|
||||
}
|
||||
type = ((function() {
|
||||
var results
|
||||
results = []
|
||||
for (key in types) {
|
||||
value = types[key]
|
||||
if (value) {
|
||||
results.push(key)
|
||||
}
|
||||
}
|
||||
return results
|
||||
})())[0]
|
||||
type = type === void 0 ? "other" : type
|
||||
fromTo = lastType + "->" + type
|
||||
lastType = type
|
||||
padding = ""
|
||||
indent += transitions[fromTo]
|
||||
padding = ((function() {
|
||||
/* eslint-disable no-unused-vars */
|
||||
var m, ref1, results, j
|
||||
results = []
|
||||
for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) {
|
||||
results.push(" ")
|
||||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
return results
|
||||
})()).join("")
|
||||
if (fromTo === "opening->closing") {
|
||||
formatted = formatted.substr(0, formatted.length - 1) + ln + "\n"
|
||||
} else {
|
||||
formatted += padding + ln + "\n"
|
||||
}
|
||||
}
|
||||
for (l = 0, len = lines.length; l < len; l++) {
|
||||
ln = lines[l]
|
||||
fn(ln)
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapted from http://github.com/asvd/microlight
|
||||
* @copyright 2016 asvd <heliosframework@gmail.com>
|
||||
@@ -536,17 +459,31 @@ export const validateMinLength = (val, min) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const validatePattern = (val, rxPattern) => {
|
||||
var patt = new RegExp(rxPattern)
|
||||
if (!patt.test(val)) {
|
||||
return "Value must follow pattern " + rxPattern
|
||||
}
|
||||
}
|
||||
|
||||
// validation of parameters before execute
|
||||
export const validateParam = (param, isXml) => {
|
||||
export const validateParam = (param, isXml, isOAS3 = false) => {
|
||||
let errors = []
|
||||
let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value")
|
||||
let required = param.get("required")
|
||||
let maximum = param.get("maximum")
|
||||
let minimum = param.get("minimum")
|
||||
let type = param.get("type")
|
||||
let format = param.get("format")
|
||||
let maxLength = param.get("maxLength")
|
||||
let minLength = param.get("minLength")
|
||||
|
||||
let paramDetails = isOAS3 ? param.get("schema") : param
|
||||
|
||||
if(!paramDetails) return errors
|
||||
|
||||
let maximum = paramDetails.get("maximum")
|
||||
let minimum = paramDetails.get("minimum")
|
||||
let type = paramDetails.get("type")
|
||||
let format = paramDetails.get("format")
|
||||
let maxLength = paramDetails.get("maxLength")
|
||||
let minLength = paramDetails.get("minLength")
|
||||
let pattern = paramDetails.get("pattern")
|
||||
|
||||
|
||||
/*
|
||||
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
|
||||
@@ -554,14 +491,24 @@ export const validateParam = (param, isXml) => {
|
||||
Only bother validating the parameter if the type was specified.
|
||||
*/
|
||||
if ( type && (required || value) ) {
|
||||
// These checks should evaluate to true if the parameter's value is valid
|
||||
let stringCheck = type === "string" && value && !validateString(value)
|
||||
// These checks should evaluate to true if there is a parameter
|
||||
let stringCheck = type === "string" && value
|
||||
let arrayCheck = type === "array" && Array.isArray(value) && value.length
|
||||
let listCheck = type === "array" && Im.List.isList(value) && value.count()
|
||||
let fileCheck = type === "file" && value instanceof win.File
|
||||
let booleanCheck = type === "boolean" && !validateBoolean(value)
|
||||
let numberCheck = type === "number" && !validateNumber(value) // validateNumber returns undefined if the value is a number
|
||||
let integerCheck = type === "integer" && !validateInteger(value) // validateInteger returns undefined if the value is an integer
|
||||
let booleanCheck = type === "boolean" && (value || value === false)
|
||||
let numberCheck = type === "number" && (value || value === 0)
|
||||
let integerCheck = type === "integer" && (value || value === 0)
|
||||
|
||||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
|
||||
if (pattern) {
|
||||
let err = validatePattern(value, pattern)
|
||||
if (err) errors.push(err)
|
||||
}
|
||||
|
||||
if (maxLength || maxLength === 0) {
|
||||
let err = validateMaxLength(value, maxLength)
|
||||
@@ -573,11 +520,6 @@ export const validateParam = (param, isXml) => {
|
||||
if (err) errors.push(err)
|
||||
}
|
||||
|
||||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
|
||||
if (maximum || maximum === 0) {
|
||||
let err = validateMaximum(value, maximum)
|
||||
if (err) errors.push(err)
|
||||
@@ -616,7 +558,7 @@ export const validateParam = (param, isXml) => {
|
||||
|
||||
if ( !value.count() ) { return errors }
|
||||
|
||||
itemType = param.getIn(["items", "type"])
|
||||
itemType = paramDetails.getIn(["items", "type"])
|
||||
|
||||
value.forEach((item, index) => {
|
||||
let err
|
||||
@@ -720,6 +662,14 @@ export const shallowEqualKeys = (a,b, keys) => {
|
||||
})
|
||||
}
|
||||
|
||||
export function sanitizeUrl(url) {
|
||||
if(typeof url !== "string" || url === "") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return braintreeSanitizeUrl(url)
|
||||
}
|
||||
|
||||
export function getAcceptControllingResponse(responses) {
|
||||
if(!Im.OrderedMap.isOrderedMap(responses)) {
|
||||
// wrong type!
|
||||
@@ -745,3 +695,5 @@ export function getAcceptControllingResponse(responses) {
|
||||
|
||||
export const createDeepLinkPath = (str) => typeof str == "string" || str instanceof String ? str.trim().replace(/\s/g, "_") : ""
|
||||
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
|
||||
|
||||
export const getExtensions = (defObj) => defObj.filter((v, k) => /^x-/.test(k))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Add a plugin
|
||||
|
||||
### Swagger-UX relies on plugins for all the good stuff.
|
||||
### Swagger-UI relies on plugins for all the good stuff.
|
||||
|
||||
Plugins allow you to add
|
||||
- `statePlugins`
|
||||
|
||||
20
src/plugins/configs/actions.js
Normal file
20
src/plugins/configs/actions.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export const UPDATE_CONFIGS = "configs_update"
|
||||
export const TOGGLE_CONFIGS = "configs_toggle"
|
||||
|
||||
// Update the configs, with a merge ( not deep )
|
||||
export function update(configName, configValue) {
|
||||
return {
|
||||
type: UPDATE_CONFIGS,
|
||||
payload: {
|
||||
[configName]: configValue
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle's the config, by name
|
||||
export function toggle(configName) {
|
||||
return {
|
||||
type: TOGGLE_CONFIGS,
|
||||
payload: configName,
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,66 @@
|
||||
import YAML from "js-yaml"
|
||||
import yamlConfig from "../../../swagger-config.yaml"
|
||||
import * as actions from "./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 {}
|
||||
try {
|
||||
return YAML.safeLoad(yaml)
|
||||
} catch(e) {
|
||||
if (system) {
|
||||
system.errActions.newThrownErr( new Error(e) )
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default function configPlugin (toolbox) {
|
||||
let { fn } = toolbox
|
||||
|
||||
const actions = {
|
||||
downloadConfig: (url) => () => {
|
||||
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 specActions = {
|
||||
downloadConfig: (url) => ({fn}) => {
|
||||
let {fetch} = fn
|
||||
return fetch(url)
|
||||
},
|
||||
|
||||
getConfigByUrl: (configUrl, cb)=> ({ specActions }) => {
|
||||
if (configUrl) {
|
||||
return specActions.downloadConfig(configUrl).then(next, next)
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
getLocalConfig: () => {
|
||||
return parseYamlConfig(yamlConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: { actions, selectors }
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default function configsPlugin() {
|
||||
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
actions: specActions,
|
||||
selectors: specSelectors,
|
||||
},
|
||||
configs: {
|
||||
reducers,
|
||||
actions,
|
||||
selectors,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
src/plugins/configs/reducers.js
Normal file
20
src/plugins/configs/reducers.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
import {
|
||||
UPDATE_CONFIGS,
|
||||
TOGGLE_CONFIGS,
|
||||
} from "./actions"
|
||||
|
||||
export default {
|
||||
|
||||
[UPDATE_CONFIGS]: (state, action) => {
|
||||
return state.merge(fromJS(action.payload))
|
||||
},
|
||||
|
||||
[TOGGLE_CONFIGS]: (state, action) => {
|
||||
const configName = action.payload
|
||||
const oriVal = state.get(configName)
|
||||
return state.set(configName, !oriVal)
|
||||
},
|
||||
|
||||
}
|
||||
4
src/plugins/configs/selectors.js
Normal file
4
src/plugins/configs/selectors.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// Just get the config value ( it can possibly be an immutable object)
|
||||
export const get = (state, path) => {
|
||||
return state.getIn(Array.isArray(path) ? path : [path])
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react"
|
||||
import React, { cloneElement } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
//import "./topbar.less"
|
||||
@@ -129,12 +129,12 @@ export default class Topbar extends React.Component {
|
||||
<div className="topbar">
|
||||
<div className="wrapper">
|
||||
<div className="topbar-wrapper">
|
||||
<Link href="#" title="Swagger UX">
|
||||
<Link href="#">
|
||||
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
|
||||
<span>swagger</span>
|
||||
</Link>
|
||||
<form className="download-url-wrapper" onSubmit={formOnSubmit}>
|
||||
{control}
|
||||
{control.map((el, i) => cloneElement(el, { key: i }))}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
if(!window.Promise) {
|
||||
require("core-js/fn/promise")
|
||||
}
|
||||
|
||||
// Required by IE 11
|
||||
if(!String.prototype.startsWith) {
|
||||
require("core-js/es6/string")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
padding: 10px 0;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
.btn-done {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-wrapper
|
||||
@@ -25,7 +29,7 @@
|
||||
margin: 0 0 10px 0;
|
||||
padding: 10px 20px;
|
||||
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-bottom: 1px solid $auth-container-border-color;
|
||||
|
||||
&:last-of-type
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user