Compare commits

..
1 Commits
Author SHA1 Message Date
kyle 66d6a36d77 Create DOCS_PLAN.md 2017-09-19 23:19:28 -07:00
128 changed files with 970 additions and 3265 deletions
+2 -4
View File
@@ -16,8 +16,7 @@
"extends": ["eslint:recommended", "plugin:react/recommended"],
"plugins": [
"react",
"mocha"
"react"
],
"rules": {
@@ -33,7 +32,6 @@
"comma-dangle": 0,
"no-console": ["error", { allow: ["warn", "error"] }],
"react/jsx-no-bind": 1,
"react/display-name": 0,
"mocha/no-exclusive-tests": "error"
"react/display-name": 0
}
}
-3
View File
@@ -1,9 +1,6 @@
<!---
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
View File
@@ -1,42 +0,0 @@
<!--- 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
View File
@@ -2,7 +2,6 @@ node_modules
.idea
.deps_check
.DS_Store
.nyc_output
npm-debug.log*
.eslintcache
package-lock.json
-1
View File
@@ -6,4 +6,3 @@
!dist/swagger-ui.js.map
!dist/swagger-ui.css
!dist/swagger-ui.css.map
!dist/oauth2-redirect.html
-4
View File
@@ -1,16 +1,12 @@
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:
+1 -1
View File
@@ -4,7 +4,7 @@ We love contributions from our community of users! This document explains our gu
#### Environment setup
0. Install Node.js (6 or newer) and npm (3 or newer).
0. Install Node.js (4 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.
+54
View File
@@ -0,0 +1,54 @@
# Swagger OSS Documentation Plan
Notes:
- I came across GitBook recently, and it looks like a great way to host documentation. Example: https://jonas.github.io/tig/, https://codiechanel.gitbooks.io/gitbook-demo/content/
### Swagger-UI
##### Usage (12h)
- Downloading the project via npm, git submodules, packagist, unkpg cdn
- How to integrate with generic JS, React
- How to use Docker image (basic + Docker Compose)
- Configuration options and their individual docs
- Instance method (`initOAuth`, etc) documentation
##### Customization (16h)
- Plugin system overview
- State machine: Redux
- Actions, Wrap Actions, Reducers, Selectors, Wrap Selectors, Components, Wrap Components
- Plugins vs. Presets (compare w. Babel: presets are collections of plugins)
- Example custom plugins for common use cases (on spec update, replace a component, etc)
- Example third-party plugin served via npm
- Example custom layout
- Disclaimer: plugins and components are not part of our public API. We'll make our best effort to minimize breaking changes, but you should pin a version if you're making heavy customizations.
-
##### Core Development (8h)
- Application architecture overview
- React + Redux + Reselect
- Custom plugin system (Editor is a plugin)
- How to set up a local dev environment
- Contribution guidelines
- Linter and testing instructions
- GitHub Issues workflow (priorities, labels, code review, etc)
- Writing tests (JS unit, Enzyme unit, Nightwatch e2e)
### Swagger-Editor
##### Usage (6h)
- Downloading the project via npm, git submodules, packagist, unkpg, direct clone
- How to integrate with generic JS, React
- How to use Docker image (basic + Docker Compose)
- Editor-specific configuration options
##### Customization (4h)
- How to extend the validation and autocomplete systems
##### Development (4h)
- Validation and autocomplete system architecture
- How to develop Editor and UI simultaneously with `npm link`
### Swagger-Client (8h)
- Basic usage in browser + Node
- File uploads in browser + Node
- Instance method documentation (`buildRequest`, `execute`, etc)
+4 -7
View File
@@ -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 issue](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22) label.
**👉🏼 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.
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.5.0 | 2017-11-23 | 2.0, 3.0 | [tag v3.5.0](https://github.com/swagger-api/swagger-ui/tree/v3.5.0)
3.2.1 | 2017-09-15 | 2.0, 3.0 | [tag v3.2.1](https://github.com/swagger-api/swagger-ui/tree/v3.2.1)
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)
@@ -119,7 +119,7 @@ scopeSeparator | scope separator for passing scopes, encoded before calling, def
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false`
```javascript
```
const ui = SwaggerUIBundle({...})
// Method can be called in any place after calling constructor SwaggerUIBundle
@@ -151,8 +151,6 @@ domNode | The HTML DOM element inside which SwaggerUi will put the user interfac
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
@@ -165,14 +163,13 @@ deepLinking | If set to `true`, enables dynamic deep linking for tags and operat
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.
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`.
### Plugins
#### Topbar plugin
Topbar plugin enables top bar with input for spec path and explore button or a dropdown if `urls` is used. By default the plugin is enabled, and to disable it you need to remove Topbar plugin from presets in `src/standalone/index.js`:
```javascript
```
let preset = [
// TopbarPlugin,
ConfigsPlugin,
-1
View File
@@ -3,7 +3,6 @@
<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" >
+1 -4
View File
@@ -27,10 +27,7 @@
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
+1 -4
View File
@@ -27,10 +27,7 @@
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
+13 -26
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
{"version":3,"file":"swagger-ui.css","sources":[],"mappings":"","sourceRoot":""}
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -32,7 +32,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
+3 -3
View File
@@ -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 `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, … }`.
- 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, … }`.
- 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.
@@ -51,4 +51,4 @@ If you've determined this is the version you have, to find the exact version:
* @link http://swagger.io
* @license Apache-2.0
*/
```
```
+1 -1
View File
@@ -137,7 +137,7 @@ module.exports = function(rules, options) {
}
},
devtool: specialOptions.sourcemaps ? "nosource-source-map" : null,
devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null,
plugins,
+10 -23
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.5.0",
"version": "3.2.1",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -32,20 +32,16 @@
"test": "npm run lint-errors && npm run just-test-in-node",
"test-in-node": "npm run lint-errors && npm run just-test-in-node",
"just-test": "karma start --config karma.conf.js",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"just-check-coverage": "nyc npm run just-test-in-node",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package",
"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",
@@ -58,35 +54,34 @@
"memoizee": "0.4.1",
"promise-worker": "^1.1.1",
"prop-types": "^15.5.10",
"react": "^15.6.2",
"react": "^15.4.0",
"react-addons-perf": "^15.4.0",
"react-addons-shallow-compare": "0.14.8",
"react-addons-test-utils": "^15.6.2",
"react-collapse": "^4.0.3",
"react-dom": "^15.6.2",
"react-addons-test-utils": "^15.4.0",
"react-collapse": "2.3.1",
"react-dom": "^15.4.0",
"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.5.2",
"react-motion": "0.4.4",
"react-object-inspector": "0.2.1",
"react-redux": "^4.x.x",
"react-split-pane": "0.1.70",
"react-remarkable": "1.1.1",
"react-split-pane": "0.1.57",
"redux": "^3.x.x",
"redux-immutable": "3.0.8",
"redux-logger": "*",
"remarkable": "^1.7.1",
"reselect": "2.5.3",
"sanitize-html": "^1.14.1",
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.3.5",
"swagger-client": "3.1.1",
"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": {
@@ -109,7 +104,6 @@
"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",
@@ -129,7 +123,6 @@
"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",
@@ -155,11 +148,5 @@
],
"optionalDependencies": {
"webpack-dev-server": "2.5.0"
},
"nyc": {
"all": true,
"include": [
"**/src/core/plugins/**.js"
]
}
}
+12 -16
View File
@@ -7,7 +7,6 @@ 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,
@@ -16,39 +15,36 @@ export default class ArrayModel extends Component {
}
render(){
let { getComponent, getConfigs, schema, depth, expandDepth, name } = this.props
let description = schema.get("description")
let { getComponent, schema, depth, expandDepth, name } = this.props
let items = schema.get("items")
let title = schema.get("title") || name
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 )
const Markdown = getComponent("Markdown")
const ModelCollapse = getComponent("ModelCollapse")
const Model = getComponent("Model")
const Property = getComponent("Property")
const titleEl = title &&
<span className="model-title">
<span className="model-title__text">{ title }</span>
</span>
/*
/*
Note: we set `name={null}` in <Model> below because we don't want
the name of the current Model passed (and displayed) as the name of the array element Model
*/
*/
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
[
{
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 } getConfigs={ getConfigs } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
<span><Model { ...this.props } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
]
{
properties.size ? <span>
{ properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={propStyle}>
<br />{ key }: { String(v) }</span>)
}<br /></span>
: null
}
</ModelCollapse>
</span>
}
+1 -5
View File
@@ -51,11 +51,7 @@ export default class ApiKeyAuth extends React.Component {
return (
<div>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(apiKey)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown source={ schema.get("description") } />
-62
View File
@@ -1,62 +0,0 @@
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,23 +1,25 @@
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
if(onClick) {
onClick()
}
let { security, authActions, authSelectors } = this.props
let definitions = authSelectors.getDefinitionsByNames(security)
authActions.showDefinitions(definitions)
}
render() {
let { isAuthorized } = this.props
let { security, authSelectors } = this.props
let isAuthorized = authSelectors.isAuthorized(security)
if(isAuthorized === null) {
return null
}
return (
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
@@ -28,4 +30,10 @@ export default class AuthorizeOperationBtn extends React.Component {
)
}
static propTypes = {
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
security: ImPropTypes.iterable.isRequired
}
}
+30 -18
View File
@@ -27,6 +27,7 @@ export default class Auths extends React.Component {
e.preventDefault()
let { authActions } = this.props
authActions.authorize(this.state)
}
@@ -41,16 +42,10 @@ 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 AuthItem = getComponent("AuthItem")
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")
const Oauth2 = getComponent("oauth2", true)
const Button = getComponent("Button")
@@ -69,19 +64,36 @@ export default class Auths extends React.Component {
!!nonOauthDefinitions.size && <form onSubmit={ this.submitAuth }>
{
nonOauthDefinitions.map( (schema, name) => {
return <AuthItem
key={name}
schema={schema}
name={name}
getComponent={getComponent}
onAuthChange={this.onAuthChange}
authorized={authorized}
errSelectors={errSelectors}
/>
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>)
}).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>
+9 -15
View File
@@ -2,6 +2,11 @@ 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,
@@ -11,7 +16,6 @@ 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
}
@@ -79,9 +83,7 @@ export default class Oauth2 extends React.Component {
}
render() {
let {
schema, getComponent, authSelectors, errSelectors, name, specSelectors
} = this.props
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
@@ -90,14 +92,6 @@ 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)
@@ -108,7 +102,7 @@ export default class Oauth2 extends React.Component {
return (
<div>
<h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
{ description && <Markdown source={ schema.get("description") } /> }
@@ -200,11 +194,11 @@ export default class Oauth2 extends React.Component {
<Row key={ name }>
<div className="checkbox">
<Input data-value={ name }
id={`${name}-${flow}-checkbox-${this.state.name}`}
id={`${name}-checkbox-${this.state.name}`}
disabled={ isAuthorized }
type="checkbox"
onChange={ this.onScopeChange }/>
<label htmlFor={`${name}-${flow}-checkbox-${this.state.name}`}>
<label htmlFor={`${name}-checkbox-${this.state.name}`}>
<span className="item"></span>
<div className="text">
<p className="name">{name}</p>
+1 -11
View File
@@ -27,16 +27,6 @@ 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() {
@@ -47,7 +37,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 -1
View File
@@ -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"
+2 -2
View File
@@ -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(" ")
+1
View File
@@ -8,6 +8,7 @@ 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
}
+6 -7
View File
@@ -2,7 +2,6 @@ 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 {
@@ -36,9 +35,9 @@ class Contact extends React.Component {
return (
<div>
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
{ email &&
<a href={sanitizeUrl(`mailto:${email}`)}>
<a href={`mailto:${email}`}>
{ url ? `Send email to ${name}` : `Contact ${name}`}
</a>
}
@@ -60,7 +59,7 @@ class License extends React.Component {
return (
<div>
{
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
url ? <a target="_blank" href={ url }>{ name }</a>
: <span>{ name }</span>
}
</div>
@@ -98,7 +97,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={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
</hgroup>
<div className="description">
@@ -107,14 +106,14 @@ export default class Info extends React.Component {
{
termsOfService && <div>
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
<a target="_blank" href={ 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={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
: null }
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import { Collapse as OriCollapse } from "react-collapse"
import OriCollapse from "react-collapse"
function xclass(...args) {
return args.filter(a => !!a).join(" ").trim()
+6 -23
View File
@@ -1,7 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { Iterable } from "immutable"
const Headers = ( { headers } )=>{
return (
@@ -29,29 +28,19 @@ Duration.propTypes = {
export default class LiveResponse extends React.Component {
static propTypes = {
response: PropTypes.instanceOf(Iterable).isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
response: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
displayRequestDuration: PropTypes.bool.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, path, method } = this.props
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
const { showMutatedRequest } = getConfigs()
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
const status = response.get("status")
const url = response.get("url")
const headers = response.get("headers").toJS()
@@ -72,13 +61,6 @@ export default class LiveResponse extends React.Component {
return (
<div>
{ curlRequest && <Curl request={ curlRequest }/> }
{ url && <div>
<h4>Request URL</h4>
<div className="request-url">
<pre>{url}</pre>
</div>
</div>
}
<h4>Server response</h4>
<table className="responses-table">
<thead>
@@ -129,6 +111,7 @@ export default class LiveResponse extends React.Component {
static propTypes = {
getComponent: PropTypes.func.isRequired,
request: ImPropTypes.map,
response: ImPropTypes.map
}
}
+5 -12
View File
@@ -7,19 +7,14 @@ export default class ModelExample extends React.Component {
specSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
example: PropTypes.any.isRequired,
isExecute: PropTypes.bool,
getConfigs: PropTypes.func.isRequired
isExecute: PropTypes.bool
}
constructor(props, context) {
super(props, context)
let { getConfigs } = this.props
let { defaultModelRendering } = getConfigs()
if (defaultModelRendering !== "example" && defaultModelRendering !== "model") {
defaultModelRendering = "example"
}
this.state = {
activeTab: defaultModelRendering
activeTab: "example"
}
}
@@ -32,8 +27,7 @@ export default class ModelExample extends React.Component {
}
render() {
let { getComponent, specSelectors, schema, example, isExecute, getConfigs } = this.props
let { defaultModelExpandDepth } = getConfigs()
let { getComponent, specSelectors, schema, example, isExecute } = this.props
const ModelWrapper = getComponent("ModelWrapper")
return <div>
@@ -52,9 +46,8 @@ export default class ModelExample extends React.Component {
{
!isExecute && this.state.activeTab === "model" && <ModelWrapper schema={ schema }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
expandDepth={ defaultModelExpandDepth } />
expandDepth={ 1 } />
}
+4 -3
View File
@@ -6,17 +6,18 @@ 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, getConfigs } = this.props
let { getComponent } = this.props
const Model = getComponent("Model")
return <div className="model-box">
<Model { ...this.props } getConfigs={ getConfigs } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
</div>
}
}
+7 -12
View File
@@ -1,12 +1,10 @@
import React, { PureComponent } from "react"
import ImPropTypes from "react-immutable-proptypes"
import React, { Component } from "react"
import PropTypes from "prop-types"
export default class Model extends PureComponent {
export default class Model extends Component {
static propTypes = {
schema: ImPropTypes.orderedMap.isRequired,
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
@@ -31,13 +29,13 @@ export default class Model extends PureComponent {
}
render () {
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef } = this.props
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
let type = "object"
let $$ref = schema && schema.get("$$ref")
// If we weren't passed a `name` but have a ref, grab the name from the ref
if ( !name && $$ref ) {
name = this.getModelName( $$ref )
@@ -46,16 +44,15 @@ export default class Model extends PureComponent {
if ( !schema && $$ref ) {
schema = this.getRefSchema( name )
}
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
isRef = isRef !== undefined ? isRef : !!$$ref
type = schema && schema.get("type") || type
switch(type) {
case "object":
return <ObjectModel
className="object" { ...this.props }
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}
@@ -63,7 +60,6 @@ export default class Model extends PureComponent {
case "array":
return <ArrayModel
className="array" { ...this.props }
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}
@@ -76,7 +72,6 @@ export default class Model extends PureComponent {
return <PrimitiveModel
{ ...this.props }
getComponent={ getComponent }
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}
+1 -3
View File
@@ -13,7 +13,7 @@ export default class Models extends Component {
render(){
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
let definitions = specSelectors.definitions()
let { docExpansion, defaultModelExpandDepth } = getConfigs()
let { docExpansion } = getConfigs()
let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" )
const ModelWrapper = getComponent("ModelWrapper")
@@ -33,10 +33,8 @@ export default class Models extends Component {
definitions.entrySeq().map( ( [ name, model ])=>{
return <div className="model-container" key={ `models-section-${name}` }>
<ModelWrapper name={ name }
expandDepth={ defaultModelExpandDepth }
schema={ model }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }/>
</div>
}).toArray()
+8 -46
View File
@@ -9,7 +9,6 @@ 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,
@@ -18,16 +17,10 @@ export default class ObjectModel extends Component {
}
render(){
let { schema, name, isRef, getComponent, getConfigs, depth, expandDepth, ...otherProps } = this.props
let { schema, name, isRef, getComponent, 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")
@@ -79,14 +72,13 @@ 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} className={isDeprecated && "deprecated"}>
return (<tr key={key}>
<td style={ propertyStyle }>
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> }
</td>
@@ -94,37 +86,12 @@ 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>&nbsp;</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>
@@ -132,7 +99,6 @@ export default class ObjectModel extends Component {
<td>
<Model { ...otherProps } required={ false }
getComponent={ getComponent }
getConfigs={ getConfigs }
schema={ additionalProperties }
depth={ depth + 1 } />
</td>
@@ -146,7 +112,6 @@ 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>
})}
@@ -161,7 +126,6 @@ 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>
})}
@@ -173,14 +137,12 @@ export default class ObjectModel extends Component {
: <tr>
<td>{ "not ->" }</td>
<td>
<div>
<Model { ...otherProps }
required={ false }
getComponent={ getComponent }
getConfigs={ getConfigs }
schema={ not }
depth={ depth + 1 } />
</div>
{not.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
schema={ schema }
depth={ depth + 1 } /></div>
})}
</td>
</tr>
}
@@ -1,6 +1,5 @@
import React from "react"
import PropTypes from "prop-types"
import { sanitizeUrl } from "core/utils"
export default class OnlineValidatorBadge extends React.Component {
static propTypes = {
@@ -33,8 +32,6 @@ 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
@@ -43,8 +40,8 @@ export default class OnlineValidatorBadge extends React.Component {
}
return (<span style={{ float: "right"}}>
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
<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>
</span>)
}
@@ -1,17 +0,0 @@
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
@@ -1,35 +0,0 @@
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
+119 -78
View File
@@ -1,22 +1,29 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { getList } from "core/utils"
import { getExtensions, sanitizeUrl } from "core/utils"
import { Iterable } from "immutable"
import * as CustomPropTypes from "core/proptypes"
//import "less/opblock"
export default class Operation extends PureComponent {
static propTypes = {
operation: PropTypes.instanceOf(Iterable).isRequired,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
operation: PropTypes.object.isRequired,
showSummary: PropTypes.bool,
toggleShown: PropTypes.func.isRequired,
onTryoutClick: PropTypes.func.isRequired,
onCancelClick: PropTypes.func.isRequired,
onExecute: PropTypes.func.isRequired,
isShownKey: CustomPropTypes.arrayOrString.isRequired,
jumpToKey: CustomPropTypes.arrayOrString.isRequired,
allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.object,
request: PropTypes.object,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
authActions: PropTypes.object,
authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired,
@@ -24,68 +31,108 @@ export default class Operation extends PureComponent {
oas3Actions: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired
fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
}
static defaultProps = {
operation: null,
showSummary: true,
response: null,
request: 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 })
}
render() {
let {
isShownKey,
jumpToKey,
path,
method,
operation,
showSummary,
response,
request,
toggleShown,
onTryoutClick,
onCancelClick,
onExecute,
allowTryItOut,
displayOperationId,
displayRequestDuration,
fn,
getComponent,
getConfigs,
specActions,
specSelectors,
authActions,
authSelectors,
getConfigs,
oas3Actions
} = this.props
let operationProps = this.props.operation
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 summary = operation.get("summary")
let description = operation.get("description")
let deprecated = operation.get("deprecated")
let externalDocs = operation.get("externalDocs")
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" )
@@ -96,27 +143,30 @@ export default class Operation extends PureComponent {
const Collapse = getComponent( "Collapse" )
const Markdown = getComponent( "Markdown" )
const Schemes = getComponent( "schemes" )
const OperationExt = getComponent( "OperationExt" )
const { showExtensions } = getConfigs()
const { deepLinking } = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
// Merge in Live Response
if(responses && response && response.size > 0) {
if(response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status")))
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" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
<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} >
<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.join("/")}` : null}>
href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
<span>{path}</span>
</a>
<JumpToPath path={jumpToKey} />
@@ -132,17 +182,13 @@ export default class Operation extends PureComponent {
{
(!security || !security.count()) ? null :
<AuthorizeOperationBtn
isAuthorized={ isAuthorized }
onClick={() => {
const applicableDefinitions = authSelectors.definitionsForRequirements(security)
authActions.showDefinitions(applicableDefinitions)
}}
/>
<AuthorizeOperationBtn authActions={ authActions }
security={ security }
authSelectors={ authSelectors }/>
}
</div>
<Collapse isOpened={isShown}>
<Collapse isOpened={shown}>
<div className="opblock-body">
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
{ description &&
@@ -153,24 +199,23 @@ export default class Operation extends PureComponent {
</div>
}
{
externalDocs && externalDocs.url ?
externalDocs && externalDocs.get("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.description } />
<Markdown source={ externalDocs.get("description") } />
</span>
<a target="_blank" className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.url) }>{ externalDocs.url }</a>
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
</div>
</div> : null
}
<Parameters
parameters={parameters}
operation={operation}
onChangeKey={onChangeKey}
onTryoutClick = { onTryoutClick }
onCancelClick = { onCancelClick }
onTryoutClick = { this.onTryoutClick }
onCancelClick = { this.onCancelClick }
tryItOutEnabled = { tryItOutEnabled }
allowTryItOut={allowTryItOut}
@@ -179,7 +224,6 @@ export default class Operation extends PureComponent {
specActions={ specActions }
specSelectors={ specSelectors }
pathMethod={ [path, method] }
getConfigs={ getConfigs }
/>
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ? <div className="opblock-schemes">
@@ -195,23 +239,25 @@ export default class Operation extends PureComponent {
{ !tryItOutEnabled || !allowTryItOut ? null :
<Execute
getComponent={getComponent}
operation={ operation }
specActions={ specActions }
specSelectors={ specSelectors }
path={ path }
method={ method }
onExecute={ onExecute } />
onExecute={ this.onExecute } />
}
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null :
<Clear
onClick={ this.onClearClick }
specActions={ specActions }
path={ path }
method={ method }/>
}
</div>
{executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
{this.state.executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
{ !responses ? null :
<Responses
@@ -225,15 +271,10 @@ export default class Operation extends PureComponent {
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
path={ path }
method={ method }
pathMethod={ [path, method] }
displayRequestDuration={ displayRequestDuration }
fn={fn} />
}
{ !showExtensions || !extensions.size ? null :
<OperationExt extensions={ extensions } getComponent={ getComponent } />
}
</div>
</Collapse>
</div>
+55 -14
View File
@@ -1,6 +1,8 @@
import React from "react"
import PropTypes from "prop-types"
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
import { helpers } from "swagger-client"
import { createDeepLinkPath } from "core/utils"
const { opId } = helpers
export default class Operations extends React.Component {
@@ -19,20 +21,27 @@ export default class Operations extends React.Component {
render() {
let {
specSelectors,
specActions,
oas3Actions,
getComponent,
layoutSelectors,
layoutActions,
getConfigs
authActions,
authSelectors,
getConfigs,
fn
} = this.props
let taggedOps = specSelectors.taggedOperations()
const OperationContainer = getComponent("OperationContainer", true)
const Operation = getComponent("operation")
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
let showSummary = layoutSelectors.showSummary()
let {
docExpansion,
displayOperationId,
displayRequestDuration,
maxDisplayedTags,
deepLinking
} = getConfigs()
@@ -80,7 +89,7 @@ export default class Operations extends React.Component {
</a>
{ !tagDescription ? null :
<small>
<Markdown source={tagDescription} />
{ tagDescription }
</small>
}
@@ -91,7 +100,7 @@ export default class Operations extends React.Component {
{ tagExternalDocsUrl ? ": " : null }
{ tagExternalDocsUrl ?
<a
href={sanitizeUrl(tagExternalDocsUrl)}
href={tagExternalDocsUrl}
onClick={(e) => e.stopPropagation()}
target={"_blank"}
>{tagExternalDocsUrl}</a> : null
@@ -110,15 +119,47 @@ export default class Operations extends React.Component {
<Collapse isOpened={showTag}>
{
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
return <OperationContainer
key={`${path}-${method}`}
op={op}
path={path}
method={method}
tag={tag}
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 }
/>
}).toArray()
}
+2 -2
View File
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
updateValues = (props) => {
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : {}
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : {}
let isXml = /xml/i.test(consumesValue)
let isJson = /json/i.test(consumesValue)
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
@@ -107,7 +107,7 @@ export default class ParamBody extends PureComponent {
const HighlightCode = getComponent("highlightCode")
const ContentType = getComponent("contentType")
// for domains where specSelectors not passed
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : param
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : param
let errors = parameter.get("errors", List())
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
@@ -1,12 +0,0 @@
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
+7 -26
View File
@@ -1,8 +1,6 @@
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 = {
@@ -13,8 +11,7 @@ export default class ParameterRow extends Component {
isExecute: PropTypes.bool,
onChangeConsumes: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired
pathMethod: PropTypes.array.isRequired
}
constructor(props, context) {
@@ -22,7 +19,7 @@ export default class ParameterRow extends Component {
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let value = parameter ? parameter.get("value") : ""
if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
@@ -31,21 +28,11 @@ 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 parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let paramValue = parameter ? parameter.get("value") : undefined
let enumValue = parameter ? parameter.get("enum") : undefined
let value
if ( paramValue !== undefined ) {
@@ -69,12 +56,10 @@ export default class ParameterRow extends Component {
}
render() {
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props
let {param, onChange, getComponent, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props
let { isOAS3 } = specSelectors
const { showExtensions } = getConfigs()
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
const ParamBody = getComponent("ParamBody")
@@ -94,7 +79,6 @@ 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")
@@ -102,9 +86,8 @@ export default class ParameterRow extends Component {
let isFormDataSupported = "FormData" in win
let required = param.get("required")
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let value = parameter ? parameter.get("value") : ""
let extensions = getExtensions(param)
return (
<tr>
@@ -118,7 +101,6 @@ 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">
@@ -132,13 +114,12 @@ 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={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
schema={ param }/>
}
{
bodyParam && schema ? <ModelExample getComponent={ getComponent }
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ schema }
+3 -6
View File
@@ -19,8 +19,7 @@ export default class Parameters extends Component {
onTryoutClick: PropTypes.func,
onCancelClick: PropTypes.func,
onChangeKey: PropTypes.array,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired
pathMethod: PropTypes.array.isRequired
}
@@ -38,7 +37,7 @@ export default class Parameters extends Component {
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
changeParam( onChangeKey, param.get("name"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -61,7 +60,6 @@ export default class Parameters extends Component {
fn,
getComponent,
getConfigs,
specSelectors,
pathMethod
} = this.props
@@ -95,9 +93,8 @@ export default class Parameters extends Component {
eachMap(parameters, (parameter) => (
<ParameterRow fn={ fn }
getComponent={ getComponent }
getConfigs={ getConfigs }
param={ parameter }
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
key={ parameter.get( "name" ) }
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
specSelectors={ specSelectors }
+6 -15
View File
@@ -1,6 +1,5 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import { getExtensions } from "core/utils"
const propStyle = { color: "#999", fontStyle: "italic" }
@@ -8,15 +7,12 @@ 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, getConfigs, name, depth } = this.props
const { showExtensions } = getConfigs()
let { schema, getComponent, name, depth } = this.props
if(!schema || !schema.get) {
// don't render if schema isn't correctly formed
@@ -29,13 +25,9 @@ export default class Primitive extends Component {
let enumArray = schema.get("enum")
let title = schema.get("title") || name
let description = schema.get("description")
let extensions = getExtensions(schema)
let properties = schema
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
.filterNot( (v, key) => extensions.has(key) )
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
const Markdown = getComponent("Markdown")
const EnumModel = getComponent("EnumModel")
const Property = getComponent("Property")
return <span className="model">
<span className="prop">
@@ -43,10 +35,9 @@ 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 ] ) => <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
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
<br />{ key }: { String(v) }</span>)
: null
}
{
!description ? null :
@@ -65,4 +56,4 @@ export default class Primitive extends Component {
</span>
</span>
}
}
}
-16
View File
@@ -1,16 +0,0 @@
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
+18 -25
View File
@@ -1,44 +1,37 @@
import React from "react"
import PropTypes from "prop-types"
import Remarkable from "remarkable"
import Remarkable from "react-remarkable"
import sanitize from "sanitize-html"
function Markdown({ source }) {
const html = new Remarkable({
html: true,
typographer: true,
breaks: true,
linkify: true,
linkTarget: "_blank"
}).render(source)
const sanitized = sanitizer(html)
const sanitized = sanitizer(source)
if ( !source || !html || !sanitized ) {
return null
}
// sometimes the sanitizer returns "undefined" as a string
if(!source || !sanitized || sanitized === "undefined") {
return null
}
return (
<div className="markdown" dangerouslySetInnerHTML={{ __html: sanitized }}></div>
)
return <div className="markdown">
<Remarkable
options={{html: true, typographer: true, breaks: true, linkify: true, linkTarget: "_blank"}}
source={sanitized}
></Remarkable>
</div>
}
Markdown.propTypes = {
source: PropTypes.string.isRequired
source: PropTypes.string.isRequired
}
export default Markdown
const sanitizeOptions = {
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(/&quot;/g, "\"")
}
textFilter: function(text) {
return text
.replace(/&quot;/g, "\"")
}
}
export function sanitizer(str) {
return sanitize(str, sanitizeOptions)
return sanitize(str, sanitizeOptions)
}
+7 -14
View File
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import formatXml from "xml-but-prettier"
import { formatXml } from "core/utils"
import lowerCase from "lodash/lowerCase"
export default class ResponseBody extends React.Component {
@@ -31,10 +31,7 @@ export default class ResponseBody extends React.Component {
// XML
} else if (/xml/i.test(contentType)) {
body = formatXml(content, {
textNodesOnSameLine: true,
indentor: " "
})
body = formatXml(content)
bodyEl = <HighlightCode value={ body } />
// HTML or Plain Text
@@ -52,10 +49,10 @@ export default class ResponseBody extends React.Component {
// Download
} else if (
/^application\/octet-stream/i.test(contentType) ||
(headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"])) ||
(headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"])) ||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"]) ||
headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"]) ||
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
@@ -86,12 +83,8 @@ export default class ResponseBody extends React.Component {
// Anything else (CORS)
} else if (typeof content === "string") {
bodyEl = <HighlightCode value={ content } />
} 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
bodyEl = <div>Unknown response type</div>
}
return ( !bodyEl ? null : <div>
+2 -13
View File
@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import cx from "classnames"
import { fromJS, Seq, Iterable } from "immutable"
import { fromJS, Seq } from "immutable"
import { getSampleSchema, fromJSOrdered } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
@@ -42,10 +42,9 @@ export default class Response extends React.Component {
static propTypes = {
code: PropTypes.string.isRequired,
response: PropTypes.instanceOf(Iterable),
response: PropTypes.object,
className: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string,
@@ -74,7 +73,6 @@ export default class Response extends React.Component {
className,
fn,
getComponent,
getConfigs,
specSelectors,
contentType,
controlsAcceptHeader
@@ -109,14 +107,6 @@ export default class Response extends React.Component {
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
}) : null
}
if(examples) {
examples = examples.map(example => {
// Remove unwanted properties from examples
return example.set ? example.set("$$ref", undefined) : example
})
}
let example = getExampleComponent( sampleResponse, examples, HighlightCode )
return (
@@ -146,7 +136,6 @@ export default class Response extends React.Component {
{ example ? (
<ModelExample
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
schema={ fromJSOrdered(schema) }
example={ example }/>
+18 -29
View File
@@ -1,52 +1,41 @@
import React from "react"
import PropTypes from "prop-types"
import { fromJS, Iterable } from "immutable"
import { fromJS } from "immutable"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component {
static propTypes = {
tryItOutResponse: PropTypes.instanceOf(Iterable),
responses: PropTypes.instanceOf(Iterable).isRequired,
produces: PropTypes.instanceOf(Iterable),
request: PropTypes.object,
tryItOutResponse: PropTypes.object,
responses: PropTypes.object.isRequired,
produces: PropTypes.object,
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,
fn: PropTypes.object.isRequired
pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
}
static defaultProps = {
request: null,
tryItOutResponse: null,
produces: fromJS(["application/json"]),
displayRequestDuration: false
}
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)
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
const { oas3Actions, path, method } = this.props
const { oas3Actions, pathMethod } = this.props
if(controlsAcceptHeader) {
oas3Actions.setResponseContentType({
value,
path,
method
pathMethod
})
}
}
@@ -54,6 +43,7 @@ export default class Responses extends React.Component {
render() {
let {
responses,
request,
tryItOutResponse,
getComponent,
getConfigs,
@@ -91,12 +81,12 @@ export default class Responses extends React.Component {
{
!tryItOutResponse ? null
: <div>
<LiveResponse response={ tryItOutResponse }
<LiveResponse request={ request }
response={ tryItOutResponse }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
path={ this.props.path }
method={ this.props.method }
pathMethod={ this.props.pathMethod }
displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4>
</div>
@@ -126,7 +116,6 @@ export default class Responses extends React.Component {
controlsAcceptHeader={response === acceptControllingResponse}
onContentTypeChange={this.onResponseContentTypeChange}
contentType={ producesValue }
getConfigs={ getConfigs }
getComponent={ getComponent }/>
)
}).toArray()
-203
View File
@@ -1,203 +0,0 @@
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,
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,
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}
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
authActions={ authActions }
authSelectors={ authSelectors }
getComponent={ getComponent }
getConfigs={ getConfigs }
fn={fn}
/>
)
}
}
+10 -11
View File
@@ -7,7 +7,8 @@ import * as AllPlugins from "core/plugins/all"
import { parseSearch } from "core/utils"
if (process.env.NODE_ENV !== "production") {
window.Perf = require("react-addons-perf")
const Perf = require("react-addons-perf")
window.Perf = Perf
}
// eslint-disable-next-line no-undef
@@ -44,9 +45,6 @@ module.exports = function SwaggerUI(opts) {
requestInterceptor: (a => a),
responseInterceptor: (a => a),
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,12 +56,13 @@ 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()
@@ -73,12 +72,12 @@ module.exports = function SwaggerUI(opts) {
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
const storeConfigs = {
const storeConfigs = deepExtend({}, constructorConfig.store, {
system: {
configs: constructorConfig.configs
},
plugins: constructorConfig.presets,
state: deepExtend({
state: {
layout: {
layout: constructorConfig.layout,
filter: constructorConfig.filter
@@ -87,8 +86,8 @@ module.exports = function SwaggerUI(opts) {
spec: "",
url: constructorConfig.url
}
}, constructorConfig.initialState)
}
}
})
let inlinePlugin = ()=> {
return {
+7 -20
View File
@@ -58,7 +58,6 @@ 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 }
@@ -68,20 +67,10 @@ 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" : ""}
title={ errors.length ? errors : ""}
onChange={ this.onChange }
disabled={isDisabled}/>)
return <Input type="file" className={ errors.length ? "invalid" : ""} onChange={ this.onChange } disabled={isDisabled}/>
}
else {
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}/>)
return <Input type={ schema.format === "password" ? "password" : "text" } className={ errors.length ? "invalid" : ""} value={value} placeholder={description} onChange={ this.onChange } disabled={isDisabled}/>
}
}
}
@@ -145,12 +134,11 @@ export class JsonSchema_array extends PureComponent {
if ( enumValue ) {
const Select = getComponent("Select")
return (<Select className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
multiple={ true }
value={ value }
allowedValues={ enumValue }
allowEmptyValue={ !required }
onChange={ this.onEnumChange }/>)
multiple={ true }
value={ value }
allowedValues={ enumValue }
allowEmptyValue={ !required }
onChange={ this.onEnumChange }/>)
}
return (
@@ -187,7 +175,6 @@ 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 }
+1 -12
View File
@@ -22,16 +22,6 @@ 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") {
@@ -74,8 +64,7 @@ export default function authorize ( { auth, authActions, errActions, configs, au
}
}
let authorizationUrl = schema.get("authorizationUrl")
let url = [authorizationUrl, query.join("&")].join(authorizationUrl.indexOf("?") === -1 ? "?" : "&")
let url = [schema.get("authorizationUrl"), query.join("&")].join("?")
// pass action authorizeOauth2 and authentication data through window
// to authorize with oauth2
+1 -1
View File
@@ -73,7 +73,7 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
let form = {
grant_type: "password",
scope: auth.scopes.join(scopeSeparator)
scope: encodeURIComponent(auth.scopes.join(scopeSeparator))
}
let query = {}
let headers = {}
+1 -1
View File
@@ -22,7 +22,7 @@ export default {
securities.entrySeq().forEach( ([ key, security ]) => {
let type = security.getIn(["schema", "type"])
if ( type === "apiKey" || type === "http" ) {
if ( type === "apiKey" ) {
map = map.set(key, security)
} else if ( type === "basic" ) {
let username = security.getIn(["value", "username"])
+4 -12
View File
@@ -10,8 +10,8 @@ export const shownDefinitions = createSelector(
export const definitionsToAuthorize = createSelector(
state,
() => ( { specSelectors } ) => {
let definitions = specSelectors.securityDefinitions() || Map({})
() =>( { specSelectors } ) => {
let definitions = specSelectors.securityDefinitions()
let list = List()
//todo refactor
@@ -27,8 +27,7 @@ export const definitionsToAuthorize = createSelector(
)
export const getDefinitionsByNames = ( state, securities ) => ( { specSelectors } ) => {
console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.")
export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors } ) => {
let securityDefinitions = specSelectors.securityDefinitions()
let result = List()
@@ -59,20 +58,13 @@ 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)) {
+1 -4
View File
@@ -7,16 +7,13 @@ export default function downloadUrlPlugin (toolbox) {
let { fn } = toolbox
const actions = {
download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => {
download: (url)=> ({ errActions, specSelectors, specActions }) => {
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,*/*"
-8
View File
@@ -3,7 +3,6 @@ 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"
@@ -28,13 +27,6 @@ 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,
-10
View File
@@ -2,7 +2,6 @@ import {
NEW_THROWN_ERR,
NEW_THROWN_ERR_BATCH,
NEW_SPEC_ERR,
NEW_SPEC_ERR_BATCH,
NEW_AUTH_ERR,
CLEAR
} from "./actions"
@@ -46,15 +45,6 @@ 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))
+2 -2
View File
@@ -28,10 +28,10 @@ export function setRequestContentType ({ value, pathMethod }) {
}
}
export function setResponseContentType ({ value, path, method }) {
export function setResponseContentType ({ value, pathMethod }) {
return {
type: UPDATE_RESPONSE_CONTENT_TYPE,
payload: { value, path, method }
payload: { value, pathMethod }
}
}
@@ -1,60 +0,0 @@
import { createSelector } from "reselect"
import { List, Map, fromJS } from "immutable"
import { isOAS3 as isOAS3Helper } from "../helpers"
// Helpers
const state = state => state
function onlyOAS3(selector) {
return (ori, system) => (state, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(system, ...args)
} else {
return ori(...args)
}
}
}
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()
definitions.entrySeq().forEach( ([ defName, definition ]) => {
const type = definition.get("type")
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
}
))
+12 -12
View File
@@ -1,12 +1,10 @@
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 OperationContainer = getComponent("OperationContainer", true)
const Operation = getComponent("operation", true)
if(!callbacks) {
return <span>No callbacks</span>
@@ -18,22 +16,24 @@ const Callbacks = (props) => {
{ callback.map((pathItem, pathItemName) => {
return <div key={pathItemName}>
{ pathItem.map((operation, method) => {
let op = fromJS({
operation
})
return <OperationContainer
{...props}
op={op}
return <Operation
operation={operation}
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: ImPropTypes.iterable.isRequired
callbacks: PropTypes.array.isRequired
}
@@ -1,131 +0,0 @@
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>&nbsp;
(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>&nbsp;
(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,9 @@ 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"
export default {
Callbacks,
HttpAuth,
RequestBody,
Servers,
RequestBodyEditor,
@@ -48,13 +48,6 @@ 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))
}
@@ -6,7 +6,6 @@ import { OrderedMap } from "immutable"
const RequestBody = ({
requestBody,
getComponent,
getConfigs,
specSelectors,
contentType,
isExecute,
@@ -22,17 +21,12 @@ const RequestBody = ({
const mediaTypeValue = requestBodyContent.get(contentType)
if(!mediaTypeValue) {
return null
}
return <div>
{ requestBodyDescription &&
<Markdown source={requestBodyDescription} />
}
<ModelExample
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
expandDepth={1}
isExecute={isExecute}
@@ -52,9 +46,8 @@ const RequestBody = ({
RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string,
contentType: PropTypes.string.isRequired,
isExecute: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired
}
-4
View File
@@ -1,7 +1,6 @@
// import reducers from "./reducers"
// import * as actions from "./actions"
import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
import * as authWrapSelectors from "./auth-extensions/wrap-selectors"
import * as specSelectors from "./spec-extensions/selectors"
import components from "./components"
import wrapComponents from "./wrap-components"
@@ -18,9 +17,6 @@ export default function() {
wrapSelectors: specWrapSelectors,
selectors: specSelectors
},
auth: {
wrapSelectors: authWrapSelectors
},
oas3: {
actions: oas3Actions,
reducers: oas3Reducers,
+2 -1
View File
@@ -18,7 +18,8 @@ export default {
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
},
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, path, method } } ) =>{
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
},
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
@@ -48,15 +48,6 @@ 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
@@ -1,23 +0,0 @@
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,5 +1,4 @@
import Markdown from "./markdown"
import AuthItem from "./auth-item"
import parameters from "./parameters"
import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge"
@@ -7,7 +6,6 @@ import Model from "./model"
export default {
Markdown,
AuthItem,
parameters,
VersionStamp,
model: Model,
@@ -1,32 +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 const Markdown = ({ source }) => {
if ( source ) {
const parser = new Parser()
const writer = new HtmlRenderer()
const html = writer.render(parser.parse(source || ""))
const sanitized = sanitizer(html)
if ( !source || !html || !sanitized ) {
return null
}
return (
<ReactMarkdown
source={sanitized}
className={"renderedMarkdown"}
/>
)
}
return null
}
Markdown.propTypes = {
source: PropTypes.string
}
export default OAS3ComponentWrapFactory(Markdown)
export default OAS3ComponentWrapFactory(({ source }) => { return source ? (
<ReactMarkdown
source={sanitizer(source)}
className={"renderedMarkdown"}
/>
) : null})
@@ -8,13 +8,12 @@ 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 { getConfigs, schema } = this.props
let { schema } = this.props
let classes = ["model-box"]
let isDeprecated = schema.get("deprecated") === true
let message = null
@@ -27,7 +26,6 @@ class ModelComponent extends Component {
return <div className={classes.join(" ")}>
{message}
<Model { ...this.props }
getConfigs={ getConfigs }
depth={ 1 }
expandDepth={ this.props.expandDepth || 0 }
/>
@@ -22,7 +22,6 @@ 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,
@@ -50,7 +49,7 @@ class Parameters extends Component {
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
changeParam( onChangeKey, param.get("name"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
@@ -87,7 +86,6 @@ class Parameters extends Component {
fn,
getComponent,
getConfigs,
specSelectors,
oas3Actions,
oas3Selectors,
@@ -139,7 +137,6 @@ class Parameters extends Component {
eachMap(parameters, (parameter) => (
<ParameterRow fn={ fn }
getComponent={ getComponent }
getConfigs={ getConfigs }
param={ parameter }
key={ parameter.get( "name" ) }
onChange={ this.onChange }
+8 -22
View File
@@ -80,12 +80,7 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
}
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
const {
modelPropertyMacro,
parameterMacro,
requestInterceptor,
responseInterceptor
} = getConfigs()
const { modelPropertyMacro, parameterMacro } = getConfigs()
if(typeof(json) === "undefined") {
json = specSelectors.specJson()
@@ -98,15 +93,8 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
let specStr = specSelectors.specStr()
return resolve({
fetch,
spec: json,
baseDoc: url,
modelPropertyMacro,
parameterMacro,
requestInterceptor,
responseInterceptor
}).then( ({spec, errors}) => {
return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro })
.then( ({spec, errors}) => {
errActions.clear({
type: "thrown"
})
@@ -142,20 +130,17 @@ export const formatIntoYaml = () => ({specActions, specSelectors}) => {
}
}
export function changeParam( path, paramName, paramIn, value, isXml ){
export function changeParam( path, paramName, value, isXml ){
return {
type: UPDATE_PARAM,
payload:{ path, value, paramName, paramIn, isXml }
payload:{ path, value, paramName, isXml }
}
}
export const validateParams = ( payload, isOAS3 ) =>{
export function validateParams( payload ){
return {
type: VALIDATE_PARAMS,
payload:{
pathMethod: payload,
isOAS3
}
payload:{ pathMethod: payload }
}
}
@@ -221,6 +206,7 @@ export const executeRequest = (req) =>
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString()
if(op && op.operationId) {
req.operationId = op.operationId
} else if(op && pathName && method) {
+4 -5
View File
@@ -40,10 +40,9 @@ export default {
},
[UPDATE_PARAM]: ( state, {payload} ) => {
let { path, paramName, paramIn, value, isXml } = payload
let { path, paramName, value, isXml } = payload
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
const index = parameters.findIndex(p => p.get( "name" ) === paramName )
if (!(value instanceof win.File)) {
value = fromJSOrdered( value )
}
@@ -51,14 +50,14 @@ export default {
})
},
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
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, isOAS3)
let errors = validateParam(parameters.get(i), isXml)
parameters.setIn([i, "errors"], fromJS(errors))
}
})
+3 -3
View File
@@ -260,10 +260,10 @@ export const allowTryItOutFor = () => {
}
// Get the parameter value by parameter name
export function getParameter(state, pathMethod, name, inType) {
export function getParameter(state, pathMethod, name) {
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.filter( (p) => {
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
return Map.isMap(p) && p.get("name") === name
}).first()
}
@@ -280,7 +280,7 @@ export function parameterValues(state, pathMethod, isXml) {
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.reduce( (hash, p) => {
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
return hash.set(p.get("name"), value)
}, fromJS({}))
}
-4
View File
@@ -13,7 +13,3 @@ export const executeRequest = (ori, { specActions }) => (req) => {
specActions.logRequest(req)
return ori(req)
}
export const validateParams = (ori, { specSelectors }) => (req) => {
return ori(req, specSelectors.isOAS3())
}
+2 -8
View File
@@ -20,14 +20,8 @@ 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( mapStateToProps )(wrappedWithSystem)
let connected = connect(state => ({state}))(wrappedWithSystem)
if(reduxStore)
return RootWrapper(reduxStore, connected)
return connected
@@ -120,5 +114,5 @@ export const getComponent = (getSystem, getStore, getComponents, componentName,
return makeContainer(getSystem, component, getStore())
// container == truthy
return makeContainer(getSystem, wrapRender(component))
return makeContainer(getSystem, component)
}
+1 -16
View File
@@ -10,17 +10,13 @@ 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"
@@ -30,14 +26,11 @@ 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"
@@ -58,7 +51,6 @@ 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"
@@ -78,7 +70,6 @@ export default function() {
authorizeBtn: AuthorizeBtn,
authorizeOperationBtn: AuthorizeOperationBtn,
auths: Auths,
AuthItem: AuthItem,
authError: AuthError,
oauth2: Oauth2,
apiKeyAuth: ApiKeyAuth,
@@ -113,15 +104,10 @@ export default function() {
ObjectModel,
ArrayModel,
PrimitiveModel,
Property,
TryItOutButton,
Markdown,
BaseLayout,
VersionStamp,
OperationExt,
OperationExtRow,
ParameterExt,
OperationContainer
VersionStamp
}
}
@@ -134,7 +120,6 @@ export default function() {
}
return [
configsPlugin,
util,
logs,
view,
+2 -1
View File
@@ -289,7 +289,8 @@ export default class Store {
getMapStateToProps() {
return () => {
return Object.assign({}, this.getSystem())
let obj = Object.assign({}, this.getSystem())
return obj
}
}
+87 -104
View File
@@ -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,6 +155,83 @@ 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>
@@ -393,18 +470,6 @@ export const propChecker = (props, nextProps, objectList=[], ignoreList=[]) => {
|| objectList.some( objectPropName => !eq(props[objectPropName], nextProps[objectPropName])))
}
export const validateMaximum = ( val, max ) => {
if (val > max) {
return "Value must be less than Maximum"
}
}
export const validateMinimum = ( val, min ) => {
if (val < min) {
return "Value must be greater than Minimum"
}
}
export const validateNumber = ( val ) => {
if (!/^-?\d+(\.?\d+)?$/.test(val)) {
return "Value must be a number"
@@ -435,52 +500,12 @@ export const validateString = ( val ) => {
}
}
export const validateDateTime = (val) => {
if (isNaN(Date.parse(val))) {
return "Value must be a DateTime"
}
}
export const validateGuid = (val) => {
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
return "Value must be a Guid"
}
}
export const validateMaxLength = (val, max) => {
if (val.length > max) {
return "Value must be less than MaxLength"
}
}
export const validateMinLength = (val, min) => {
if (val.length < min) {
return "Value must be greater than MinLength"
}
}
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, isOAS3 = false) => {
export const validateParam = (param, isXml) => {
let errors = []
let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value")
let required = param.get("required")
let paramDetails = isOAS3 ? param.get("schema") : param
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")
let type = param.get("type")
/*
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
@@ -488,54 +513,22 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
Only bother validating the parameter if the type was specified.
*/
if ( type && (required || value) ) {
// These checks should evaluate to true if there is a parameter
let stringCheck = type === "string" && value
// These checks should evaluate to true if the parameter's value is valid
let stringCheck = type === "string" && value && !validateString(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" && (value || value === false)
let numberCheck = type === "number" && (value || value === 0)
let integerCheck = type === "integer" && (value || value === 0)
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
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)
if (err) errors.push(err)
}
if (minLength) {
let err = validateMinLength(value, minLength)
if (err) errors.push(err)
}
if (maximum || maximum === 0) {
let err = validateMaximum(value, maximum)
if (err) errors.push(err)
}
if (minimum || minimum === 0) {
let err = validateMinimum(value, minimum)
if (err) errors.push(err)
}
if ( type === "string" ) {
let err
if (format === "date-time") {
err = validateDateTime(value)
} else if (format === "uuid") {
err = validateGuid(value)
} else {
err = validateString(value)
}
let err = validateString(value)
if (!err) return errors
errors.push(err)
} else if ( type === "boolean" ) {
@@ -555,7 +548,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
if ( !value.count() ) { return errors }
itemType = paramDetails.getIn(["items", "type"])
itemType = param.getIn(["items", "type"])
value.forEach((item, index) => {
let err
@@ -659,14 +652,6 @@ 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!
@@ -692,5 +677,3 @@ 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 -1
View File
@@ -1,6 +1,6 @@
# Add a plugin
### Swagger-UI relies on plugins for all the good stuff.
### Swagger-UX relies on plugins for all the good stuff.
Plugins allow you to add
- `statePlugins`
-20
View File
@@ -1,20 +0,0 @@
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,
}
}
+41 -51
View File
@@ -1,66 +1,56 @@
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) )
try {
return YAML.safeLoad(yaml)
} catch(e) {
if (system) {
system.errActions.newThrownErr( new Error(e) )
}
return {}
}
return {}
}
}
const specActions = {
downloadConfig: (url) => ({fn}) => {
let {fetch} = fn
return fetch(url)
},
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))
}
}
}
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 selectors = {
getLocalConfig: () => {
return parseYamlConfig(yamlConfig)
}
}
}
}
const specSelectors = {
getLocalConfig: () => {
return parseYamlConfig(yamlConfig)
}
}
export default function configsPlugin() {
return {
statePlugins: {
spec: {
actions: specActions,
selectors: specSelectors,
},
configs: {
reducers,
actions,
selectors,
}
return {
statePlugins: {
spec: { actions, selectors }
}
}
}
}
-20
View File
@@ -1,20 +0,0 @@
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
View File
@@ -1,4 +0,0 @@
// 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])
}
+3 -3
View File
@@ -1,4 +1,4 @@
import React, { cloneElement } from "react"
import React 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="#">
<Link href="#" title="Swagger UX">
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
<span>swagger</span>
</Link>
<form className="download-url-wrapper" onSubmit={formOnSubmit}>
{control.map((el, i) => cloneElement(el, { key: i }))}
{control}
</form>
</div>
</div>
-5
View File
@@ -3,8 +3,3 @@
if(!window.Promise) {
require("core-js/fn/promise")
}
// Required by IE 11
if(!String.prototype.startsWith) {
require("core-js/es6/string")
}
+1 -5
View File
@@ -5,10 +5,6 @@
padding: 10px 0;
justify-content: center;
.btn-done {
margin-right: 1em;
}
}
.auth-wrapper
@@ -29,7 +25,7 @@
margin: 0 0 10px 0;
padding: 10px 20px;
border-bottom: 1px solid $auth-container-border-color;
border-bottom: 1px solid #ebebeb;
&:last-of-type
{
+24 -23
View File
@@ -7,10 +7,10 @@
transition: all .3s;
border: 2px solid $btn-border-color;
border: 2px solid #888;
border-radius: 4px;
background: transparent;
box-shadow: 0 1px 2px rgba($btn-box-shadow-color,.1);
box-shadow: 0 1px 2px rgba(#000,.1);
@include text_headline();
@@ -29,14 +29,14 @@
&:hover
{
box-shadow: 0 0 5px rgba($btn-box-shadow-color,.3);
box-shadow: 0 0 5px rgba(#000,.3);
}
&.cancel
{
border-color: $btn-cancel-border-color;
background-color: $btn-cancel-background-color;
@include text_headline($btn-cancel-font-color);
border-color: #ff6060;
@include text_headline(#ff6060);
}
&.authorize
@@ -45,9 +45,9 @@
display: inline;
color: $btn-authorize-font-color;
border-color: $btn-authorize-border-color;
background-color: $btn-authorize-background-color;
color: $_color-post;
border-color: $_color-post;
span
{
@@ -58,17 +58,16 @@
svg
{
fill: $btn-authorize-svg-fill-color;
fill: $_color-post;
}
}
&.execute
{
animation: swagger-ui-pulse 2s infinite;
will-change: transform;
background-color: $btn-execute-background-color;
color: $btn-execute-font-color;
border-color: $btn-execute-border-color;
color: #fff;
border-color: #4990e2;
}
}
@@ -77,19 +76,21 @@
{
0%
{
color: $btn-execute-font-color;
background: $btn-execute-background-color-alt;
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, .8);
color: #fff;
background: #4990e2;
box-shadow: 0 0 0 0 rgba(#4990e2, .8);
}
70%
{
box-shadow: 0 0 0 5px rgba($btn-execute-background-color-alt, 0);
//color: #4990e2;
//background: transparent;
box-shadow: 0 0 0 5px rgba(#4990e2, 0);
}
100%
{
color: $btn-execute-font-color;
background: $btn-execute-background-color-alt;
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, 0);
color: #fff;
background: #4990e2;
box-shadow: 0 0 0 0 rgba(#4990e2, 0);
}
}
@@ -154,7 +155,7 @@
{
svg
{
fill: $expand-methods-svg-fill-color-hover;
fill: #444;
}
}
@@ -162,7 +163,7 @@
{
transition: all .3s;
fill: $expand-methods-svg-fill-color;
fill: #777;
}
}
+1 -1
View File
@@ -27,7 +27,7 @@
small
{
color: $errors-wrapper-errors-small-font-color;
color: #666;
}
}

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