Compare commits
17
Commits
cbt-run-e2e
...
v3.13.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14ef0cc379 | ||
|
|
08cfc77fde | ||
|
|
b8cf080e6c | ||
|
|
245428e7cd | ||
|
|
624a81201f | ||
|
|
b0be261ef8 | ||
|
|
6c5a370ef7 | ||
|
|
fb7f12551f | ||
|
|
49e0466641 | ||
|
|
ad57f5a549 | ||
|
|
ebf94dfccf | ||
|
|
6ba93ad43f | ||
|
|
54153dd929 | ||
|
|
83232dc2d8 | ||
|
|
ad43965d16 | ||
|
|
861cc65cff | ||
|
|
4900b11079 |
+1
-1
@@ -34,7 +34,7 @@ deploy:
|
|||||||
node: '6.9'
|
node: '6.9'
|
||||||
after_success:
|
after_success:
|
||||||
- if [ $DOCKER_HUB_USERNAME ]; then
|
- if [ $DOCKER_HUB_USERNAME ]; then
|
||||||
docker login --email=$DOCKER_HUB_EMAIL --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD;
|
docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD;
|
||||||
|
|
||||||
if [ ! -z "$TRAVIS_TAG" ]; then
|
if [ ! -z "$TRAVIS_TAG" ]; then
|
||||||
DOCKER_IMAGE_TAG=$TRAVIS_TAG;
|
DOCKER_IMAGE_TAG=$TRAVIS_TAG;
|
||||||
|
|||||||
@@ -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
|
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||||
------------------ | ------------ | -------------------------- | -----
|
------------------ | ------------ | -------------------------- | -----
|
||||||
3.11.0 | 2018-02-09 | 2.0, 3.0 | [tag v3.11.0](https://github.com/swagger-api/swagger-ui/tree/v3.11.0)
|
3.13.0 | 2018-03-16 | 2.0, 3.0 | [tag v3.13.0](https://github.com/swagger-api/swagger-ui/tree/v3.13.0)
|
||||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
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.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)
|
||||||
|
|||||||
Vendored
+14
-20
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
|||||||
|
# Plug points
|
||||||
|
|
||||||
|
Swagger-UI exposes most of its internal logic through the plugin system.
|
||||||
|
|
||||||
|
Often, it is beneficial to override the core internals to achieve custom behavior.
|
||||||
|
|
||||||
|
### Note: Semantic Versioning
|
||||||
|
|
||||||
|
Swagger-UI's internal APIs are _not_ part of our public contract, which means that they can change without the major version changing.
|
||||||
|
|
||||||
|
If your custom plugins wrap, extend, override, or consume any internal core APIs, we recommend specifying a specific minor version of Swagger-UI to use in your application, because they will _not_ change between patch versions.
|
||||||
|
|
||||||
|
If you're installing Swagger-UI via NPM, for example, you can do this by using a tilde:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"swagger-ui": "~3.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `fn.opsFilter`
|
||||||
|
|
||||||
|
When using the `filter` option, tag names will be filtered by the user-provided value. If you'd like to customize this behavior, you can override the default `opsFilter` function.
|
||||||
|
|
||||||
|
For example, you can implement a multiple-phrase filter:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const MultiplePhraseFilterPlugin = function() {
|
||||||
|
return {
|
||||||
|
fn: {
|
||||||
|
opsFilter: (taggedOps, phrase) => {
|
||||||
|
const phrases = phrase.split(", ")
|
||||||
|
|
||||||
|
return taggedOps.filter((val, key) => {
|
||||||
|
return phrases.some(item => key.indexOf(item) > -1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
A plugin is a function that returns an object - more specifically, the object may contain functions and components that augment and modify Swagger-UI's functionality.
|
A plugin is a function that returns an object - more specifically, the object may contain functions and components that augment and modify Swagger-UI's functionality.
|
||||||
|
|
||||||
|
### Note: Semantic Versioning
|
||||||
|
|
||||||
|
Swagger-UI's internal APIs are _not_ part of our public contract, which means that they can change without the major version changing.
|
||||||
|
|
||||||
|
If your custom plugins wrap, extend, override, or consume any internal core APIs, we recommend specifying a specific minor version of Swagger-UI to use in your application, because they will _not_ change between patch versions.
|
||||||
|
|
||||||
|
If you're installing Swagger-UI via NPM, for example, you can do this by using a tilde:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"swagger-ui": "~3.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Format
|
### Format
|
||||||
|
|
||||||
A plugin return value may contain any of these keys, where `stateKey` is a name for a piece of state:
|
A plugin return value may contain any of these keys, where `stateKey` is a name for a piece of state:
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ Parameter Name | Description
|
|||||||
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
|
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
|
||||||
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
|
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
|
||||||
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
|
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
|
||||||
|
`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
|
||||||
|
|
||||||
##### Network
|
##### Network
|
||||||
|
|
||||||
@@ -77,3 +78,11 @@ Parameter Name | Description
|
|||||||
--- | ---
|
--- | ---
|
||||||
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
|
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
|
||||||
`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
|
`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
|
||||||
|
|
||||||
|
### Instance methods
|
||||||
|
|
||||||
|
Method Name | Description
|
||||||
|
--- | ---
|
||||||
|
`initOAuth` | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
|
||||||
|
`preauthorizeBasic` | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
|
||||||
|
`preauthorizeApiKey` | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# OAuth2 configuration
|
# OAuth2 configuration
|
||||||
You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle`
|
You can configure OAuth2 authorization by calling the `initOAuth` method.
|
||||||
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`,
|
|
||||||
`useBasicAuthenticationWithAccessCodeGrant`.
|
|
||||||
|
|
||||||
Config Name | Description
|
Config Name | Description
|
||||||
--- | ---
|
--- | ---
|
||||||
@@ -11,7 +9,7 @@ realm | realm query parameter (for oauth1) added to `authorizationUrl` and `toke
|
|||||||
appName | application name, displayed in authorization popup. 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
|
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
|
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`
|
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 base64encode(client_id + client_secret)`). The default is `false`
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const ui = SwaggerUI({...})
|
const ui = SwaggerUI({...})
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "swagger-ui",
|
"name": "swagger-ui",
|
||||||
"version": "3.11.0",
|
"version": "3.13.0",
|
||||||
"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": [
|
||||||
@@ -37,7 +37,8 @@
|
|||||||
"test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
|
"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",
|
"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",
|
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
|
||||||
"e2e": "npm-run-all --parallel -r hot-server mock-api test-e2e"
|
"hot-e2e-server": "webpack-dev-server --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress",
|
||||||
|
"e2e": "npm-run-all --parallel -r hot-e2e-server mock-api test-e2e"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^2.0.2",
|
"@braintree/sanitize-url": "^2.0.2",
|
||||||
@@ -83,7 +84,7 @@
|
|||||||
"scroll-to-element": "^2.0.0",
|
"scroll-to-element": "^2.0.0",
|
||||||
"serialize-error": "2.0.0",
|
"serialize-error": "2.0.0",
|
||||||
"shallowequal": "0.2.2",
|
"shallowequal": "0.2.2",
|
||||||
"swagger-client": "^3.5.0",
|
"swagger-client": "^3.5.1",
|
||||||
"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",
|
||||||
@@ -144,6 +145,7 @@
|
|||||||
"standard": "^10.0.2",
|
"standard": "^10.0.2",
|
||||||
"standard-loader": "^6.0.1",
|
"standard-loader": "^6.0.1",
|
||||||
"style-loader": "0.18.2",
|
"style-loader": "0.18.2",
|
||||||
|
"tachyons-sass": "^4.9.2",
|
||||||
"url-loader": "0.5.9",
|
"url-loader": "0.5.9",
|
||||||
"webpack": "^2.6.1",
|
"webpack": "^2.6.1",
|
||||||
"webpack-bundle-size-analyzer": "^2.5.0"
|
"webpack-bundle-size-analyzer": "^2.5.0"
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class ArrayModel extends Component {
|
|||||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!description ? null :
|
!description ? (properties.size ? <div className="markdown"></div> : null) :
|
||||||
<Markdown source={ description } />
|
<Markdown source={ description } />
|
||||||
}
|
}
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -81,11 +81,11 @@ export default class Auths extends React.Component {
|
|||||||
}).toArray()
|
}).toArray()
|
||||||
}
|
}
|
||||||
<div className="auth-btn-wrapper">
|
<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>
|
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>
|
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>
|
||||||
}
|
}
|
||||||
|
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ export default class Oauth2 extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
let { authActions } = this.props
|
||||||
|
|
||||||
|
authActions.showDefinitions(false)
|
||||||
|
}
|
||||||
|
|
||||||
authorize =() => {
|
authorize =() => {
|
||||||
let { authActions, errActions, getConfigs, authSelectors } = this.props
|
let { authActions, errActions, getConfigs, authSelectors } = this.props
|
||||||
let configs = getConfigs()
|
let configs = getConfigs()
|
||||||
@@ -231,6 +238,7 @@ export default class Oauth2 extends React.Component {
|
|||||||
: <Button className="btn modal-btn auth authorize" onClick={ this.authorize }>Authorize</Button>
|
: <Button className="btn modal-btn auth authorize" onClick={ this.authorize }>Authorize</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import ImPropTypes from "react-immutable-proptypes"
|
|||||||
import { sanitizeUrl } from "core/utils"
|
import { sanitizeUrl } from "core/utils"
|
||||||
|
|
||||||
|
|
||||||
class Path extends React.Component {
|
export class InfoBasePath extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
host: PropTypes.string,
|
host: PropTypes.string,
|
||||||
basePath: PropTypes.string
|
basePath: PropTypes.string
|
||||||
@@ -68,6 +68,17 @@ class License extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class InfoUrl extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
url: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { url } = this.props
|
||||||
|
return <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class Info extends React.Component {
|
export default class Info extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
info: PropTypes.object,
|
info: PropTypes.object,
|
||||||
@@ -90,6 +101,8 @@ export default class Info extends React.Component {
|
|||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown")
|
||||||
const VersionStamp = getComponent("VersionStamp")
|
const VersionStamp = getComponent("VersionStamp")
|
||||||
|
const InfoUrl = getComponent("InfoUrl")
|
||||||
|
const InfoBasePath = getComponent("InfoBasePath")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="info">
|
<div className="info">
|
||||||
@@ -97,8 +110,8 @@ export default class Info extends React.Component {
|
|||||||
<h2 className="title" >{ title }
|
<h2 className="title" >{ title }
|
||||||
{ version && <VersionStamp version={version}></VersionStamp> }
|
{ version && <VersionStamp version={version}></VersionStamp> }
|
||||||
</h2>
|
</h2>
|
||||||
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
|
{ host || basePath ? <InfoBasePath host={ host } basePath={ basePath } /> : null }
|
||||||
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
|
{ url && <InfoUrl url={url} /> }
|
||||||
</hgroup>
|
</hgroup>
|
||||||
|
|
||||||
<div className="description">
|
<div className="description">
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default class Models extends Component {
|
|||||||
|
|
||||||
const content = <ModelWrapper name={ name }
|
const content = <ModelWrapper name={ name }
|
||||||
expandDepth={ defaultModelsExpandDepth }
|
expandDepth={ defaultModelsExpandDepth }
|
||||||
schema={ schema }
|
schema={ schema || Im.Map() }
|
||||||
specPath={Im.List([...specPathBase, name])}
|
specPath={Im.List([...specPathBase, name])}
|
||||||
getComponent={ getComponent }
|
getComponent={ getComponent }
|
||||||
specSelectors={ specSelectors }
|
specSelectors={ specSelectors }
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class ValidatorImage extends React.Component {
|
|||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
return <img alt={"Error"} />
|
return <img alt={"Error"} />
|
||||||
} else if (!this.state.loaded) {
|
} else if (!this.state.loaded) {
|
||||||
return <img alt= {"Loading..."} />
|
return null
|
||||||
}
|
}
|
||||||
return <img src={this.props.src} alt={this.props.alt} />
|
return <img src={this.props.src} alt={this.props.alt} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,18 @@ export default class ParameterRow extends Component {
|
|||||||
|
|
||||||
let { specSelectors, pathMethod, param } = props
|
let { specSelectors, pathMethod, param } = props
|
||||||
let defaultValue = param.get("default")
|
let defaultValue = param.get("default")
|
||||||
|
let xExampleValue = param.get("x-example")
|
||||||
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||||
let value = parameter ? parameter.get("value") : ""
|
let value = parameter ? parameter.get("value") : ""
|
||||||
if ( defaultValue !== undefined && value === undefined ) {
|
|
||||||
this.onChangeWrapper(defaultValue)
|
if( param.get("in") !== "body" ) {
|
||||||
|
if ( xExampleValue !== undefined && value === undefined && specSelectors.isSwagger2() ) {
|
||||||
|
this.onChangeWrapper(xExampleValue)
|
||||||
|
} else if ( defaultValue !== undefined && value === undefined ) {
|
||||||
|
this.onChangeWrapper(defaultValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(props) {
|
componentWillReceiveProps(props) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const sanitizeOptions = {
|
|||||||
"td": [ "colspan" ],
|
"td": [ "colspan" ],
|
||||||
"*": [ "class" ]
|
"*": [ "class" ]
|
||||||
},
|
},
|
||||||
|
allowedSchemesByTag: { img: [ "http", "https", "data" ] },
|
||||||
textFilter: function(text) {
|
textFilter: function(text) {
|
||||||
return text.replace(/"/g, "\"")
|
return text.replace(/"/g, "\"")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export default function() {
|
|||||||
afterLoad(system) {
|
afterLoad(system) {
|
||||||
this.rootInjects = this.rootInjects || {}
|
this.rootInjects = this.rootInjects || {}
|
||||||
this.rootInjects.initOAuth = system.authActions.configureAuth
|
this.rootInjects.initOAuth = system.authActions.configureAuth
|
||||||
|
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
|
||||||
|
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
|
||||||
},
|
},
|
||||||
statePlugins: {
|
statePlugins: {
|
||||||
auth: {
|
auth: {
|
||||||
@@ -21,3 +23,50 @@ export default function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function preauthorizeBasic(system, key, username, password) {
|
||||||
|
const {
|
||||||
|
authActions: { authorize },
|
||||||
|
specSelectors: { specJson, isOAS3 }
|
||||||
|
} = system
|
||||||
|
|
||||||
|
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
|
||||||
|
|
||||||
|
const schema = specJson().getIn([...definitionBase, key])
|
||||||
|
|
||||||
|
if(!schema) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorize({
|
||||||
|
[key]: {
|
||||||
|
value: {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
},
|
||||||
|
schema: schema.toJS()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function preauthorizeApiKey(system, key, value) {
|
||||||
|
const {
|
||||||
|
authActions: { authorize },
|
||||||
|
specSelectors: { specJson, isOAS3 }
|
||||||
|
} = system
|
||||||
|
|
||||||
|
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
|
||||||
|
|
||||||
|
const schema = specJson().getIn([...definitionBase, key])
|
||||||
|
|
||||||
|
if(!schema) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return authorize({
|
||||||
|
[key]: {
|
||||||
|
value,
|
||||||
|
schema: schema.toJS()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
import { Map } from "immutable"
|
import { Map } from "immutable"
|
||||||
|
import win from "../window"
|
||||||
|
|
||||||
export default function downloadUrlPlugin (toolbox) {
|
export default function downloadUrlPlugin (toolbox) {
|
||||||
let { fn } = toolbox
|
let { fn } = toolbox
|
||||||
@@ -12,6 +13,7 @@ export default function downloadUrlPlugin (toolbox) {
|
|||||||
const config = getConfigs()
|
const config = getConfigs()
|
||||||
url = url || specSelectors.url()
|
url = url || specSelectors.url()
|
||||||
specActions.updateLoadingStatus("loading")
|
specActions.updateLoadingStatus("loading")
|
||||||
|
errActions.clear({source: "fetch"})
|
||||||
fetch({
|
fetch({
|
||||||
url,
|
url,
|
||||||
loadSpec: true,
|
loadSpec: true,
|
||||||
@@ -26,7 +28,10 @@ export default function downloadUrlPlugin (toolbox) {
|
|||||||
function next(res) {
|
function next(res) {
|
||||||
if(res instanceof Error || res.status >= 400) {
|
if(res instanceof Error || res.status >= 400) {
|
||||||
specActions.updateLoadingStatus("failed")
|
specActions.updateLoadingStatus("failed")
|
||||||
return errActions.newThrownErr( new Error((res.message || res.statusText) + " " + url) )
|
errActions.newThrownErr(Object.assign( new Error((res.message || res.statusText) + " " + url), {source: "fetch"}))
|
||||||
|
// Check if the failure was possibly due to CORS or mixed content
|
||||||
|
if (!res.status && res instanceof Error) checkPossibleFailReasons()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
specActions.updateLoadingStatus("success")
|
specActions.updateLoadingStatus("success")
|
||||||
specActions.updateSpec(res.text)
|
specActions.updateSpec(res.text)
|
||||||
@@ -35,6 +40,38 @@ export default function downloadUrlPlugin (toolbox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkPossibleFailReasons() {
|
||||||
|
try {
|
||||||
|
let specUrl
|
||||||
|
|
||||||
|
if("URL" in win ) {
|
||||||
|
specUrl = new URL(url)
|
||||||
|
} else {
|
||||||
|
// legacy browser, use <a href> to parse the URL
|
||||||
|
specUrl = document.createElement("a")
|
||||||
|
specUrl.href = url
|
||||||
|
}
|
||||||
|
|
||||||
|
if(specUrl.protocol !== "https:" && win.location.protocol === "https:") {
|
||||||
|
const error = Object.assign(
|
||||||
|
new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${specUrl.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),
|
||||||
|
{source: "fetch"}
|
||||||
|
)
|
||||||
|
errActions.newThrownErr(error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(specUrl.origin !== win.location.origin) {
|
||||||
|
const error = Object.assign(
|
||||||
|
new Error(`Possible cross-origin (CORS) issue? The URL origin (${specUrl.origin}) does not match the page (${win.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),
|
||||||
|
{source: "fetch"}
|
||||||
|
)
|
||||||
|
errActions.newThrownErr(error)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateLoadingStatus: (status) => {
|
updateLoadingStatus: (status) => {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export default class RequestBodyEditor extends PureComponent {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isEditBox: false,
|
isEditBox: false,
|
||||||
|
userDidModify: false,
|
||||||
value: ""
|
value: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,11 @@ export default class RequestBodyEditor extends PureComponent {
|
|||||||
this.onChange(this.sample(explicitMediaType))
|
this.onChange(this.sample(explicitMediaType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetValueToSample = (explicitMediaType) => {
|
||||||
|
this.setState({ userDidModify: false })
|
||||||
|
this.setValueToSample(explicitMediaType)
|
||||||
|
}
|
||||||
|
|
||||||
sample = (explicitMediaType) => {
|
sample = (explicitMediaType) => {
|
||||||
let { requestBody, mediaType } = this.props
|
let { requestBody, mediaType } = this.props
|
||||||
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
|
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
|
||||||
@@ -78,6 +84,7 @@ export default class RequestBodyEditor extends PureComponent {
|
|||||||
const isJson = /json/i.test(mediaType)
|
const isJson = /json/i.test(mediaType)
|
||||||
const inputValue = isJson ? e.target.value.trim() : e.target.value
|
const inputValue = isJson ? e.target.value.trim() : e.target.value
|
||||||
|
|
||||||
|
this.setState({ userDidModify: true })
|
||||||
this.onChange(inputValue)
|
this.onChange(inputValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,13 +94,14 @@ export default class RequestBodyEditor extends PureComponent {
|
|||||||
let {
|
let {
|
||||||
isExecute,
|
isExecute,
|
||||||
getComponent,
|
getComponent,
|
||||||
|
mediaType,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const Button = getComponent("Button")
|
const Button = getComponent("Button")
|
||||||
const TextArea = getComponent("TextArea")
|
const TextArea = getComponent("TextArea")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("highlightCode")
|
||||||
|
|
||||||
let { value, isEditBox } = this.state
|
let { value, isEditBox, userDidModify } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="body-param">
|
<div className="body-param">
|
||||||
@@ -104,14 +112,18 @@ export default class RequestBodyEditor extends PureComponent {
|
|||||||
value={ value }/>)
|
value={ value }/>)
|
||||||
}
|
}
|
||||||
<div className="body-param-options">
|
<div className="body-param-options">
|
||||||
{
|
<div className="body-param-edit">
|
||||||
!isExecute ? null
|
{
|
||||||
: <div className="body-param-edit">
|
!isExecute ? null
|
||||||
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
|
: <Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
|
||||||
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
|
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
{ userDidModify &&
|
||||||
|
<Button className="btn ml3" onClick={() => { this.resetValueToSample(mediaType) }}>Reset</Button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
let engaged = false
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
statePlugins: {
|
||||||
|
spec: {
|
||||||
|
wrapActions: {
|
||||||
|
updateSpec: (ori) => (...args) => {
|
||||||
|
engaged = true
|
||||||
|
return ori(...args)
|
||||||
|
},
|
||||||
|
updateJsonSpec: (ori, system) => (...args) => {
|
||||||
|
const cb = system.getConfigs().onComplete
|
||||||
|
if(engaged && typeof cb === "function") {
|
||||||
|
// call `onComplete` on next tick, which allows React to
|
||||||
|
// reconcile the DOM before we notify the user
|
||||||
|
setTimeout(cb, 0)
|
||||||
|
engaged = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ori(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,10 @@ export const sampleFromSchema = (schema, config={}) => {
|
|||||||
let { type, example, properties, additionalProperties, items } = objectify(schema)
|
let { type, example, properties, additionalProperties, items } = objectify(schema)
|
||||||
let { includeReadOnly, includeWriteOnly } = config
|
let { includeReadOnly, includeWriteOnly } = config
|
||||||
|
|
||||||
|
if(example && example.$$ref) {
|
||||||
|
delete example.$$ref
|
||||||
|
}
|
||||||
|
|
||||||
if(example !== undefined)
|
if(example !== undefined)
|
||||||
return example
|
return example
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import downloadUrlPlugin from "core/plugins/download-url"
|
|||||||
import configsPlugin from "core/plugins/configs"
|
import configsPlugin from "core/plugins/configs"
|
||||||
import deepLinkingPlugin from "core/plugins/deep-linking"
|
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||||
import filter from "core/plugins/filter"
|
import filter from "core/plugins/filter"
|
||||||
|
import onComplete from "core/plugins/on-complete"
|
||||||
|
|
||||||
import OperationContainer from "core/containers/OperationContainer"
|
import OperationContainer from "core/containers/OperationContainer"
|
||||||
|
|
||||||
@@ -45,7 +46,10 @@ import Headers from "core/components/headers"
|
|||||||
import Errors from "core/components/errors"
|
import Errors from "core/components/errors"
|
||||||
import ContentType from "core/components/content-type"
|
import ContentType from "core/components/content-type"
|
||||||
import Overview from "core/components/overview"
|
import Overview from "core/components/overview"
|
||||||
import Info from "core/components/info"
|
import Info, {
|
||||||
|
InfoUrl,
|
||||||
|
InfoBasePath
|
||||||
|
} from "core/components/info"
|
||||||
import Footer from "core/components/footer"
|
import Footer from "core/components/footer"
|
||||||
import ParamBody from "core/components/param-body"
|
import ParamBody from "core/components/param-body"
|
||||||
import Curl from "core/components/curl"
|
import Curl from "core/components/curl"
|
||||||
@@ -124,7 +128,9 @@ export default function() {
|
|||||||
OperationExtRow,
|
OperationExtRow,
|
||||||
ParameterExt,
|
ParameterExt,
|
||||||
OperationContainer,
|
OperationContainer,
|
||||||
DeepLink
|
DeepLink,
|
||||||
|
InfoUrl,
|
||||||
|
InfoBasePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +160,7 @@ export default function() {
|
|||||||
SplitPaneModePlugin,
|
SplitPaneModePlugin,
|
||||||
downloadUrlPlugin,
|
downloadUrlPlugin,
|
||||||
deepLinkingPlugin,
|
deepLinkingPlugin,
|
||||||
filter
|
filter,
|
||||||
|
onComplete
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -350,7 +350,7 @@ export function extractFileNameFromContentDispositionHeader(value){
|
|||||||
if (responseFilename !== null && responseFilename.length > 1) {
|
if (responseFilename !== null && responseFilename.length > 1) {
|
||||||
return responseFilename[1]
|
return responseFilename[1]
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// PascalCase, aka UpperCamelCase
|
// PascalCase, aka UpperCamelCase
|
||||||
@@ -559,7 +559,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
|
|||||||
} else if ( type === "array" ) {
|
} else if ( type === "array" ) {
|
||||||
let itemType
|
let itemType
|
||||||
|
|
||||||
if ( !value.count() ) { return errors }
|
if ( !listCheck || !value.count() ) { return errors }
|
||||||
|
|
||||||
itemType = paramDetails.getIn(["items", "type"])
|
itemType = paramDetails.getIn(["items", "type"])
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export default class Topbar extends React.Component {
|
|||||||
setSearch = (spec) => {
|
setSearch = (spec) => {
|
||||||
let search = parseSearch()
|
let search = parseSearch()
|
||||||
search["urls.primaryName"] = spec.name
|
search["urls.primaryName"] = spec.name
|
||||||
window.location.search = serializeSearch(search)
|
const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`
|
||||||
|
if(window && window.history && window.history.pushState) {
|
||||||
|
window.history.replaceState(null, "", `${newUrl}?${serializeSearch(search)}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedUrl = (selectedUrl) => {
|
setSelectedUrl = (selectedUrl) => {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default class StandaloneLayout extends React.Component {
|
|||||||
let Container = getComponent("Container")
|
let Container = getComponent("Container")
|
||||||
let Row = getComponent("Row")
|
let Row = getComponent("Row")
|
||||||
let Col = getComponent("Col")
|
let Col = getComponent("Col")
|
||||||
|
let Errors = getComponent("errors", true)
|
||||||
|
|
||||||
const Topbar = getComponent("Topbar", true)
|
const Topbar = getComponent("Topbar", true)
|
||||||
const BaseLayout = getComponent("BaseLayout", true)
|
const BaseLayout = getComponent("BaseLayout", true)
|
||||||
@@ -45,8 +46,8 @@ export default class StandaloneLayout extends React.Component {
|
|||||||
<div className="info">
|
<div className="info">
|
||||||
<div className="loading-container">
|
<div className="loading-container">
|
||||||
<h4 className="title">Failed to load API definition.</h4>
|
<h4 className="title">Failed to load API definition.</h4>
|
||||||
<p>{lastErrMsg}</p>
|
<Errors/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{ loadingStatus === "failedConfig" &&
|
{ loadingStatus === "failedConfig" &&
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
.authorize
|
.authorize
|
||||||
{
|
{
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
.swagger-ui
|
.swagger-ui
|
||||||
{
|
{
|
||||||
|
@import '~tachyons-sass/tachyons.scss';
|
||||||
@import 'mixins';
|
@import 'mixins';
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
@import 'colors';
|
@import 'colors';
|
||||||
|
|||||||
@@ -24,6 +24,18 @@ describe("Markdown component", function() {
|
|||||||
const el = render(<Markdown source={str} />)
|
const el = render(<Markdown source={str} />)
|
||||||
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
|
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("allows image elements with https scheme", function() {
|
||||||
|
const str = ``
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p><img src="https://image.source" title="Image title"></p>\n</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("allows image elements with data scheme", function() {
|
||||||
|
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p>` + str + `</p>\n</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
it("allows heading elements", function() {
|
it("allows heading elements", function() {
|
||||||
const str = `
|
const str = `
|
||||||
@@ -51,6 +63,18 @@ describe("Markdown component", function() {
|
|||||||
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("allows image elements with https scheme", function() {
|
||||||
|
const str = ``
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="https://image.source" title="Image title"></p></div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("allows image elements with data scheme", function() {
|
||||||
|
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div>` + str + `</div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
it("allows heading elements", function() {
|
it("allows heading elements", function() {
|
||||||
const str = `
|
const str = `
|
||||||
# h1
|
# h1
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
import expect from "expect"
|
||||||
|
import { fromJS } from "immutable"
|
||||||
|
import { preauthorizeBasic, preauthorizeApiKey } from "corePlugins/auth"
|
||||||
|
import { authorize } from "corePlugins/auth/actions"
|
||||||
|
|
||||||
|
const S2_SYSTEM = {
|
||||||
|
authActions: {
|
||||||
|
authorize
|
||||||
|
},
|
||||||
|
specSelectors: {
|
||||||
|
isOAS3: () => false,
|
||||||
|
specJson: () => {
|
||||||
|
return fromJS({
|
||||||
|
swagger: "2.0",
|
||||||
|
securityDefinitions: {
|
||||||
|
"APIKeyHeader": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"in": "header",
|
||||||
|
"name": "X-API-Key"
|
||||||
|
},
|
||||||
|
"basicAuth": {
|
||||||
|
"type": "basic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const OAI3_SYSTEM = {
|
||||||
|
authActions: {
|
||||||
|
authorize
|
||||||
|
},
|
||||||
|
specSelectors: {
|
||||||
|
isOAS3: () => true,
|
||||||
|
specJson: () => {
|
||||||
|
return fromJS({
|
||||||
|
openapi: "3.0.0",
|
||||||
|
components: {
|
||||||
|
securitySchemes: {
|
||||||
|
basicAuth: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "basic"
|
||||||
|
},
|
||||||
|
APIKeyHeader: {
|
||||||
|
type: "apiKey",
|
||||||
|
in: "header",
|
||||||
|
name: "X-API-Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("auth plugin - preauthorizers", () => {
|
||||||
|
describe("preauthorizeBasic", () => {
|
||||||
|
it("should return a valid authorize action in Swagger 2", () => {
|
||||||
|
const res = preauthorizeBasic(S2_SYSTEM, "basicAuth", "user", "pass")
|
||||||
|
|
||||||
|
expect(res).toEqual({
|
||||||
|
type: "authorize",
|
||||||
|
payload: {
|
||||||
|
basicAuth: {
|
||||||
|
schema: {
|
||||||
|
type: "basic"
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
username: "user",
|
||||||
|
password: "pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it("should return a valid authorize action in OpenAPI 3", () => {
|
||||||
|
const res = preauthorizeBasic(OAI3_SYSTEM, "basicAuth", "user", "pass")
|
||||||
|
|
||||||
|
expect(res).toEqual({
|
||||||
|
type: "authorize",
|
||||||
|
payload: {
|
||||||
|
basicAuth: {
|
||||||
|
schema: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "basic"
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
username: "user",
|
||||||
|
password: "pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it("should return null when the authorization name is invalid in Swagger 2", () => {
|
||||||
|
const res = preauthorizeBasic(S2_SYSTEM, "fakeBasicAuth", "user", "pass")
|
||||||
|
|
||||||
|
expect(res).toEqual(null)
|
||||||
|
})
|
||||||
|
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
|
||||||
|
const res = preauthorizeBasic(OAI3_SYSTEM, "fakeBasicAuth", "user", "pass")
|
||||||
|
|
||||||
|
expect(res).toEqual(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("preauthorizeApiKey", () => {
|
||||||
|
it("should return a valid authorize action in Swagger 2", () => {
|
||||||
|
const res = preauthorizeApiKey(S2_SYSTEM, "APIKeyHeader", "Asdf1234")
|
||||||
|
|
||||||
|
expect(res).toEqual({
|
||||||
|
type: "authorize",
|
||||||
|
payload: {
|
||||||
|
APIKeyHeader: {
|
||||||
|
schema: {
|
||||||
|
type: "apiKey",
|
||||||
|
name: "X-API-Key",
|
||||||
|
"in": "header"
|
||||||
|
},
|
||||||
|
value: "Asdf1234"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it("should return a valid authorize action in OpenAPI 3", () => {
|
||||||
|
const res = preauthorizeApiKey(OAI3_SYSTEM, "APIKeyHeader", "Asdf1234")
|
||||||
|
|
||||||
|
expect(res).toEqual({
|
||||||
|
type: "authorize",
|
||||||
|
payload: {
|
||||||
|
APIKeyHeader: {
|
||||||
|
schema: {
|
||||||
|
type: "apiKey",
|
||||||
|
"in": "header",
|
||||||
|
name: "X-API-Key"
|
||||||
|
},
|
||||||
|
value: "Asdf1234"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it("should return null when the authorization name is invalid in Swagger 2", () => {
|
||||||
|
const res = preauthorizeApiKey(S2_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
|
||||||
|
|
||||||
|
expect(res).toEqual(null)
|
||||||
|
})
|
||||||
|
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
|
||||||
|
const res = preauthorizeApiKey(OAI3_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
|
||||||
|
|
||||||
|
expect(res).toEqual(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -701,6 +701,36 @@ describe("bound system", function(){
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("rootInjects", function() {
|
||||||
|
it("should attach a rootInject function as an instance method", function() {
|
||||||
|
// This is the same thing as the `afterLoad` tests, but is here for posterity
|
||||||
|
|
||||||
|
// Given
|
||||||
|
const system = new System({
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
afterLoad(system) {
|
||||||
|
this.rootInjects.wow = system.dogeSelectors.wow
|
||||||
|
},
|
||||||
|
statePlugins: {
|
||||||
|
doge: {
|
||||||
|
selectors: {
|
||||||
|
wow: () => (system) => {
|
||||||
|
return "so selective"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// When
|
||||||
|
var res = system.getSystem().wow()
|
||||||
|
expect(res).toEqual("so selective")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("error catching", function() {
|
describe("error catching", function() {
|
||||||
it("should encapsulate thrown errors in an afterLoad method", function() {
|
it("should encapsulate thrown errors in an afterLoad method", function() {
|
||||||
// Given
|
// Given
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
<!-- HTML for dev server -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<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" >
|
||||||
|
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||||
|
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||||
|
<style>
|
||||||
|
html
|
||||||
|
{
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after
|
||||||
|
{
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin:0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
|
||||||
|
<defs>
|
||||||
|
<symbol viewBox="0 0 20 20" id="unlocked">
|
||||||
|
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 20 20" id="locked">
|
||||||
|
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 20 20" id="close">
|
||||||
|
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 20 20" id="large-arrow">
|
||||||
|
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 20 20" id="large-arrow-down">
|
||||||
|
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 24 24" id="jump-to">
|
||||||
|
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<symbol viewBox="0 0 24 24" id="expand">
|
||||||
|
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
|
<script src="./swagger-ui-bundle.js"> </script>
|
||||||
|
<script src="./swagger-ui-standalone-preset.js"> </script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
|
||||||
|
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
|
||||||
|
// Build a system
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "http://petstore.swagger.io/v2/swagger.json",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout",
|
||||||
|
onComplete: () => {
|
||||||
|
if(window.completeCount) {
|
||||||
|
window.completeCount++
|
||||||
|
} else {
|
||||||
|
window.completeCount = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const expect = require("expect")
|
||||||
|
|
||||||
|
describe("onComplete option", function () {
|
||||||
|
let mainPage
|
||||||
|
beforeEach(function (client, done) {
|
||||||
|
mainPage = client
|
||||||
|
.url("localhost:3200")
|
||||||
|
.page.main()
|
||||||
|
|
||||||
|
client.waitForElementVisible(".opblock-tag-section", 5000)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("triggers the page-provided onComplete exactly 1 times", function (client, done) {
|
||||||
|
client.execute(function() {
|
||||||
|
return window.completeCount
|
||||||
|
}, [], (result) => {
|
||||||
|
expect(result.value).toEqual(1)
|
||||||
|
client.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -63,7 +63,6 @@ module.exports = require("./make-webpack-config")(rules, {
|
|||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
port: 3200,
|
port: 3200,
|
||||||
contentBase: path.join(__dirname, "dev-helpers"),
|
|
||||||
publicPath: "/",
|
publicPath: "/",
|
||||||
noInfo: true,
|
noInfo: true,
|
||||||
hot: true,
|
hot: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user