Merge branch 'master' into docker-run.sh-eol-lf

This commit is contained in:
Ron
2017-06-08 12:49:43 -07:00
committed by GitHub
49 changed files with 7478 additions and 212 deletions

View File

@@ -22,7 +22,7 @@
"rules": { "rules": {
"semi": [2, "never"], "semi": [2, "never"],
"strict": 0, "strict": 0,
"quotes": 2, "quotes": [2, "double", { "allowTemplateLiterals": true }],
"no-unused-vars": 2, "no-unused-vars": 2,
"no-multi-spaces": 1, "no-multi-spaces": 1,
"camelcase": 1, "camelcase": 1,

View File

@@ -15,7 +15,7 @@ The OpenAPI Specification has undergone 4 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes | Status Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes | Status
------------------ | ------------ | -------------------------- | ----- | ------ ------------------ | ------------ | -------------------------- | ----- | ------
3.0.9 | 2017-03-19 | 2.0 | [tag v3.0.9](https://github.com/swagger-api/swagger-ui/tree/v3.0.9) | 3.0.13 | 2017-06-02 | 2.0 | [tag v3.0.13](https://github.com/swagger-api/swagger-ui/tree/v3.0.13) |
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.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) | 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) |
2.0.24 | 2014-09-12 | 1.1, 1.2 | [tag v2.0.24](https://github.com/swagger-api/swagger-ui/tree/v2.0.24) | 2.0.24 | 2014-09-12 | 1.1, 1.2 | [tag v2.0.24](https://github.com/swagger-api/swagger-ui/tree/v2.0.24) |
@@ -41,7 +41,9 @@ Will start nginx with swagger-ui on port 80.
If you just want to see your specs, open `dist/index.html` in your browser directly from your filesystem. If you just want to see your specs, open `dist/index.html` in your browser directly from your filesystem.
If you'd like to make modifications to the codebase, run the dev server with: `npm run dev`. If you'd like to make modifications to the codebase, run the dev server with: `npm run dev`. A development server will open on `3200`.
If you'd like to rebuild the `/dist` folder with your codebase changes, run `npm run build`.
##### Browser support ##### Browser support
Swagger UI works in the latest versions of Chrome, Safari, Firefox, Edge and IE11. Swagger UI works in the latest versions of Chrome, Safari, Firefox, Edge and IE11.
@@ -75,6 +77,33 @@ To use swagger-ui's bundles, you should take a look at the [source of swagger-ui
}) })
``` ```
#### OAuth2 configuration
You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle`
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`.
Config Name | Description
--- | ---
client_id | Default clientId. MUST be a string
client_secret | Default clientSecret. MUST be a string
realm | realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl` . MUST be a string
appName | application name, displayed in authorization popup. MUST be a string
scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
```
const ui = SwaggerUIBundle({...})
// Method can be called in any place after calling constructor SwaggerUIBundle
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {test: "hello"}
})
```
If you'd like to use the bundle files via npm, check out the [`swagger-ui-dist` package](https://www.npmjs.com/package/swagger-ui-dist). If you'd like to use the bundle files via npm, check out the [`swagger-ui-dist` package](https://www.npmjs.com/package/swagger-ui-dist).
#### Parameters #### Parameters
@@ -88,6 +117,10 @@ dom_id | The id of a dom element inside which SwaggerUi will put the user interf
oauth2RedirectUrl | OAuth redirect URL oauth2RedirectUrl | OAuth redirect URL
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. 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.
configUrl | Configs URL configUrl | Configs URL
parameterMacro | MUST be a function. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'.
displayOperationId | Controls the display of operationId in operations list. The default is `false`.
### Plugins ### Plugins
@@ -109,7 +142,7 @@ let preset = [
#### Configs plugin #### Configs plugin
Configs plugin allows to fetch external configs instead of passing them to `SwaggerUIBundle`. Fetched configs support two formats: JSON or yaml. The plugin is enabled by default. Configs plugin allows to fetch external configs instead of passing them to `SwaggerUIBundle`. Fetched configs support two formats: JSON or yaml. The plugin is enabled by default.
There are three options of passing config: There are three options of passing config:
- add a query parameter `config` with URL to a server where the configs are hosted. For ex. http://petstore.swagger.io/?configs=http://localhost:3001/config.yaml - add a query parameter `config` with URL to a server where the configs are hosted. For ex. http://petstore.swagger.io/?config=http://localhost:3001/config.yaml
- add a config `configUrl` with URL to SwaggerUIBundle - add a config `configUrl` with URL to SwaggerUIBundle
- change default configs in `swagger-config.yaml` *Note: after changing, the project must be re-built* - change default configs in `swagger-config.yaml` *Note: after changing, the project must be re-built*

View File

@@ -88,6 +88,15 @@ window.onload = function() {
}) })
window.ui = ui window.ui = ui
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
} }
</script> </script>
</body> </body>

View File

@@ -8,6 +8,7 @@
function run () { function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2; var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state; var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr; var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1); qp = (window.location.hash || location.search).substring(1);
@@ -35,7 +36,7 @@
if (qp.code) { if (qp.code) {
delete oauth2.state; delete oauth2.state;
oauth2.auth.code = qp.code; oauth2.auth.code = qp.code;
oauth2.callback(oauth2.auth); oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else { } else {
oauth2.errCb({ oauth2.errCb({
authId: oauth2.auth.name, authId: oauth2.auth.name,
@@ -45,9 +46,8 @@
}); });
} }
} else { } else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid}); oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
} }
window.close(); window.close();
} }
</script> </script>

View File

@@ -8,6 +8,7 @@
function run () { function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2; var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state; var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr; var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1); qp = (window.location.hash || location.search).substring(1);
@@ -35,7 +36,7 @@
if (qp.code) { if (qp.code) {
delete oauth2.state; delete oauth2.state;
oauth2.auth.code = qp.code; oauth2.auth.code = qp.code;
oauth2.callback(oauth2.auth); oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else { } else {
oauth2.errCb({ oauth2.errCb({
authId: oauth2.auth.name, authId: oauth2.auth.name,
@@ -45,9 +46,8 @@
}); });
} }
} else { } else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid}); oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
} }
window.close(); window.close();
} }
</script> </script>

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;AAu/FA;AA6+FA;;;;;;;;;;;;;;;;;;;;;;;;;;AAieA;AAkoJA;AAwiCA;AAw8GA;AAw5HA;AAkvGA;AA86EA;AA0qDA;AAq/CA;AAqjDA;AAk/CA;;;;;AAw2CA;AAmwJA;;;;;;;;;;;;;;AA8sEA;AAyoIA;AAiuJA;AA8kHA;AAonGA;AAukEA;AA02DA;AAs3EA;AA+vGA;;;;;;AA2gFA;AAo3FA;;;;;AAk6CA;AA2qFA;AAw2CA;AAqkCA;AA++CA;AA+wEA;AAy8FA;;;;;;;;;AA23BA;AA2zIA;AAg4DA;AAulDA;;;;;;AA4kCA;AA8iHA;AAipGA","sourceRoot":""} {"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;AAu/FA;AA6+FA;;;;;;;;;;;;;;;;;;;;;;;;;;AAweA;AAkoJA;AAwiCA;AAo9GA;AAw5HA;AAkvGA;AA47EA;AAmqDA;AAk/CA;AA+jDA;AAk/CA;;;;;AAs2CA;AAmwJA;;;;;;;;;;;;;;AA8sEA;AAyoIA;AAiuJA;AA8kHA;AAonGA;AAukEA;AA02DA;AA65EA;AAy/FA;;;;;;AAo5FA;AAk7FA;;;;;AAy/CA;AA2qFA;AAs2CA;AA+kCA;AAg8CA;AAyxDA;AA27CA;AA28FA;;;;;;;;;AAu4BA;AA2zIA;AAu7FA;AA8rFA;AA20EA;;;;;;AA4kCA;AA8iHA;AAipGA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA+PA;AAyiGA;AAiwFA;;;;;;AAkeA;AAkvFA;AAu+CA;AAo+CA;AAgrCA;AAgyEA","sourceRoot":""} {"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA+PA;AAyiGA;AAwxFA;;;;;;AA0bA;AAkvFA;AAu+CA;AAo+CA;AAgrCA;AAgyEA","sourceRoot":""}

2
dist/swagger-ui.css vendored

File diff suppressed because one or more lines are too long

18
dist/swagger-ui.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAyxCA;AAoyHA;AAqxHA;AAy4FA;AA2sCA;AAmgCA;AA0iCA;AA64BA","sourceRoot":""} {"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA4yCA;AAoyHA;AAmyHA;AAukGA;AA+9BA;AA2jCA;AA0iCA;AAy4BA","sourceRoot":""}

View File

@@ -9,7 +9,17 @@ const {gitDescribeSync} = require('git-describe');
var loadersByExtension = require('./build-tools/loadersByExtension') var loadersByExtension = require('./build-tools/loadersByExtension')
var pkg = require('./package.json') var pkg = require('./package.json')
const gitInfo = gitDescribeSync(__dirname)
let gitInfo
try {
gitInfo = gitDescribeSync(__dirname)
} catch(e) {
gitInfo = {
hash: 'noGit',
dirty: false
}
}
module.exports = function(options) { module.exports = function(options) {

7033
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "swagger-ui", "name": "swagger-ui",
"version": "3.0.9", "version": "3.0.13",
"main": "dist/swagger-ui.js", "main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git", "repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [ "contributors": [
@@ -19,7 +19,7 @@
"build-core": "webpack --config webpack-dist.config.js --colors", "build-core": "webpack --config webpack-dist.config.js --colors",
"build-standalone": "webpack --config webpack-dist-standalone.config.js --colors", "build-standalone": "webpack --config webpack-dist-standalone.config.js --colors",
"predev": "npm install", "predev": "npm install",
"dev": "npm-run-all --parallel hot-server watch open-localhost", "dev": "npm-run-all --parallel hot-server open-localhost",
"watch": "webpack --config webpack-watch.config.js --watch --progress", "watch": "webpack --config webpack-watch.config.js --watch --progress",
"open-localhost": "node -e 'require(\"open\")(\"http://localhost:3200\")'", "open-localhost": "node -e 'require(\"open\")(\"http://localhost:3200\")'",
"hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/", "hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/",
@@ -32,7 +32,7 @@
"test": "npm run lint-errors && npm run just-test-in-node", "test": "npm run lint-errors && npm run just-test-in-node",
"test-in-node": "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": "karma start --config karma.conf.js",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components" "just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.23.0", "babel-polyfill": "^6.23.0",
@@ -67,9 +67,10 @@
"redux-immutable": "3.0.8", "redux-immutable": "3.0.8",
"redux-logger": "*", "redux-logger": "*",
"reselect": "2.5.3", "reselect": "2.5.3",
"sanitize-html": "^1.14.1",
"serialize-error": "2.0.0", "serialize-error": "2.0.0",
"shallowequal": "0.2.2", "shallowequal": "0.2.2",
"swagger-client": "^3.0.9", "swagger-client": "~3.0.13",
"url-parse": "^1.1.8", "url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1", "whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1", "worker-loader": "^0.7.1",

View File

@@ -53,8 +53,7 @@ export default class ApiKeyAuth extends React.Component {
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4> <h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ value && <h6>Authorized</h6>} { value && <h6>Authorized</h6>}
<Row> <Row>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} <Markdown source={ schema.get("description") } />
source={ schema.get("description") } />
</Row> </Row>
<Row> <Row>
<p>Name: <code>{ schema.get("name") }</code></p> <p>Name: <code>{ schema.get("name") }</code></p>

View File

@@ -59,8 +59,7 @@ export default class BasicAuth extends React.Component {
<h4>Basic authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4> <h4>Basic authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ username && <h6>Authorized</h6> } { username && <h6>Authorized</h6> }
<Row> <Row>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} <Markdown source={ schema.get("description") } />
source={ schema.get("description") } />
</Row> </Row>
<Row> <Row>
<label>Username:</label> <label>Username:</label>

View File

@@ -21,14 +21,16 @@ export default class Oauth2 extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context) super(props, context)
let { name, schema, authorized } = this.props let { name, schema, authorized, authSelectors } = this.props
let auth = authorized && authorized.get(name) let auth = authorized && authorized.get(name)
let authConfigs = authSelectors.getConfigs() || {}
let username = auth && auth.get("username") || "" let username = auth && auth.get("username") || ""
let clientId = auth && auth.get("clientId") || "" let clientId = auth && auth.get("clientId") || authConfigs.clientId || ""
let clientSecret = auth && auth.get("clientSecret") || "" let clientSecret = auth && auth.get("clientSecret") || authConfigs.clientSecret || ""
let passwordType = auth && auth.get("passwordType") || "basic" let passwordType = auth && auth.get("passwordType") || "request-body"
this.state = { this.state = {
appName: authConfigs.appName,
name: name, name: name,
schema: schema, schema: schema,
scopes: [], scopes: [],
@@ -41,11 +43,12 @@ export default class Oauth2 extends React.Component {
} }
authorize =() => { authorize =() => {
let { authActions, errActions, getConfigs } = this.props let { authActions, errActions, getConfigs, authSelectors } = this.props
let configs = getConfigs() let configs = getConfigs()
let authConfigs = authSelectors.getConfigs()
errActions.clear({authId: name,type: "auth", source: "auth"}) errActions.clear({authId: name,type: "auth", source: "auth"})
oauth2Authorize(this.state, authActions, errActions, configs) oauth2Authorize({auth: this.state, authActions, errActions, configs, authConfigs })
} }
onScopeChange =(e) => { onScopeChange =(e) => {
@@ -94,12 +97,13 @@ export default class Oauth2 extends React.Component {
let isAuthorized = !!authorizedAuth let isAuthorized = !!authorizedAuth
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name) let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
let isValid = !errors.filter( err => err.get("source") === "validation").size let isValid = !errors.filter( err => err.get("source") === "validation").size
let description = schema.get("description")
return ( return (
<div> <div>
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4> <h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} { !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
source={ schema.get("description") } /> { description && <Markdown source={ schema.get("description") } /> }
{ isAuthorized && <h6>Authorized</h6> } { isAuthorized && <h6>Authorized</h6> }
@@ -137,8 +141,8 @@ export default class Oauth2 extends React.Component {
isAuthorized ? <code> { this.state.passwordType } </code> isAuthorized ? <code> { this.state.passwordType } </code>
: <Col tablet={10} desktop={10}> : <Col tablet={10} desktop={10}>
<select id="password_type" data-name="passwordType" onChange={ this.onInputChange }> <select id="password_type" data-name="passwordType" onChange={ this.onInputChange }>
<option value="basic">Basic auth</option>
<option value="request-body">Request body</option> <option value="request-body">Request body</option>
<option value="basic">Basic auth</option>
<option value="query">Query parameters</option> <option value="query">Query parameters</option>
</select> </select>
</Col> </Col>
@@ -153,7 +157,11 @@ export default class Oauth2 extends React.Component {
{ {
isAuthorized ? <code> ****** </code> isAuthorized ? <code> ****** </code>
: <Col tablet={10} desktop={10}> : <Col tablet={10} desktop={10}>
<input id="client_id" type="text" required={ flow === PASSWORD } data-name="clientId" <input id="client_id"
type="text"
required={ flow === PASSWORD }
value={ this.state.clientId }
data-name="clientId"
onChange={ this.onInputChange }/> onChange={ this.onInputChange }/>
</Col> </Col>
} }
@@ -166,7 +174,10 @@ export default class Oauth2 extends React.Component {
{ {
isAuthorized ? <code> ****** </code> isAuthorized ? <code> ****** </code>
: <Col tablet={10} desktop={10}> : <Col tablet={10} desktop={10}>
<input id="client_secret" type="text" data-name="clientSecret" <input id="client_secret"
value={ this.state.clientSecret }
type="text"
data-name="clientSecret"
onChange={ this.onInputChange }/> onChange={ this.onInputChange }/>
</Col> </Col>
} }

View File

@@ -19,7 +19,7 @@ export default class Curl extends React.Component {
<div> <div>
<h4>Curl</h4> <h4>Curl</h4>
<div className="copy-paste"> <div className="copy-paste">
<textarea onFocus={this.handleFocus} readOnly="true" className="curl" style={{ whiteSpace: "normal" }} value={curl}></textarea> <textarea onFocus={this.handleFocus} className="curl" style={{ whiteSpace: "normal" }} value={curl}></textarea>
</div> </div>
</div> </div>
) )

View File

@@ -5,14 +5,18 @@ import Collapse from "react-collapse"
export default class Errors extends React.Component { export default class Errors extends React.Component {
static propTypes = { static propTypes = {
jumpToLine: PropTypes.func, editorActions: PropTypes.object,
errSelectors: PropTypes.object.isRequired, errSelectors: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired layoutActions: PropTypes.object.isRequired
} }
render() { render() {
let { jumpToLine, errSelectors, layoutSelectors, layoutActions } = this.props let { editorActions, errSelectors, layoutSelectors, layoutActions } = this.props
if(editorActions && editorActions.jumpToLine) {
var jumpToLine = editorActions.jumpToLine
}
let errors = errSelectors.allErrors() let errors = errSelectors.allErrors()
@@ -37,10 +41,11 @@ export default class Errors extends React.Component {
<Collapse isOpened={ isVisible } animated > <Collapse isOpened={ isVisible } animated >
<div className="errors"> <div className="errors">
{ sortedJSErrors.map((err, i) => { { sortedJSErrors.map((err, i) => {
if(err.get("type") === "thrown") { let type = err.get("type")
if(type === "thrown" || type === "auth") {
return <ThrownErrorItem key={ i } error={ err.get("error") || err } jumpToLine={jumpToLine} /> return <ThrownErrorItem key={ i } error={ err.get("error") || err } jumpToLine={jumpToLine} />
} }
if(err.get("type") === "spec") { if(type === "spec") {
return <SpecErrorItem key={ i } error={ err } jumpToLine={jumpToLine} /> return <SpecErrorItem key={ i } error={ err } jumpToLine={jumpToLine} />
} }
}) } }) }
@@ -95,7 +100,7 @@ const SpecErrorItem = ( { error, jumpToLine } ) => {
<div> <div>
<h4>{ toTitleCase(error.get("source")) + " " + error.get("level") }&nbsp;{ locationMessage }</h4> <h4>{ toTitleCase(error.get("source")) + " " + error.get("level") }&nbsp;{ locationMessage }</h4>
<span style={{ whiteSpace: "pre-line"}}>{ error.get("message") }</span> <span style={{ whiteSpace: "pre-line"}}>{ error.get("message") }</span>
<div> <div style={{ "text-decoration": "underline", "cursor": "pointer" }}>
{ jumpToLine ? ( { jumpToLine ? (
<a onClick={jumpToLine.bind(null, error.get("line"))}>Jump to line { error.get("line") }</a> <a onClick={jumpToLine.bind(null, error.get("line"))}>Jump to line { error.get("line") }</a>
) : null } ) : null }

View File

@@ -99,7 +99,7 @@ export default class Info extends React.Component {
</hgroup> </hgroup>
<div className="description"> <div className="description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ description } /> <Markdown source={ description } />
</div> </div>
{ {

View File

@@ -1,13 +1,10 @@
import React, { PropTypes } from "react" import React, { PropTypes } from "react"
import OriCollapse from "react-collapse" import OriCollapse from "react-collapse"
import _Markdown from "react-remarkable"
function xclass(...args) { function xclass(...args) {
return args.filter(a => !!a).join(" ").trim() return args.filter(a => !!a).join(" ").trim()
} }
export const Markdown = _Markdown
export class Container extends React.Component { export class Container extends React.Component {
render() { render() {
let { fullscreen, full, ...rest } = this.props let { fullscreen, full, ...rest } = this.props
@@ -73,7 +70,7 @@ export class Col extends React.Component {
} }
} }
let classes = xclass(rest.className, "clear", ...classesAr) let classes = xclass(rest.className, ...classesAr)
return ( return (
<section {...rest} style={{display: hide ? "none": null}} className={classes}/> <section {...rest} style={{display: hide ? "none": null}} className={classes}/>

View File

@@ -6,13 +6,15 @@ export default class Models extends Component {
getComponent: PropTypes.func, getComponent: PropTypes.func,
specSelectors: PropTypes.object, specSelectors: PropTypes.object,
layoutSelectors: PropTypes.object, layoutSelectors: PropTypes.object,
layoutActions: PropTypes.object layoutActions: PropTypes.object,
getConfigs: PropTypes.func.isRequired
} }
render(){ render(){
let { specSelectors, getComponent, layoutSelectors, layoutActions } = this.props let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
let definitions = specSelectors.definitions() let definitions = specSelectors.definitions()
let showModels = layoutSelectors.isShown("models", true) let { docExpansion } = getConfigs()
let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" )
const Model = getComponent("model") const Model = getComponent("model")
const Collapse = getComponent("Collapse") const Collapse = getComponent("Collapse")

View File

@@ -17,6 +17,8 @@ export default class Operation extends React.Component {
allowTryItOut: PropTypes.bool, allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool,
response: PropTypes.object, response: PropTypes.object,
request: PropTypes.object, request: PropTypes.object,
@@ -27,13 +29,15 @@ export default class Operation extends React.Component {
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
} }
static defaultProps = { static defaultProps = {
showSummary: true, showSummary: true,
response: null, response: null,
allowTryItOut: true, allowTryItOut: true,
displayOperationId: false,
} }
constructor(props, context) { constructor(props, context) {
@@ -76,8 +80,10 @@ export default class Operation extends React.Component {
} }
isShown =() => { isShown =() => {
let { layoutSelectors, isShownKey } = this.props let { layoutSelectors, isShownKey, getConfigs } = this.props
return layoutSelectors.isShown(isShownKey, false ) // Here is where we set the default let { docExpansion } = getConfigs()
return layoutSelectors.isShown(isShownKey, docExpansion === "full" ) // Here is where we set the default
} }
onTryoutClick =() => { onTryoutClick =() => {
@@ -105,6 +111,7 @@ export default class Operation extends React.Component {
response, response,
request, request,
allowTryItOut, allowTryItOut,
displayOperationId,
fn, fn,
getComponent, getComponent,
@@ -123,6 +130,7 @@ export default class Operation extends React.Component {
let produces = operation.get("produces") let produces = operation.get("produces")
let schemes = operation.get("schemes") let schemes = operation.get("schemes")
let parameters = getList(operation, ["parameters"]) let parameters = getList(operation, ["parameters"])
let operationId = operation.get("__originalOperationId")
const Responses = getComponent("responses") const Responses = getComponent("responses")
const Parameters = getComponent( "parameters" ) const Parameters = getComponent( "parameters" )
@@ -159,6 +167,8 @@ export default class Operation extends React.Component {
</div> </div>
} }
{ displayOperationId && operationId ? <span className="opblock-summary-operation-id">{operationId}</span> : null }
{ {
(!security || !security.count()) ? null : (!security || !security.count()) ? null :
<AuthorizeOperationBtn authActions={ authActions } <AuthorizeOperationBtn authActions={ authActions }
@@ -173,7 +183,7 @@ export default class Operation extends React.Component {
{ description && { description &&
<div className="opblock-description-wrapper"> <div className="opblock-description-wrapper">
<div className="opblock-description"> <div className="opblock-description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ description } /> <Markdown source={ description } />
</div> </div>
</div> </div>
} }

View File

@@ -10,10 +10,7 @@ export default class Operations extends React.Component {
layoutActions: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired, authActions: PropTypes.object.isRequired,
authSelectors: PropTypes.object.isRequired, authSelectors: PropTypes.object.isRequired,
}; getConfigs: PropTypes.func.isRequired
static defaultProps = {
}; };
render() { render() {
@@ -25,6 +22,7 @@ export default class Operations extends React.Component {
layoutActions, layoutActions,
authActions, authActions,
authSelectors, authSelectors,
getConfigs,
fn fn
} = this.props } = this.props
@@ -34,6 +32,7 @@ export default class Operations extends React.Component {
const Collapse = getComponent("Collapse") const Collapse = getComponent("Collapse")
let showSummary = layoutSelectors.showSummary() let showSummary = layoutSelectors.showSummary()
let { docExpansion, displayOperationId } = getConfigs()
return ( return (
<div> <div>
@@ -43,16 +42,15 @@ export default class Operations extends React.Component {
let tagDescription = tagObj.getIn(["tagDetails", "description"], null) let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let isShownKey = ["operations-tag", tag] let isShownKey = ["operations-tag", tag]
let showTag = layoutSelectors.isShown(isShownKey, true) let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
return ( return (
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}> <div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
<h4 className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }> <h4 onClick={() => layoutActions.show(isShownKey, !showTag)} className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }>
<span onClick={() => layoutActions.show(isShownKey, !showTag)}>{tag}</span> <span>{tag}</span>
{ !tagDescription ? null : { !tagDescription ? null :
<small onClick={() => layoutActions.show(isShownKey, !showTag)} > <small>
{ tagDescription } { tagDescription }
</small> </small>
} }
@@ -88,6 +86,8 @@ export default class Operations extends React.Component {
request={ request } request={ request }
allowTryItOut={allowTryItOut} allowTryItOut={allowTryItOut}
displayOperationId={displayOperationId}
specActions={ specActions } specActions={ specActions }
specSelectors={ specSelectors } specSelectors={ specSelectors }
@@ -99,6 +99,7 @@ export default class Operations extends React.Component {
getComponent={ getComponent } getComponent={ getComponent }
fn={fn} fn={fn}
getConfigs={ getConfigs }
/> />
}).toArray() }).toArray()
} }

View File

@@ -99,8 +99,7 @@ export default class ParameterRow extends Component {
</td> </td>
<td className="col parameters-col_description"> <td className="col parameters-col_description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} <Markdown source={ param.get("description") }/>
source={ param.get("description") }/>
{(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>} {(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>}
{ bodyParam || !isExecute ? null { bodyParam || !isExecute ? null

View File

@@ -0,0 +1,6 @@
# Providers
Providers are generic bridges to third-party components. They provide two benefits:
1. ability for plugins to override third-party components, because providers are loaded through `getComponent`
2. allows us to avoid painting ourselves into a corner with a third-party component

View File

@@ -0,0 +1,24 @@
import React, { PropTypes } from "react"
import Remarkable from "react-remarkable"
import sanitize from "sanitize-html"
const sanitizeOptions = {
textFilter: function(text) {
return text
.replace(/&quot;/g, "\"")
}
}
function Markdown({ source }) {
const sanitized = sanitize(source, sanitizeOptions)
return <Remarkable
options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
source={sanitized}
></Remarkable>
}
Markdown.propTypes = {
source: PropTypes.string.isRequired
}
export default Markdown

View File

@@ -76,7 +76,7 @@ export default class Response extends React.Component {
<td className="col response-col_description"> <td className="col response-col_description">
<div className="response-col_description__inner"> <div className="response-col_description__inner">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ response.get( "description" ) } /> <Markdown source={ response.get( "description" ) } />
</div> </div>
{ example ? ( { example ? (

View File

@@ -7,8 +7,8 @@ import * as AllPlugins from "core/plugins/all"
import { parseSeach, filterConfigs } from "core/utils" import { parseSeach, filterConfigs } from "core/utils"
const CONFIGS = [ "url", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", const CONFIGS = [ "url", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
"apisSorter", "operationsSorter", "supportedSubmitMethods", "highlightSizeThreshold", "dom_id", "apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
"defaultModelRendering", "oauth2RedirectUrl", "showRequestHeaders" ] "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" ]
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo
@@ -24,9 +24,11 @@ module.exports = function SwaggerUI(opts) {
spec: {}, spec: {},
url: "", url: "",
layout: "BaseLayout", layout: "BaseLayout",
docExpansion: "list",
validatorUrl: "https://online.swagger.io/validator", validatorUrl: "https://online.swagger.io/validator",
configs: { configs: {},
}, custom: {},
displayOperationId: false,
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance. // 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. // Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
@@ -78,6 +80,8 @@ module.exports = function SwaggerUI(opts) {
var system = store.getSystem() var system = store.getSystem()
let queryConfig = parseSeach() let queryConfig = parseSeach()
system.initOAuth = system.authActions.configureAuth
const downloadSpec = (fetchedConfig) => { const downloadSpec = (fetchedConfig) => {
if(typeof constructorConfig !== "object") { if(typeof constructorConfig !== "object") {
return system return system

View File

@@ -69,7 +69,7 @@ export class JsonSchema_string extends Component {
return <Input type="file" className={ errors.length ? "invalid" : ""} onChange={ this.onChange } disabled={isDisabled}/> return <Input type="file" className={ errors.length ? "invalid" : ""} onChange={ this.onChange } disabled={isDisabled}/>
} }
else { else {
return <Input type="text" className={ errors.length ? "invalid" : ""} value={value} placeholder={description} onChange={ this.onChange } disabled={isDisabled}/> return <Input type={ schema.format === "password" ? "password" : "text" } className={ errors.length ? "invalid" : ""} value={value} placeholder={description} onChange={ this.onChange } disabled={isDisabled}/>
} }
} }
} }

View File

@@ -1,27 +1,37 @@
import win from "core/window" import win from "core/window"
import { btoa } from "core/utils" import { btoa } from "core/utils"
export default function authorize ( auth, authActions, errActions, configs ) { export default function authorize ( { auth, authActions, errActions, configs, authConfigs={} } ) {
let { schema, scopes, name, clientId } = auth let { schema, scopes, name, clientId } = auth
let redirectUrl = configs.oauth2RedirectUrl
let scopeSeparator = " "
let state = btoa(new Date())
let flow = schema.get("flow") let flow = schema.get("flow")
let url let query = []
if (flow === "password") { switch (flow) {
case "password":
authActions.authorizePassword(auth) authActions.authorizePassword(auth)
return return
}
if (flow === "application") { case "application":
authActions.authorizeApplication(auth) authActions.authorizeApplication(auth)
return return
case "accessCode":
query.push("response_type=code")
break
case "implicit":
query.push("response_type=token")
break
} }
if (typeof clientId === "string") {
query.push("client_id=" + encodeURIComponent(clientId))
}
let redirectUrl = configs.oauth2RedirectUrl
// todo move to parser // todo move to parser
if ( !redirectUrl ) { if (typeof redirectUrl === "undefined") {
errActions.newAuthErr( { errActions.newAuthErr( {
authId: name, authId: name,
source: "validation", source: "validation",
@@ -30,21 +40,38 @@ export default function authorize ( auth, authActions, errActions, configs ) {
}) })
return return
} }
query.push("redirect_uri=" + encodeURIComponent(redirectUrl))
if (flow === "implicit" || flow === "accessCode") { if (Array.isArray(scopes) && 0 < scopes.length) {
url = schema.get("authorizationUrl") + "?response_type=" + (flow === "implicit" ? "token" : "code") let scopeSeparator = authConfigs.scopeSeparator || " "
query.push("scope=" + encodeURIComponent(scopes.join(scopeSeparator)))
} }
url += "&redirect_uri=" + encodeURIComponent(redirectUrl) let state = btoa(new Date())
+ "&scope=" + encodeURIComponent(scopes.join(scopeSeparator))
+ "&state=" + encodeURIComponent(state) query.push("state=" + encodeURIComponent(state))
+ "&client_id=" + encodeURIComponent(clientId)
if (typeof authConfigs.realm !== "undefined") {
query.push("realm=" + encodeURIComponent(authConfigs.realm))
}
let { additionalQueryStringParams } = authConfigs
for (let key in additionalQueryStringParams) {
if (typeof additionalQueryStringParams[key] !== "undefined") {
query.push([key, additionalQueryStringParams[key]].map(encodeURIComponent).join("="))
}
}
let url = [schema.get("authorizationUrl"), query.join("&")].join("?")
// pass action authorizeOauth2 and authentication data through window // pass action authorizeOauth2 and authentication data through window
// to authorize with oauth2 // to authorize with oauth2
win.swaggerUIRedirectOauth2 = { win.swaggerUIRedirectOauth2 = {
auth: auth, auth: auth,
state: state, state: state,
redirectUrl: redirectUrl,
callback: flow === "implicit" ? authActions.preAuthorizeImplicit : authActions.authorizeAccessCode, callback: flow === "implicit" ? authActions.preAuthorizeImplicit : authActions.authorizeAccessCode,
errCb: errActions.newAuthErr errCb: errActions.newAuthErr
} }

View File

@@ -7,6 +7,7 @@ export const LOGOUT = "logout"
export const PRE_AUTHORIZE_OAUTH2 = "pre_authorize_oauth2" export const PRE_AUTHORIZE_OAUTH2 = "pre_authorize_oauth2"
export const AUTHORIZE_OAUTH2 = "authorize_oauth2" export const AUTHORIZE_OAUTH2 = "authorize_oauth2"
export const VALIDATE = "validate" export const VALIDATE = "validate"
export const CONFIGURE_AUTH = "configure_auth"
const scopeSeparator = " " const scopeSeparator = " "
@@ -81,11 +82,16 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
headers.Authorization = "Basic " + btoa(username + ":" + password) headers.Authorization = "Basic " + btoa(username + ":" + password)
} else { } else {
Object.assign(form, {username}, {password}) Object.assign(form, {username}, {password})
if ( passwordType === "query") { if ( passwordType === "query") {
if ( clientId ) { query.client_id = clientId } if ( clientId ) {
if ( clientSecret ) { query.client_secret = clientSecret } query.client_id = clientId
}
if ( clientSecret ) {
query.client_secret = clientSecret
}
} else { } else {
Object.assign(form, {client_id: clientId}, {client_secret: clientSecret}) headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret)
} }
} }
@@ -94,31 +100,38 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
export const authorizeApplication = ( auth ) => ( { authActions } ) => { export const authorizeApplication = ( auth ) => ( { authActions } ) => {
let { schema, scopes, name, clientId, clientSecret } = auth let { schema, scopes, name, clientId, clientSecret } = auth
let headers = {
Authorization: "Basic " + btoa(clientId + ":" + clientSecret)
}
let form = { let form = {
grant_type: "client_credentials", grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
scope: scopes.join(scopeSeparator) scope: scopes.join(scopeSeparator)
} }
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth }) return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers })
} }
export const authorizeAccessCode = ( auth ) => ( { authActions } ) => { export const authorizeAccessCode = ( { auth, redirectUrl } ) => ( { authActions } ) => {
let { schema, name, clientId, clientSecret } = auth let { schema, name, clientId, clientSecret } = auth
let form = { let form = {
grant_type: "authorization_code", grant_type: "authorization_code",
code: auth.code, code: auth.code,
client_id: clientId, client_id: clientId,
client_secret: clientSecret client_secret: clientSecret,
redirect_uri: redirectUrl
} }
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth}) return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth})
} }
export const authorizeRequest = ( data ) => ( { fn, authActions, errActions } ) => { export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, authSelectors } ) => {
let { body, query={}, headers={}, name, url, auth } = data let { body, query={}, headers={}, name, url, auth } = data
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
let fetchUrl = url
for (let key in additionalQueryStringParams) {
url += "&" + key + "=" + encodeURIComponent(additionalQueryStringParams[key])
}
let _headers = Object.assign({ let _headers = Object.assign({
"Accept":"application/json, text/plain, */*", "Accept":"application/json, text/plain, */*",
@@ -127,7 +140,7 @@ export const authorizeRequest = ( data ) => ( { fn, authActions, errActions } )
}, headers) }, headers)
fn.fetch({ fn.fetch({
url: url, url: fetchUrl,
method: "post", method: "post",
headers: _headers, headers: _headers,
query: query, query: query,
@@ -167,5 +180,13 @@ export const authorizeRequest = ( data ) => ( { fn, authActions, errActions } )
level: "error", level: "error",
source: "auth", source: "auth",
message: err.message message: err.message
} ) }) } )
})
}
export function configureAuth(payload) {
return {
type: CONFIGURE_AUTH,
payload: payload
}
} }

View File

@@ -5,7 +5,8 @@ import {
SHOW_AUTH_POPUP, SHOW_AUTH_POPUP,
AUTHORIZE, AUTHORIZE,
AUTHORIZE_OAUTH2, AUTHORIZE_OAUTH2,
LOGOUT LOGOUT,
CONFIGURE_AUTH
} from "./actions" } from "./actions"
export default { export default {
@@ -57,5 +58,9 @@ export default {
}) })
return state.set("authorized", result) return state.set("authorized", result)
},
[CONFIGURE_AUTH]: (state, { payload } ) =>{
return state.set("configs", payload)
} }
} }

View File

@@ -79,3 +79,8 @@ export const isAuthorized = ( state, securities ) =>( { authSelectors } ) => {
}).indexOf(false) === -1 }).indexOf(false) === -1
}).length }).length
} }
export const getConfigs = createSelector(
state,
auth => auth.get( "configs" )
)

View File

@@ -77,7 +77,9 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
return specActions.updateJsonSpec(json) return specActions.updateJsonSpec(json)
} }
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }}) => { export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
const { modelPropertyMacro, parameterMacro } = getConfigs()
if(typeof(json) === "undefined") { if(typeof(json) === "undefined") {
json = specSelectors.specJson() json = specSelectors.specJson()
} }
@@ -89,7 +91,7 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
let specStr = specSelectors.specStr() let specStr = specSelectors.specStr()
return resolve({fetch, spec: json, baseDoc: url}) return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro })
.then( ({spec, errors}) => { .then( ({spec, errors}) => {
errActions.clear({ errActions.clear({
type: "thrown" type: "thrown"
@@ -186,16 +188,23 @@ export const logRequest = (req) => {
// Actually fire the request via fn.execute // Actually fire the request via fn.execute
// (For debugging) and ease of testing // (For debugging) and ease of testing
export const executeRequest = (req) => ({fn, specActions, specSelectors}) => { export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
let { pathName, method } = req let { pathName, method, operation } = req
let op = operation.toJS()
// if url is relative, parseUrl makes it absolute by inferring from `window.location` // if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString() req.contextUrl = parseUrl(specSelectors.url()).toString()
let parsedRequest = Object.assign({}, req)
if ( pathName && method ) { if(op && op.operationId) {
parsedRequest.operationId = method.toLowerCase() + "-" + pathName req.operationId = op.operationId
} else if(op && pathName && method) {
req.operationId = fn.opId(op, pathName, method)
} }
let parsedRequest = Object.assign({}, req)
parsedRequest = fn.buildRequest(parsedRequest) parsedRequest = fn.buildRequest(parsedRequest)
specActions.setRequest(req.pathName, req.method, parsedRequest) specActions.setRequest(req.pathName, req.method, parsedRequest)
return fn.execute(req) return fn.execute(req)

View File

@@ -42,7 +42,9 @@ export default {
let { path, paramName, value, isXml } = payload let { path, paramName, value, isXml } = payload
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => { return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
let index = parameters.findIndex( p => p.get( "name" ) === paramName ) let index = parameters.findIndex( p => p.get( "name" ) === paramName )
value = value instanceof win.File ? value.name : value if (!(value instanceof win.File)) {
value = fromJSOrdered( value )
}
return parameters.setIn( [ index, isXml ? "value_xml" : "value" ], value) return parameters.setIn( [ index, isXml ? "value_xml" : "value" ], value)
}) })
}, },

View File

@@ -43,8 +43,6 @@ export const specResolved = createSelector(
// Default Spec ( as an object ) // Default Spec ( as an object )
export const spec = state => { export const spec = state => {
let res = specResolved(state) let res = specResolved(state)
if(res.count() < 1)
res = specJson(state)
return res return res
} }

View File

@@ -7,7 +7,8 @@ module.exports = function({ configs }) {
buildRequest: Swagger.buildRequest, buildRequest: Swagger.buildRequest,
execute: Swagger.execute, execute: Swagger.execute,
resolve: Swagger.resolve, resolve: Swagger.resolve,
serializeRes: Swagger.serializeRes serializeRes: Swagger.serializeRes,
opId: Swagger.helpers.opId
} }
} }
} }

View File

@@ -46,6 +46,8 @@ import Model from "core/components/model"
import Models from "core/components/models" import Models from "core/components/models"
import TryItOutButton from "core/components/try-it-out-button" import TryItOutButton from "core/components/try-it-out-button"
import Markdown from "core/components/providers/markdown"
import BaseLayout from "core/components/layouts/base" import BaseLayout from "core/components/layouts/base"
import * as LayoutUtils from "core/components/layout-utils" import * as LayoutUtils from "core/components/layout-utils"
@@ -89,6 +91,7 @@ export default function() {
model: Model, model: Model,
models: Models, models: Models,
TryItOutButton, TryItOutButton,
Markdown,
BaseLayout BaseLayout
} }
} }

View File

@@ -58,7 +58,7 @@
&.execute &.execute
{ {
animation: pulse 2s infinite; animation: swagger-ui-pulse 2s infinite;
color: #fff; color: #fff;
border-color: #4990e2; border-color: #4990e2;
@@ -66,7 +66,7 @@
} }
@keyframes pulse @keyframes swagger-ui-pulse
{ {
0% 0%
{ {

View File

@@ -13,13 +13,18 @@
font-size: 12px; font-size: 12px;
} }
} }
p p, li, table
{ {
font-size: 14px; font-size: 14px;
@include text_body(); @include text_body();
} }
h1, h2, h3, h4, h5
{
@include text_body();
}
code code
{ {
padding: 3px 5px; padding: 3px 5px;

View File

@@ -209,6 +209,7 @@ body
} }
.opblock-summary-path, .opblock-summary-path,
.opblock-summary-operation-id,
.opblock-summary-path__deprecated .opblock-summary-path__deprecated
{ {
font-size: 16px; font-size: 16px;
@@ -247,6 +248,11 @@ body
text-decoration: line-through; text-decoration: line-through;
} }
.opblock-summary-operation-id
{
font-size: 14px;
}
.opblock-summary-description .opblock-summary-description
{ {
font-size: 13px; font-size: 13px;
@@ -492,11 +498,18 @@ body
margin: 0; margin: 0;
padding: 10px; padding: 10px;
word-wrap: break-word;
word-break: break-all;
word-break: break-word;
hyphens: auto;
white-space: pre-wrap; white-space: pre-wrap;
border-radius: 4px; border-radius: 4px;
background: #41444e; background: #41444e;
overflow-wrap: break-word;
@include text_code(#fff); @include text_code(#fff);
span span
{ {

View File

@@ -1,3 +1,8 @@
This module, `swagger-ui-dist`, exposes Swagger-UI's entire dist folder as a dependency-free npm module. This module, `swagger-ui-dist`, exposes Swagger-UI's entire dist folder as a dependency-free npm module. Use `swagger-ui` instead, if you'd like to have npm install dependencies for you.
Use `swagger-ui` instead, if you'd like to have npm install dependencies for you. `SwaggerUIBundle` and `SwaggerUIStandalonePreset` can be imported:
```javascript
import { SwaggerUIBundle, SwaggerUIStandalonePreset } from 'swagger-ui-dist'
```
For anything else, check the [Swagger-UI](https://github.com/swagger-api/swagger-ui) repository.

View File

@@ -18,4 +18,4 @@ else
npm pack . npm pack .
fi fi
find . -not -name .npmignore -not -name .npmrc -not -name deploy.sh -not -name package.json -not -name README.md -not -name *.tgz -delete find . -not -name .npmignore -not -name .npmrc -not -name deploy.sh -not -name index.js -not -name package.json -not -name README.md -not -name *.tgz -delete

View File

@@ -0,0 +1,2 @@
module.exports.SwaggerUIBundle = require('./swagger-ui-bundle.js')
module.exports.SwaggerUIStandalonePreset = require('./swagger-ui-standalone-preset.js')

View File

@@ -1,7 +1,7 @@
{ {
"name": "swagger-ui-dist", "name": "swagger-ui-dist",
"version": "$$VERSION", "version": "$$VERSION",
"main": "dist/swagger-ui.js", "main": "index.js",
"repository": "git@github.com:swagger-api/swagger-ui.git", "repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [ "contributors": [
"(in alphabetical order)", "(in alphabetical order)",

View File

@@ -0,0 +1,23 @@
/* eslint-env mocha */
import React from "react"
import expect from "expect"
import { render } from "enzyme"
import Markdown from "components/providers/markdown"
describe("UI-3199: Sanitized Markdown causing code examples to be double escaped", function(){
it("should single-escape quotes", function(){
let str = "" +
"This is a test: \n\n" +
" {\"abc\": \"def\"}\n"
let props = {
source: str
}
let el = render(<Markdown {...props}/>)
expect(el.find("code").first().text()).toEqual("{\"abc\": \"def\"}\n")
expect(el.find("code").first().html()).toEqual("{&quot;abc&quot;: &quot;def&quot;}\n")
})
})

View File

@@ -10,11 +10,13 @@ module.exports = require("./make-webpack-config")({
devtool: "eval", devtool: "eval",
entry: { entry: {
'swagger-ui-bundle': [ 'swagger-ui-bundle': [
'webpack/hot/dev-server',
'babel-polyfill', 'babel-polyfill',
'./src/core/index.js' './src/core/index.js',
], ],
'swagger-ui-standalone-preset': [ 'swagger-ui-standalone-preset': [
'./src/standalone/index.js' 'webpack/hot/dev-server',
'./src/standalone/index.js',
] ]
}, },
output: { output: {