Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
003ec46ef5 | ||
|
|
9c8955edd4 | ||
|
|
411884f714 | ||
|
|
7c8e3f4444 | ||
|
|
93d27ff392 | ||
|
|
003a2e7584 | ||
|
|
aefcfcd5d0 | ||
|
|
1f9fb0e68d | ||
|
|
14ef0cc379 | ||
|
|
08cfc77fde | ||
|
|
b8cf080e6c | ||
|
|
245428e7cd | ||
|
|
624a81201f | ||
|
|
b0be261ef8 | ||
|
|
6c5a370ef7 | ||
|
|
fb7f12551f | ||
|
|
49e0466641 | ||
|
|
ad57f5a549 | ||
|
|
ebf94dfccf | ||
|
|
6ba93ad43f | ||
|
|
54153dd929 | ||
|
|
83232dc2d8 | ||
|
|
ad43965d16 | ||
|
|
861cc65cff | ||
|
|
4900b11079 | ||
|
|
b13b05e416 | ||
|
|
feef20dd67 | ||
|
|
4fd7f1d099 | ||
|
|
8777d8b9ff | ||
|
|
22036b1fbf | ||
|
|
043e491f94 | ||
|
|
1fb56c6f96 | ||
|
|
e6722d87aa | ||
|
|
6f7a4c3097 | ||
|
|
71d7c1a5ab | ||
|
|
e41067b4c1 | ||
|
|
558d054986 | ||
|
|
d1779dc47b | ||
|
|
ba1603a7e2 | ||
|
|
ecf688171f | ||
|
|
54ed39d69f | ||
|
|
48a65983c1 | ||
|
|
1f565b4847 | ||
|
|
ba59db2449 | ||
|
|
1013d16d9e | ||
|
|
f0c5f1fe33 | ||
|
|
f589cb99af | ||
|
|
3d7a00c0d0 | ||
|
|
1f44231df9 | ||
|
|
f2a8ed07e0 | ||
|
|
7fd229fe72 | ||
|
|
d9c60d7d25 |
+1
-1
@@ -34,7 +34,7 @@ deploy:
|
||||
node: '6.9'
|
||||
after_success:
|
||||
- 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
|
||||
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
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.9.3 | 2018-01-26 | 2.0, 3.0 | [tag v3.9.3](https://github.com/swagger-api/swagger-ui/tree/v3.9.3)
|
||||
3.13.1 | 2018-03-23 | 2.0, 3.0 | [tag v3.13.1](https://github.com/swagger-api/swagger-ui/tree/v3.13.1)
|
||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
||||
|
||||
@@ -45,11 +45,18 @@
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: "Authorization failed: no accessCode received from the server"
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
Vendored
+8
-1
@@ -45,11 +45,18 @@
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: "Authorization failed: no accessCode received from the server"
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
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
+12
-12
@@ -1,17 +1,17 @@
|
||||
#### Usage
|
||||
- [Installation](docs/usage/installation.md)
|
||||
- [Configuration](docs/usage/configuration.md)
|
||||
- [CORS](docs/usage/cors.md)
|
||||
- [OAuth2](docs/usage/oauth2.md)
|
||||
- [Deep Linking](docs/usage/deep-linking.md)
|
||||
- [Limitations](docs/usage/limitations.md)
|
||||
- [Version detection](docs/usage/version-detection.md)
|
||||
- [Installation](usage/installation.md)
|
||||
- [Configuration](usage/configuration.md)
|
||||
- [CORS](usage/cors.md)
|
||||
- [OAuth2](usage/oauth2.md)
|
||||
- [Deep Linking](usage/deep-linking.md)
|
||||
- [Limitations](usage/limitations.md)
|
||||
- [Version detection](usage/version-detection.md)
|
||||
|
||||
#### Customization
|
||||
- [Overview](docs/customization/overview.md)
|
||||
- [Plugin API](docs/customization/plugin-api.md)
|
||||
- [Custom layout](docs/customization/custom-layout.md)
|
||||
- [Overview](customization/overview.md)
|
||||
- [Plugin API](customization/plugin-api.md)
|
||||
- [Custom layout](customization/custom-layout.md)
|
||||
|
||||
#### Development
|
||||
- [Setting up](docs/development/setting-up.md)
|
||||
- [Scripts](docs/development/scripts.md)
|
||||
- [Setting up](development/setting-up.md)
|
||||
- [Scripts](development/scripts.md)
|
||||
|
||||
@@ -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,14 +2,30 @@
|
||||
|
||||
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
|
||||
|
||||
A plugin return value may contain any of these keys, where `myStateKey` 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:
|
||||
|
||||
```javascript
|
||||
{
|
||||
statePlugins: {
|
||||
myStateKey: {
|
||||
[stateKey]: {
|
||||
actions,
|
||||
reducers,
|
||||
selectors,
|
||||
|
||||
@@ -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.
|
||||
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
|
||||
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
|
||||
`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
|
||||
|
||||
##### Network
|
||||
|
||||
@@ -68,6 +69,7 @@ Parameter Name | Description
|
||||
`requestInterceptor` | `Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
|
||||
`responseInterceptor` |`Function=(a => a)`. MUST be a function. Function to intercept remote definition, Try-It-Out, and OAuth2 responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
|
||||
`showMutatedRequest` | `Boolean=true`. If set to `true`, uses the mutated request returned from a requestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
|
||||
`supportedSubmitMethods` | `Array=["get", "put", "post", "delete", "options", "head", "patch", "trace"]`. List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. This does not filter the operations from the display.
|
||||
`validatorUrl` | `String="https://online.swagger.io/validator" OR null`. By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation.
|
||||
|
||||
##### Macros
|
||||
@@ -76,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
|
||||
`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
|
||||
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`,
|
||||
`useBasicAuthenticationWithAccessCodeGrant`.
|
||||
You can configure OAuth2 authorization by calling the `initOAuth` method.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
const ui = SwaggerUI({...})
|
||||
|
||||
+8
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.9.3",
|
||||
"version": "3.13.1",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
@@ -37,7 +37,8 @@
|
||||
"test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
|
||||
"e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render",
|
||||
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
|
||||
"e2e": "npm-run-all --parallel -r hot-server mock-api test-e2e"
|
||||
"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": {
|
||||
"@braintree/sanitize-url": "^2.0.2",
|
||||
@@ -53,7 +54,7 @@
|
||||
"ieee754": "^1.1.8",
|
||||
"immutable": "^3.x.x",
|
||||
"js-yaml": "^3.5.5",
|
||||
"lodash": "4.17.2",
|
||||
"lodash": "4.17.5",
|
||||
"matcher": "^0.1.2",
|
||||
"memoizee": "0.4.1",
|
||||
"promise-worker": "^1.1.1",
|
||||
@@ -63,6 +64,7 @@
|
||||
"react-addons-shallow-compare": "0.14.8",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-collapse": "^4.0.3",
|
||||
"react-debounce-input": "^3.2.0",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-height": "^2.0.0",
|
||||
"react-hot-loader": "1.3.1",
|
||||
@@ -82,14 +84,14 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "^3.4.6",
|
||||
"swagger-client": "^3.5.2",
|
||||
"url-parse": "^1.1.8",
|
||||
"whatwg-fetch": "0.11.1",
|
||||
"worker-loader": "^0.7.1",
|
||||
"xml": "1.0.1",
|
||||
"xml-but-prettier": "^1.0.1",
|
||||
"yaml-js": "0.2.0",
|
||||
"zenscroll": "4.0.0"
|
||||
"zenscroll": "4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "7.1.1",
|
||||
@@ -143,6 +145,7 @@
|
||||
"standard": "^10.0.2",
|
||||
"standard-loader": "^6.0.1",
|
||||
"style-loader": "0.18.2",
|
||||
"tachyons-sass": "^4.9.2",
|
||||
"url-loader": "0.5.9",
|
||||
"webpack": "^2.6.1",
|
||||
"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
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
!description ? (properties.size ? <div className="markdown"></div> : null) :
|
||||
<Markdown source={ description } />
|
||||
}
|
||||
<span>
|
||||
|
||||
@@ -81,11 +81,11 @@ export default class Auths extends React.Component {
|
||||
}).toArray()
|
||||
}
|
||||
<div className="auth-btn-wrapper">
|
||||
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Done</Button>
|
||||
{
|
||||
nonOauthDefinitions.size === authorizedAuth.size ? <Button className="btn modal-btn auth" onClick={ this.logoutClick }>Logout</Button>
|
||||
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>
|
||||
}
|
||||
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -39,6 +39,13 @@ export default class Oauth2 extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
close = (e) => {
|
||||
e.preventDefault()
|
||||
let { authActions } = this.props
|
||||
|
||||
authActions.showDefinitions(false)
|
||||
}
|
||||
|
||||
authorize =() => {
|
||||
let { authActions, errActions, getConfigs, authSelectors } = this.props
|
||||
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 btn-done" onClick={ this.close }>Close</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import ImPropTypes from "react-immutable-proptypes"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
|
||||
class Path extends React.Component {
|
||||
export class InfoBasePath extends React.Component {
|
||||
static propTypes = {
|
||||
host: 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 {
|
||||
static propTypes = {
|
||||
info: PropTypes.object,
|
||||
@@ -90,6 +101,8 @@ export default class Info extends React.Component {
|
||||
|
||||
const Markdown = getComponent("Markdown")
|
||||
const VersionStamp = getComponent("VersionStamp")
|
||||
const InfoUrl = getComponent("InfoUrl")
|
||||
const InfoBasePath = getComponent("InfoBasePath")
|
||||
|
||||
return (
|
||||
<div className="info">
|
||||
@@ -97,8 +110,8 @@ export default class Info extends React.Component {
|
||||
<h2 className="title" >{ title }
|
||||
{ version && <VersionStamp version={version}></VersionStamp> }
|
||||
</h2>
|
||||
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
|
||||
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
|
||||
{ host || basePath ? <InfoBasePath host={ host } basePath={ basePath } /> : null }
|
||||
{ url && <InfoUrl url={url} /> }
|
||||
</hgroup>
|
||||
|
||||
<div className="description">
|
||||
|
||||
@@ -53,7 +53,7 @@ export default class LiveResponse extends React.Component {
|
||||
|
||||
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)
|
||||
const status = response.get("status")
|
||||
const url = response.get("url")
|
||||
const url = curlRequest.get("url")
|
||||
const headers = response.get("headers").toJS()
|
||||
const notDocumented = response.get("notDocumented")
|
||||
const isError = response.get("error")
|
||||
|
||||
@@ -8,14 +8,17 @@ export default class ModelCollapse extends Component {
|
||||
children: PropTypes.any,
|
||||
title: PropTypes.element,
|
||||
modelName: PropTypes.string,
|
||||
onToggle: PropTypes.func
|
||||
classes: PropTypes.string,
|
||||
onToggle: PropTypes.func,
|
||||
hideSelfOnExpand: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
collapsedContent: "{...}",
|
||||
expanded: false,
|
||||
title: null,
|
||||
onToggle: () => {}
|
||||
onToggle: () => {},
|
||||
hideSelfOnExpand: false
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -29,17 +32,23 @@ export default class ModelCollapse extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps){
|
||||
componentDidMount() {
|
||||
const { hideSelfOnExpand, expanded, modelName } = this.props
|
||||
if(hideSelfOnExpand && expanded) {
|
||||
// We just mounted pre-expanded, and we won't be going back..
|
||||
// So let's give our parent an `onToggle` call..
|
||||
// Since otherwise it will never be called.
|
||||
this.props.onToggle(modelName, expanded)
|
||||
}
|
||||
}
|
||||
|
||||
if(this.props.expanded!= nextProps.expanded){
|
||||
componentWillReceiveProps(nextProps){
|
||||
if(this.props.expanded !== nextProps.expanded){
|
||||
this.setState({expanded: nextProps.expanded})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toggleCollapsed=()=>{
|
||||
|
||||
|
||||
if(this.props.onToggle){
|
||||
this.props.onToggle(this.props.modelName,!this.state.expanded)
|
||||
}
|
||||
@@ -50,9 +59,18 @@ export default class ModelCollapse extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {title} = this.props
|
||||
const { title, classes } = this.props
|
||||
|
||||
if(this.state.expanded ) {
|
||||
if(this.props.hideSelfOnExpand) {
|
||||
return <span className={classes || ""}>
|
||||
{this.props.children}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span className={classes || ""}>
|
||||
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
|
||||
|
||||
@@ -49,6 +49,17 @@ export default class Model extends ImmutablePureComponent {
|
||||
schema = this.getRefSchema( name )
|
||||
}
|
||||
|
||||
if(!schema) {
|
||||
return <span className="model model-title">
|
||||
<span className="model-title__text">{ name }</span>
|
||||
<img src={require("core/../img/rolling-load.svg")} height={"20px"} width={"20px"} style={{
|
||||
marginLeft: "1em",
|
||||
position: "relative",
|
||||
bottom: "0px"
|
||||
}} />
|
||||
</span>
|
||||
}
|
||||
|
||||
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
|
||||
isRef = isRef !== undefined ? isRef : !!$$ref
|
||||
type = schema && schema.get("type") || type
|
||||
|
||||
@@ -6,11 +6,29 @@ export default class Models extends Component {
|
||||
static propTypes = {
|
||||
getComponent: PropTypes.func,
|
||||
specSelectors: PropTypes.object,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object,
|
||||
layoutActions: PropTypes.object,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
getSchemaBasePath = () => {
|
||||
const isOAS3 = this.props.specSelectors.isOAS3()
|
||||
return isOAS3 ? ["components", "schemas"] : ["definitions"]
|
||||
}
|
||||
|
||||
getCollapsedContent = () => {
|
||||
return " "
|
||||
}
|
||||
|
||||
handleToggle = (name, isExpanded) => {
|
||||
const { layoutActions } = this.props
|
||||
layoutActions.show(["models", name], isExpanded)
|
||||
if(isExpanded) {
|
||||
this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
|
||||
let definitions = specSelectors.definitions()
|
||||
@@ -18,10 +36,11 @@ export default class Models extends Component {
|
||||
if (!definitions.size || defaultModelsExpandDepth < 0) return null
|
||||
|
||||
let showModels = layoutSelectors.isShown("models", defaultModelsExpandDepth > 0 && docExpansion !== "none")
|
||||
const specPathBase = specSelectors.isOAS3() ? ["components", "schemas"] : ["definitions"]
|
||||
const specPathBase = this.getSchemaBasePath()
|
||||
|
||||
const ModelWrapper = getComponent("ModelWrapper")
|
||||
const Collapse = getComponent("Collapse")
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
|
||||
return <section className={ showModels ? "models is-open" : "models"}>
|
||||
<h4 onClick={() => layoutActions.show("models", !showModels)}>
|
||||
@@ -32,18 +51,40 @@ export default class Models extends Component {
|
||||
</h4>
|
||||
<Collapse isOpened={showModels}>
|
||||
{
|
||||
definitions.entrySeq().map( ( [ name, model ])=>{
|
||||
definitions.entrySeq().map( ( [ name ])=>{
|
||||
|
||||
const schema = specSelectors.specResolvedSubtree([...specPathBase, name])
|
||||
|
||||
if(layoutSelectors.isShown(["models", name], false) && schema === undefined) {
|
||||
// Firing an action in a container render is not great,
|
||||
// but it works for now.
|
||||
this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])
|
||||
}
|
||||
|
||||
const content = <ModelWrapper name={ name }
|
||||
expandDepth={ defaultModelsExpandDepth }
|
||||
schema={ schema || Im.Map() }
|
||||
specPath={Im.List([...specPathBase, name])}
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
getConfigs = {getConfigs}
|
||||
layoutSelectors = {layoutSelectors}
|
||||
layoutActions = {layoutActions}/>
|
||||
|
||||
const title = <span className="model-box">
|
||||
<span className="model model-title">{name}</span>
|
||||
</span>
|
||||
|
||||
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
|
||||
<ModelWrapper name={ name }
|
||||
expandDepth={ defaultModelsExpandDepth }
|
||||
schema={ model }
|
||||
specPath={Im.List([...specPathBase, name])}
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
getConfigs = {getConfigs}
|
||||
layoutSelectors = {layoutSelectors}
|
||||
layoutActions = {layoutActions}/>
|
||||
<ModelCollapse
|
||||
classes="model-box"
|
||||
collapsedContent={this.getCollapsedContent(name)}
|
||||
onToggle={this.handleToggle}
|
||||
title={title}
|
||||
modelName={name}
|
||||
hideSelfOnExpand={true}
|
||||
expanded={defaultModelsExpandDepth > 1}
|
||||
>{content}</ModelCollapse>
|
||||
</div>
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class ValidatorImage extends React.Component {
|
||||
if (this.state.error) {
|
||||
return <img alt={"Error"} />
|
||||
} else if (!this.state.loaded) {
|
||||
return <img alt= {"Loading..."} />
|
||||
return null
|
||||
}
|
||||
return <img src={this.props.src} alt={this.props.alt} />
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ import { getExtensions, sanitizeUrl } from "core/utils"
|
||||
import { Iterable, List } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
|
||||
export default class Operation extends PureComponent {
|
||||
static propTypes = {
|
||||
specPath: ImPropTypes.list.isRequired,
|
||||
operation: PropTypes.instanceOf(Iterable).isRequired,
|
||||
summary: PropTypes.string,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
|
||||
@@ -34,7 +36,8 @@ export default class Operation extends PureComponent {
|
||||
operation: null,
|
||||
response: null,
|
||||
request: null,
|
||||
specPath: List()
|
||||
specPath: List(),
|
||||
summary: ""
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -59,6 +62,8 @@ export default class Operation extends PureComponent {
|
||||
let operationProps = this.props.operation
|
||||
|
||||
let {
|
||||
summary,
|
||||
deprecated,
|
||||
isShown,
|
||||
isAuthorized,
|
||||
path,
|
||||
@@ -76,14 +81,13 @@ export default class Operation extends PureComponent {
|
||||
} = operationProps.toJS()
|
||||
|
||||
let {
|
||||
summary,
|
||||
summary: resolvedSummary,
|
||||
description,
|
||||
deprecated,
|
||||
externalDocs,
|
||||
schemes
|
||||
} = op.operation
|
||||
} = op
|
||||
|
||||
let operation = operationProps.getIn(["op", "operation"])
|
||||
let operation = operationProps.getIn(["op"])
|
||||
let security = operationProps.get("security")
|
||||
let responses = operation.get("responses")
|
||||
let produces = operation.get("produces")
|
||||
@@ -132,7 +136,7 @@ export default class Operation extends PureComponent {
|
||||
|
||||
{ !showSummary ? null :
|
||||
<div className="opblock-summary-description">
|
||||
{ summary }
|
||||
{ resolvedSummary || summary }
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -152,6 +156,9 @@ export default class Operation extends PureComponent {
|
||||
|
||||
<Collapse isOpened={isShown}>
|
||||
<div className="opblock-body">
|
||||
{ (operation && operation.size) || operation === null ? null :
|
||||
<img height={"32px"} width={"32px"} src={require("core/../img/rolling-load.svg")} className="opblock-loading-animation" />
|
||||
}
|
||||
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
|
||||
{ description &&
|
||||
<div className="opblock-description-wrapper">
|
||||
@@ -173,23 +180,25 @@ export default class Operation extends PureComponent {
|
||||
</div> : null
|
||||
}
|
||||
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
specPath={specPath.push("parameters")}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
{ !operation || !operation.size ? null :
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
specPath={specPath.push("parameters")}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
|
||||
fn={fn}
|
||||
getComponent={ getComponent }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ [path, method] }
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
fn={fn}
|
||||
getComponent={ getComponent }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ [path, method] }
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
}
|
||||
|
||||
{ !tryItOutEnabled ? null :
|
||||
<OperationServers
|
||||
|
||||
@@ -21,7 +21,8 @@ export default class Operations extends React.Component {
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
fn: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -30,7 +31,8 @@ export default class Operations extends React.Component {
|
||||
getComponent,
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
getConfigs
|
||||
getConfigs,
|
||||
fn
|
||||
} = this.props
|
||||
|
||||
let taggedOps = specSelectors.taggedOperations()
|
||||
@@ -52,9 +54,7 @@ export default class Operations extends React.Component {
|
||||
|
||||
if (filter) {
|
||||
if (filter !== true) {
|
||||
taggedOps = taggedOps.filter((tagObj, tag) => {
|
||||
return tag.indexOf(filter) !== -1
|
||||
})
|
||||
taggedOps = fn.opsFilter(taggedOps, filter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
|
||||
|
||||
updateValues = (props) => {
|
||||
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
|
||||
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : fromJS({})
|
||||
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})
|
||||
let isXml = /xml/i.test(consumesValue)
|
||||
let isJson = /json/i.test(consumesValue)
|
||||
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
|
||||
@@ -107,7 +107,7 @@ export default class ParamBody extends PureComponent {
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
const ContentType = getComponent("contentType")
|
||||
// for domains where specSelectors not passed
|
||||
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : param
|
||||
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : param
|
||||
let errors = parameter.get("errors", List())
|
||||
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
|
||||
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
|
||||
|
||||
@@ -24,11 +24,18 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let { specSelectors, pathMethod, param } = props
|
||||
let defaultValue = param.get("default")
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let xExampleValue = param.get("x-example")
|
||||
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||
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) {
|
||||
@@ -37,7 +44,7 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let example = param.get("example")
|
||||
let defaultValue = param.get("default")
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||
let enumValue
|
||||
|
||||
if(isOAS3()) {
|
||||
@@ -98,17 +105,17 @@ export default class ParameterRow extends Component {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ParameterExt = getComponent("ParameterExt")
|
||||
|
||||
let paramWithMeta = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||
|
||||
let schema = param.get("schema")
|
||||
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type")
|
||||
let isFormData = inType === "formData"
|
||||
let isFormDataSupported = "FormData" in win
|
||||
let required = param.get("required")
|
||||
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let value = parameter ? parameter.get("value") : ""
|
||||
let value = paramWithMeta ? paramWithMeta.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
|
||||
let paramItems // undefined
|
||||
let paramItemsEnum // undefined
|
||||
let isDisplayParamItemsEnum = false
|
||||
|
||||
@@ -101,7 +101,7 @@ export default class Parameters extends Component {
|
||||
specPath={specPath.push(i.toString())}
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
param={ parameter }
|
||||
param={ specSelectors.parameterWithMeta(pathMethod, parameter.get("name"), parameter.get("in")) }
|
||||
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
|
||||
onChange={ this.onChange }
|
||||
onChangeConsumes={this.onChangeConsumesWrapper}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { getExtensions } from "core/utils"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
const propStyle = { color: "#6b6b6b", fontStyle: "italic" }
|
||||
|
||||
export default class Primitive extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -3,7 +3,17 @@ import PropTypes from "prop-types"
|
||||
import Remarkable from "remarkable"
|
||||
import sanitize from "sanitize-html"
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const isPlainText = (str) => /^[A-Z\s0-9!?\.]+$/gi.test(str)
|
||||
|
||||
function Markdown({ source }) {
|
||||
if(isPlainText(source)) {
|
||||
// If the source text is not Markdown,
|
||||
// let's save some time and just render it.
|
||||
return <div className="markdown">
|
||||
{source}
|
||||
</div>
|
||||
}
|
||||
const html = new Remarkable({
|
||||
html: true,
|
||||
typographer: true,
|
||||
@@ -29,11 +39,14 @@ Markdown.propTypes = {
|
||||
export default Markdown
|
||||
|
||||
const sanitizeOptions = {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img" ]),
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img", "span" ]),
|
||||
allowedAttributes: {
|
||||
...sanitize.defaults.allowedAttributes,
|
||||
"img": sanitize.defaults.allowedAttributes.img.concat(["title"])
|
||||
"img": sanitize.defaults.allowedAttributes.img.concat(["title"]),
|
||||
"td": [ "colspan" ],
|
||||
"*": [ "class" ]
|
||||
},
|
||||
allowedSchemesByTag: { img: [ "http", "https", "data" ] },
|
||||
textFilter: function(text) {
|
||||
return text.replace(/"/g, "\"")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from "prop-types"
|
||||
import formatXml from "xml-but-prettier"
|
||||
import lowerCase from "lodash/lowerCase"
|
||||
import { extractFileNameFromContentDispositionHeader } from "core/utils"
|
||||
import win from "core/window"
|
||||
|
||||
export default class ResponseBody extends React.Component {
|
||||
|
||||
@@ -47,7 +48,11 @@ export default class ResponseBody extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
|
||||
if(win.navigator && win.navigator.msSaveOrOpenBlob) {
|
||||
bodyEl = <div><a href={ href } onClick={() => win.navigator.msSaveOrOpenBlob(blob, download)}>{ "Download file" }</a></div>
|
||||
} else {
|
||||
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
|
||||
}
|
||||
} else {
|
||||
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { helpers } from "swagger-client"
|
||||
import { Iterable, fromJS } from "immutable"
|
||||
import { Iterable, fromJS, Map } from "immutable"
|
||||
|
||||
const { opId } = helpers
|
||||
|
||||
@@ -56,13 +56,13 @@ export default class OperationContainer extends PureComponent {
|
||||
|
||||
mapStateToProps(nextState, props) {
|
||||
const { op, layoutSelectors, getConfigs } = props
|
||||
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration } = getConfigs()
|
||||
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration, supportedSubmitMethods } = getConfigs()
|
||||
const showSummary = layoutSelectors.showSummary()
|
||||
const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
|
||||
const isShownKey = ["operations", props.tag, operationId]
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
const allowTryItOut = typeof props.allowTryItOut === "undefined" ?
|
||||
props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut
|
||||
const allowTryItOut = supportedSubmitMethods.indexOf(props.method) >= 0 && (typeof props.allowTryItOut === "undefined" ?
|
||||
props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut)
|
||||
const security = op.getIn(["operation", "security"]) || props.specSelectors.security()
|
||||
|
||||
return {
|
||||
@@ -81,14 +81,34 @@ export default class OperationContainer extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { isShown } = this.props
|
||||
const resolvedSubtree = this.getResolvedSubtree()
|
||||
|
||||
if(isShown && resolvedSubtree === undefined) {
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(nextProps.response !== this.props.response) {
|
||||
const { response, isShown } = nextProps
|
||||
const resolvedSubtree = this.getResolvedSubtree()
|
||||
|
||||
if(response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if(isShown && resolvedSubtree === undefined) {
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, tag, operationId, isShown } = this.props
|
||||
if(!isShown) {
|
||||
// transitioning from collapsed to expanded
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
layoutActions.show(["operations", tag, operationId], !isShown)
|
||||
}
|
||||
|
||||
@@ -106,9 +126,40 @@ export default class OperationContainer extends PureComponent {
|
||||
this.setState({ executeInProgress: true })
|
||||
}
|
||||
|
||||
getResolvedSubtree = () => {
|
||||
const {
|
||||
specSelectors,
|
||||
path,
|
||||
method,
|
||||
specPath
|
||||
} = this.props
|
||||
|
||||
if(specPath) {
|
||||
return specSelectors.specResolvedSubtree(specPath.toJS())
|
||||
}
|
||||
|
||||
return specSelectors.specResolvedSubtree(["paths", path, method])
|
||||
}
|
||||
|
||||
requestResolvedSubtree = () => {
|
||||
const {
|
||||
specActions,
|
||||
path,
|
||||
method,
|
||||
specPath
|
||||
} = this.props
|
||||
|
||||
|
||||
if(specPath) {
|
||||
return specActions.requestResolvedSubtree(specPath.toJS())
|
||||
}
|
||||
|
||||
return specActions.requestResolvedSubtree(["paths", path, method])
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
op,
|
||||
op: unresolvedOp,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
@@ -140,10 +191,14 @@ export default class OperationContainer extends PureComponent {
|
||||
|
||||
const Operation = getComponent( "operation" )
|
||||
|
||||
const resolvedSubtree = this.getResolvedSubtree() || Map()
|
||||
|
||||
const operationProps = fromJS({
|
||||
op,
|
||||
op: resolvedSubtree,
|
||||
tag,
|
||||
path,
|
||||
summary: unresolvedOp.getIn(["operation", "summary"]) || "",
|
||||
deprecated: resolvedSubtree.get("deprecated") || unresolvedOp.getIn(["operation", "deprecated"]) || false,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
|
||||
@@ -49,6 +49,16 @@ module.exports = function SwaggerUI(opts) {
|
||||
defaultModelExpandDepth: 1,
|
||||
defaultModelsExpandDepth: 1,
|
||||
showExtensions: false,
|
||||
supportedSubmitMethods: [
|
||||
"get",
|
||||
"put",
|
||||
"post",
|
||||
"delete",
|
||||
"options",
|
||||
"head",
|
||||
"patch",
|
||||
"trace"
|
||||
],
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { PureComponent, Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List, fromJS } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import DebounceInput from "react-debounce-input"
|
||||
//import "less/json-schema-form"
|
||||
|
||||
const noop = ()=> {}
|
||||
@@ -79,10 +80,13 @@ export class JsonSchema_string extends Component {
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
else {
|
||||
return (<Input type={ schema.format === "password" ? "password" : "text" }
|
||||
return (<DebounceInput
|
||||
type={ schema.format === "password" ? "password" : "text" }
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={value}
|
||||
minLength={0}
|
||||
debounceTimeout={350}
|
||||
placeholder={description}
|
||||
onChange={ this.onChange }
|
||||
disabled={isDisabled}/>)
|
||||
|
||||
@@ -8,6 +8,8 @@ export default function() {
|
||||
afterLoad(system) {
|
||||
this.rootInjects = this.rootInjects || {}
|
||||
this.rootInjects.initOAuth = system.authActions.configureAuth
|
||||
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
|
||||
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
|
||||
},
|
||||
statePlugins: {
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { escapeDeepLinkPath } from "core/utils"
|
||||
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
|
||||
|
||||
|
||||
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||
export const updateJsonSpec = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||
ori(...args)
|
||||
|
||||
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import win from "../window"
|
||||
|
||||
export default function downloadUrlPlugin (toolbox) {
|
||||
let { fn } = toolbox
|
||||
@@ -12,6 +13,7 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
const config = getConfigs()
|
||||
url = url || specSelectors.url()
|
||||
specActions.updateLoadingStatus("loading")
|
||||
errActions.clear({source: "fetch"})
|
||||
fetch({
|
||||
url,
|
||||
loadSpec: true,
|
||||
@@ -26,11 +28,48 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
function next(res) {
|
||||
if(res instanceof Error || res.status >= 400) {
|
||||
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.updateSpec(res.text)
|
||||
specActions.updateUrl(url)
|
||||
if(specSelectors.url() !== url) {
|
||||
specActions.updateUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ export const NEW_SPEC_ERR = "err_new_spec_err"
|
||||
export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
||||
export const NEW_AUTH_ERR = "err_new_auth_err"
|
||||
export const CLEAR = "err_clear"
|
||||
export const CLEAR_BY = "err_clear_by"
|
||||
|
||||
export function newThrownErr(err) {
|
||||
return {
|
||||
@@ -49,3 +50,11 @@ export function clear(filter = {}) {
|
||||
payload: filter
|
||||
}
|
||||
}
|
||||
|
||||
export function clearBy(filter = () => true) {
|
||||
// filter is a function
|
||||
return {
|
||||
type: CLEAR_BY,
|
||||
payload: filter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import {
|
||||
NEW_SPEC_ERR,
|
||||
NEW_SPEC_ERR_BATCH,
|
||||
NEW_AUTH_ERR,
|
||||
CLEAR
|
||||
CLEAR,
|
||||
CLEAR_BY,
|
||||
} from "./actions"
|
||||
|
||||
import reject from "lodash/reject"
|
||||
|
||||
import Im, { fromJS, List } from "immutable"
|
||||
import { fromJS, List } from "immutable"
|
||||
|
||||
import transformErrors from "./error-transformers/hook"
|
||||
|
||||
@@ -65,11 +64,34 @@ export default function(system) {
|
||||
},
|
||||
|
||||
[CLEAR]: (state, { payload }) => {
|
||||
if(!payload) {
|
||||
return
|
||||
if(!payload || !state.get("errors")) {
|
||||
return state
|
||||
}
|
||||
// TODO: Rework, to use immutable only, no need for lodash
|
||||
let newErrors = Im.fromJS(reject((state.get("errors") || List()).toJS(), payload))
|
||||
|
||||
let newErrors = state.get("errors")
|
||||
.filter(err => {
|
||||
return err.keySeq().every(k => {
|
||||
const errValue = err.get(k)
|
||||
const filterValue = payload[k]
|
||||
|
||||
if(!filterValue) return true
|
||||
|
||||
return errValue !== filterValue
|
||||
})
|
||||
})
|
||||
return state.merge({
|
||||
errors: newErrors
|
||||
})
|
||||
},
|
||||
|
||||
[CLEAR_BY]: (state, { payload }) => {
|
||||
if(!payload || typeof payload !== "function") {
|
||||
return state
|
||||
}
|
||||
let newErrors = state.get("errors")
|
||||
.filter(err => {
|
||||
return payload(err)
|
||||
})
|
||||
return state.merge({
|
||||
errors: newErrors
|
||||
})
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import opsFilter from "./opsFilter"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
fn: {
|
||||
opsFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function(taggedOps, phrase) {
|
||||
return taggedOps.filter((tagObj, tag) => tag.indexOf(phrase) !== -1)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import ImPropTypes from "react-immutable-proptypes"
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
const Callbacks = (props) => {
|
||||
let { callbacks, getComponent } = props
|
||||
let { callbacks, getComponent, specPath } = props
|
||||
// const Markdown = getComponent("Markdown")
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
|
||||
@@ -28,6 +28,7 @@ const Callbacks = (props) => {
|
||||
tag={""}
|
||||
method={method}
|
||||
path={pathItemName}
|
||||
specPath={specPath.push(callbackName, pathItemName, method)}
|
||||
allowTryItOut={false}
|
||||
/>
|
||||
}) }
|
||||
@@ -42,8 +43,8 @@ const Callbacks = (props) => {
|
||||
|
||||
Callbacks.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
callbacks: ImPropTypes.iterable.isRequired
|
||||
|
||||
callbacks: ImPropTypes.iterable.isRequired,
|
||||
specPath: ImPropTypes.list.isRequired,
|
||||
}
|
||||
|
||||
export default Callbacks
|
||||
|
||||
@@ -27,6 +27,7 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
|
||||
this.state = {
|
||||
isEditBox: false,
|
||||
userDidModify: false,
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
@@ -59,6 +60,11 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
this.onChange(this.sample(explicitMediaType))
|
||||
}
|
||||
|
||||
resetValueToSample = (explicitMediaType) => {
|
||||
this.setState({ userDidModify: false })
|
||||
this.setValueToSample(explicitMediaType)
|
||||
}
|
||||
|
||||
sample = (explicitMediaType) => {
|
||||
let { requestBody, mediaType } = this.props
|
||||
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 inputValue = isJson ? e.target.value.trim() : e.target.value
|
||||
|
||||
this.setState({ userDidModify: true })
|
||||
this.onChange(inputValue)
|
||||
}
|
||||
|
||||
@@ -87,13 +94,14 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
let {
|
||||
isExecute,
|
||||
getComponent,
|
||||
mediaType,
|
||||
} = this.props
|
||||
|
||||
const Button = getComponent("Button")
|
||||
const TextArea = getComponent("TextArea")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
|
||||
let { value, isEditBox } = this.state
|
||||
let { value, isEditBox, userDidModify } = this.state
|
||||
|
||||
return (
|
||||
<div className="body-param">
|
||||
@@ -104,14 +112,18 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
value={ value }/>)
|
||||
}
|
||||
<div className="body-param-options">
|
||||
{
|
||||
!isExecute ? null
|
||||
: <div className="body-param-edit">
|
||||
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
|
||||
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
<div className="body-param-edit">
|
||||
{
|
||||
!isExecute ? null
|
||||
: <Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
|
||||
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
|
||||
</Button>
|
||||
|
||||
}
|
||||
{ userDidModify &&
|
||||
<Button className="btn ml3" onClick={() => { this.resetValueToSample(mediaType) }}>Reset</Button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ const state = state => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
const nullSelector = createSelector(() => null)
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
|
||||
|
||||
@@ -161,7 +161,10 @@ class Parameters extends Component {
|
||||
</div> : "" }
|
||||
|
||||
{this.state.callbackVisible ? <div className="callbacks-container opblock-description-wrapper">
|
||||
<Callbacks callbacks={Map(operation.get("callbacks"))} />
|
||||
<Callbacks
|
||||
callbacks={Map(operation.get("callbacks"))}
|
||||
specPath={specPath.slice(0, -1).push("callbacks")}
|
||||
/>
|
||||
</div> : "" }
|
||||
{
|
||||
isOAS3() && requestBody && this.state.parametersVisible &&
|
||||
|
||||
@@ -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 { includeReadOnly, includeWriteOnly } = config
|
||||
|
||||
if(example && example.$$ref) {
|
||||
delete example.$$ref
|
||||
}
|
||||
|
||||
if(example !== undefined)
|
||||
return example
|
||||
|
||||
@@ -69,6 +73,14 @@ export const sampleFromSchema = (schema, config={}) => {
|
||||
}
|
||||
|
||||
if(type === "array") {
|
||||
if(Array.isArray(items.anyOf)) {
|
||||
return items.anyOf.map(i => sampleFromSchema(i, config))
|
||||
}
|
||||
|
||||
if(Array.isArray(items.oneOf)) {
|
||||
return items.oneOf.map(i => sampleFromSchema(i, config))
|
||||
}
|
||||
|
||||
return [ sampleFromSchema(items, config) ]
|
||||
}
|
||||
|
||||
@@ -202,7 +214,9 @@ export const sampleXmlFromSchema = (schema, config={}) => {
|
||||
|| enumAttrVal || primitive(props[propName])
|
||||
} else {
|
||||
props[propName].xml.name = props[propName].xml.name || propName
|
||||
props[propName].example = props[propName].example !== undefined ? props[propName].example : example[propName]
|
||||
if(props[propName].example === undefined && example[propName] !== undefined) {
|
||||
props[propName].example = example[propName]
|
||||
}
|
||||
let t = sampleXmlFromSchema(props[propName])
|
||||
if (Array.isArray(t)) {
|
||||
res[displayName] = res[displayName].concat(t)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import YAML from "js-yaml"
|
||||
import { Map } from "immutable"
|
||||
import parseUrl from "url-parse"
|
||||
import serializeError from "serialize-error"
|
||||
import isString from "lodash/isString"
|
||||
import debounce from "lodash/debounce"
|
||||
import set from "lodash/set"
|
||||
import { isJSONObject } from "core/utils"
|
||||
|
||||
// Actions conform to FSA (flux-standard-actions)
|
||||
@@ -21,6 +24,7 @@ export const CLEAR_REQUEST = "spec_clear_request"
|
||||
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
|
||||
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
|
||||
export const UPDATE_RESOLVED = "spec_update_resolved"
|
||||
export const UPDATE_RESOLVED_SUBTREE = "spec_update_resolved_subtree"
|
||||
export const SET_SCHEME = "set_scheme"
|
||||
|
||||
const toStr = (str) => isString(str) ? str : ""
|
||||
@@ -74,7 +78,14 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
|
||||
return {}
|
||||
}
|
||||
|
||||
let hasWarnedAboutResolveSpecDeprecation = false
|
||||
|
||||
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
|
||||
if(!hasWarnedAboutResolveSpecDeprecation) {
|
||||
console.warn(`specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!`)
|
||||
hasWarnedAboutResolveSpecDeprecation = true
|
||||
}
|
||||
|
||||
const {
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
@@ -124,6 +135,98 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
|
||||
})
|
||||
}
|
||||
|
||||
let requestBatch = []
|
||||
|
||||
const debResolveSubtrees = debounce(async () => {
|
||||
const system = requestBatch.system // Just a reference to the "latest" system
|
||||
|
||||
if(!system) {
|
||||
console.error("debResolveSubtrees: don't have a system to operate on, aborting.")
|
||||
return
|
||||
}
|
||||
const {
|
||||
errActions,
|
||||
errSelectors,
|
||||
fn: {
|
||||
resolveSubtree,
|
||||
AST: { getLineNumberForPath }
|
||||
},
|
||||
specSelectors,
|
||||
specActions,
|
||||
} = system
|
||||
|
||||
if(!resolveSubtree) {
|
||||
console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.")
|
||||
return
|
||||
}
|
||||
|
||||
const specStr = specSelectors.specStr()
|
||||
|
||||
const {
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
} = system.getConfigs()
|
||||
|
||||
try {
|
||||
var batchResult = await requestBatch.reduce(async (prev, path) => {
|
||||
const { resultMap, specWithCurrentSubtrees } = await prev
|
||||
const { errors, spec } = await resolveSubtree(specWithCurrentSubtrees, path, {
|
||||
baseDoc: specSelectors.url(),
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
})
|
||||
|
||||
if(errSelectors.allErrors().size) {
|
||||
errActions.clear({
|
||||
type: "thrown"
|
||||
})
|
||||
}
|
||||
|
||||
if(Array.isArray(errors) && errors.length > 0) {
|
||||
let preparedErrors = errors
|
||||
.map(err => {
|
||||
err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null
|
||||
err.path = err.fullPath ? err.fullPath.join(".") : null
|
||||
err.level = "error"
|
||||
err.type = "thrown"
|
||||
err.source = "resolver"
|
||||
Object.defineProperty(err, "message", { enumerable: true, value: err.message })
|
||||
return err
|
||||
})
|
||||
errActions.newThrownErrBatch(preparedErrors)
|
||||
}
|
||||
|
||||
set(resultMap, path, spec)
|
||||
set(specWithCurrentSubtrees, path, spec)
|
||||
|
||||
return {
|
||||
resultMap,
|
||||
specWithCurrentSubtrees
|
||||
}
|
||||
}, Promise.resolve({
|
||||
resultMap: (specSelectors.specResolvedSubtree([]) || Map()).toJS(),
|
||||
specWithCurrentSubtrees: specSelectors.specJson().toJS()
|
||||
}))
|
||||
|
||||
delete requestBatch.system
|
||||
requestBatch = [] // Clear stack
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
specActions.updateResolvedSubtree([], batchResult.resultMap)
|
||||
}, 35)
|
||||
|
||||
export const requestResolvedSubtree = path => system => {
|
||||
requestBatch.push(path)
|
||||
requestBatch.system = system
|
||||
debResolveSubtrees()
|
||||
}
|
||||
|
||||
export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||
return {
|
||||
type: UPDATE_PARAM,
|
||||
@@ -131,6 +234,23 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||
}
|
||||
}
|
||||
|
||||
export const updateResolvedSubtree = (path, value) => {
|
||||
return {
|
||||
type: UPDATE_RESOLVED_SUBTREE,
|
||||
payload: { path, value }
|
||||
}
|
||||
}
|
||||
|
||||
export const invalidateResolvedSubtreeCache = () => {
|
||||
return {
|
||||
type: UPDATE_RESOLVED_SUBTREE,
|
||||
payload: {
|
||||
path: [],
|
||||
value: Map()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const validateParams = ( payload, isOAS3 ) =>{
|
||||
return {
|
||||
type: VALIDATE_PARAMS,
|
||||
@@ -251,6 +371,7 @@ export const executeRequest = (req) =>
|
||||
// track duration of request
|
||||
const startTime = Date.now()
|
||||
|
||||
|
||||
return fn.execute(req)
|
||||
.then( res => {
|
||||
res.duration = Date.now() - startTime
|
||||
@@ -267,13 +388,22 @@ export const executeRequest = (req) =>
|
||||
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
|
||||
export const execute = ( { path, method, ...extras }={} ) => (system) => {
|
||||
let { fn:{fetch}, specSelectors, specActions } = system
|
||||
let spec = specSelectors.spec().toJS()
|
||||
let spec = specSelectors.specJsonWithResolvedSubtrees().toJS()
|
||||
let scheme = specSelectors.operationScheme(path, method)
|
||||
let { requestContentType, responseContentType } = specSelectors.contentTypeValues([path, method]).toJS()
|
||||
let isXml = /xml/i.test(requestContentType)
|
||||
let parameters = specSelectors.parameterValues([path, method], isXml).toJS()
|
||||
|
||||
return specActions.executeRequest({fetch, spec, pathName: path, method, parameters, requestContentType, scheme, responseContentType, ...extras })
|
||||
return specActions.executeRequest({
|
||||
...extras,
|
||||
fetch,
|
||||
spec,
|
||||
pathName: path,
|
||||
method, parameters,
|
||||
requestContentType,
|
||||
scheme,
|
||||
responseContentType
|
||||
})
|
||||
}
|
||||
|
||||
export function clearResponse (path, method) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { fromJS } from "immutable"
|
||||
import { fromJS, List } from "immutable"
|
||||
import { fromJSOrdered, validateParam } from "core/utils"
|
||||
import win from "../../window"
|
||||
|
||||
// selector-in-reducer is suboptimal, but `operationWithMeta` is more of a helper
|
||||
import {
|
||||
operationWithMeta
|
||||
} from "./selectors"
|
||||
|
||||
import {
|
||||
UPDATE_SPEC,
|
||||
UPDATE_URL,
|
||||
@@ -12,6 +17,7 @@ import {
|
||||
SET_REQUEST,
|
||||
SET_MUTATED_REQUEST,
|
||||
UPDATE_RESOLVED,
|
||||
UPDATE_RESOLVED_SUBTREE,
|
||||
UPDATE_OPERATION_META_VALUE,
|
||||
CLEAR_RESPONSE,
|
||||
CLEAR_REQUEST,
|
||||
@@ -39,38 +45,38 @@ export default {
|
||||
return state.setIn(["resolved"], fromJSOrdered(action.payload))
|
||||
},
|
||||
|
||||
[UPDATE_PARAM]: ( state, {payload} ) => {
|
||||
let { path, paramName, paramIn, value, isXml } = payload
|
||||
[UPDATE_RESOLVED_SUBTREE]: (state, action) => {
|
||||
const { value, path } = action.payload
|
||||
return state.setIn(["resolvedSubtrees", ...path], fromJSOrdered(value))
|
||||
},
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
|
||||
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
|
||||
if (!(value instanceof win.File)) {
|
||||
value = fromJSOrdered( value )
|
||||
}
|
||||
return parameters.setIn( [ index, isXml ? "value_xml" : "value" ], value)
|
||||
})
|
||||
[UPDATE_PARAM]: ( state, {payload} ) => {
|
||||
let { path: pathMethod, paramName, paramIn, value, isXml } = payload
|
||||
|
||||
const valueKey = isXml ? "value_xml" : "value"
|
||||
|
||||
return state.setIn(
|
||||
["meta", "paths", ...pathMethod, "parameters", `${paramName}.${paramIn}`, valueKey],
|
||||
value
|
||||
)
|
||||
},
|
||||
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
|
||||
let isXml = /xml/i.test(meta.get("consumes_value"))
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
let errors = validateParam(parameters.get(i), isXml, isOAS3)
|
||||
parameters.setIn([i, "errors"], fromJS(errors))
|
||||
}
|
||||
})
|
||||
const op = operationWithMeta(state, ...pathMethod)
|
||||
|
||||
return state.updateIn(["meta", "paths", ...pathMethod, "parameters"], fromJS({}), paramMeta => {
|
||||
return op.get("parameters", List()).reduce((res, param) => {
|
||||
const errors = validateParam(param, isXml, isOAS3)
|
||||
return res.setIn([`${param.get("name")}.${param.get("in")}`, "errors"], fromJS(errors))
|
||||
}, paramMeta)
|
||||
})
|
||||
},
|
||||
[CLEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
parameters.setIn([i, "errors"], fromJS([]))
|
||||
}
|
||||
})
|
||||
return state.updateIn( [ "meta", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.map(param => param.set("errors", fromJS([])))
|
||||
})
|
||||
},
|
||||
|
||||
@@ -109,10 +115,10 @@ export default {
|
||||
|
||||
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
|
||||
// path is a pathMethod tuple... can't change the name now.
|
||||
let operationPath = ["resolved", "paths", ...path]
|
||||
let operationPath = ["paths", ...path]
|
||||
let metaPath = ["meta", "paths", ...path]
|
||||
|
||||
if(!state.getIn(operationPath)) {
|
||||
if(!state.getIn(["json", ...operationPath]) && !state.getIn(["resolved", ...operationPath])) {
|
||||
// do nothing if the operation does not exist
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -42,9 +42,40 @@ export const specResolved = createSelector(
|
||||
spec => spec.get("resolved", Map())
|
||||
)
|
||||
|
||||
export const specResolvedSubtree = (state, path) => {
|
||||
return state.getIn(["resolvedSubtrees", ...path], undefined)
|
||||
}
|
||||
|
||||
const mergerFn = (oldVal, newVal) => {
|
||||
if(Map.isMap(oldVal) && Map.isMap(newVal)) {
|
||||
if(newVal.get("$$ref")) {
|
||||
// resolver artifacts indicated that this key was directly resolved
|
||||
// so we should drop the old value entirely
|
||||
return newVal
|
||||
}
|
||||
|
||||
return Map().mergeWith(
|
||||
mergerFn,
|
||||
oldVal,
|
||||
newVal
|
||||
)
|
||||
}
|
||||
|
||||
return newVal
|
||||
}
|
||||
|
||||
export const specJsonWithResolvedSubtrees = createSelector(
|
||||
state,
|
||||
spec => Map().mergeWith(
|
||||
mergerFn,
|
||||
spec.get("json"),
|
||||
spec.get("resolvedSubtrees")
|
||||
)
|
||||
)
|
||||
|
||||
// Default Spec ( as an object )
|
||||
export const spec = state => {
|
||||
let res = specResolved(state)
|
||||
let res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -137,7 +168,9 @@ export const securityDefinitions = createSelector(
|
||||
|
||||
|
||||
export const findDefinition = ( state, name ) => {
|
||||
return specResolved(state).getIn(["definitions", name], null)
|
||||
const resolvedRes = state.getIn(["resolvedSubtrees", "definitions", name], null)
|
||||
const unresolvedRes = state.getIn(["json", "definitions", name], null)
|
||||
return resolvedRes || unresolvedRes || null
|
||||
}
|
||||
|
||||
export const definitions = createSelector(
|
||||
@@ -261,10 +294,40 @@ export const allowTryItOutFor = () => {
|
||||
return true
|
||||
}
|
||||
|
||||
export const operationWithMeta = (state, path, method) => {
|
||||
const op = specJsonWithResolvedSubtrees(state).getIn(["paths", path, method], Map())
|
||||
const meta = state.getIn(["meta", "paths", path, method], Map())
|
||||
|
||||
const mergedParams = op.get("parameters", List()).map((param) => {
|
||||
return Map().merge(
|
||||
param,
|
||||
meta.getIn(["parameters", `${param.get("name")}.${param.get("in")}`])
|
||||
)
|
||||
})
|
||||
|
||||
return Map()
|
||||
.merge(op, meta)
|
||||
.set("parameters", mergedParams)
|
||||
}
|
||||
|
||||
export const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {
|
||||
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], Map())
|
||||
const metaParams = state.getIn(["meta", "paths", ...pathMethod, "parameters"], Map())
|
||||
|
||||
const mergedParams = opParams.map((param) => {
|
||||
return Map().merge(
|
||||
param,
|
||||
metaParams.get(`${param.get("name")}.${param.get("in")}`)
|
||||
)
|
||||
})
|
||||
|
||||
return mergedParams.find(param => param.get("in") === paramIn && param.get("name") === paramName, Map())
|
||||
}
|
||||
|
||||
// Get the parameter value by parameter name
|
||||
export function getParameter(state, pathMethod, name, inType) {
|
||||
pathMethod = pathMethod || []
|
||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let params = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
return params.find( (p) => {
|
||||
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
|
||||
}) || Map() // Always return a map
|
||||
@@ -281,8 +344,9 @@ export const hasHost = createSelector(
|
||||
// Get the parameter values, that the user filled out
|
||||
export function parameterValues(state, pathMethod, isXml) {
|
||||
pathMethod = pathMethod || []
|
||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
return params.reduce( (hash, p) => {
|
||||
// let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let paramValues = operationWithMeta(state, ...pathMethod).get("parameters", List())
|
||||
return paramValues.reduce( (hash, p) => {
|
||||
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
|
||||
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
|
||||
}, fromJS({}))
|
||||
@@ -305,7 +369,7 @@ export function parametersIncludeType(parameters, typeValue="") {
|
||||
// Get the consumes/produces value that the user selected
|
||||
export function contentTypeValues(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
|
||||
let op = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod], fromJS({}))
|
||||
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
|
||||
let producesValue = currentProducesFor(state, pathMethod)
|
||||
|
||||
@@ -327,14 +391,14 @@ export function contentTypeValues(state, pathMethod) {
|
||||
// Get the consumes/produces by path
|
||||
export function operationConsumes(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
||||
return specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
||||
}
|
||||
|
||||
// Get the currently selected produces value for an operation
|
||||
export function currentProducesFor(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
|
||||
const operation = spec(state).getIn(["paths", ...pathMethod], null)
|
||||
const operation = specJsonWithResolvedSubtrees(state).getIn([ "paths", ...pathMethod], null)
|
||||
|
||||
if(operation === null) {
|
||||
// return nothing if the operation does not exist
|
||||
@@ -362,10 +426,10 @@ export const canExecuteScheme = ( state, path, method ) => {
|
||||
|
||||
export const validateBeforeExecute = ( state, pathMethod ) => {
|
||||
pathMethod = pathMethod || []
|
||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let isValid = true
|
||||
|
||||
params.forEach( (p) => {
|
||||
paramValues.forEach( (p) => {
|
||||
let errors = p.get("errors")
|
||||
if ( errors && errors.count() ) {
|
||||
isValid = false
|
||||
|
||||
@@ -5,7 +5,7 @@ export const updateSpec = (ori, {specActions}) => (...args) => {
|
||||
|
||||
export const updateJsonSpec = (ori, {specActions}) => (...args) => {
|
||||
ori(...args)
|
||||
specActions.resolveSpec(...args)
|
||||
specActions.invalidateResolvedSubtreeCache()
|
||||
}
|
||||
|
||||
// Log the request ( just for debugging, shouldn't affect prod )
|
||||
@@ -16,4 +16,4 @@ export const executeRequest = (ori, { specActions }) => (req) => {
|
||||
|
||||
export const validateParams = (ori, { specSelectors }) => (req) => {
|
||||
return ori(req, specSelectors.isOAS3())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = function({ configs }) {
|
||||
buildRequest: Swagger.buildRequest,
|
||||
execute: Swagger.execute,
|
||||
resolve: Swagger.resolve,
|
||||
resolveSubtree: Swagger.resolveSubtree,
|
||||
serializeRes: Swagger.serializeRes,
|
||||
opId: Swagger.helpers.opId
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
||||
import downloadUrlPlugin from "core/plugins/download-url"
|
||||
import configsPlugin from "core/plugins/configs"
|
||||
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||
import filter from "core/plugins/filter"
|
||||
import onComplete from "core/plugins/on-complete"
|
||||
|
||||
import OperationContainer from "core/containers/OperationContainer"
|
||||
|
||||
@@ -44,7 +46,10 @@ import Headers from "core/components/headers"
|
||||
import Errors from "core/components/errors"
|
||||
import ContentType from "core/components/content-type"
|
||||
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 ParamBody from "core/components/param-body"
|
||||
import Curl from "core/components/curl"
|
||||
@@ -123,7 +128,9 @@ export default function() {
|
||||
OperationExtRow,
|
||||
ParameterExt,
|
||||
OperationContainer,
|
||||
DeepLink
|
||||
DeepLink,
|
||||
InfoUrl,
|
||||
InfoBasePath
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +159,8 @@ export default function() {
|
||||
ast,
|
||||
SplitPaneModePlugin,
|
||||
downloadUrlPlugin,
|
||||
deepLinkingPlugin
|
||||
deepLinkingPlugin,
|
||||
filter,
|
||||
onComplete
|
||||
]
|
||||
}
|
||||
|
||||
+10
-4
@@ -350,7 +350,7 @@ export function extractFileNameFromContentDispositionHeader(value){
|
||||
if (responseFilename !== null && responseFilename.length > 1) {
|
||||
return responseFilename[1]
|
||||
}
|
||||
return null
|
||||
return null
|
||||
}
|
||||
|
||||
// PascalCase, aka UpperCamelCase
|
||||
@@ -445,7 +445,7 @@ export const validateDateTime = (val) => {
|
||||
|
||||
export const validateGuid = (val) => {
|
||||
val = val.toString().toLowerCase()
|
||||
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
|
||||
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(val)) {
|
||||
return "Value must be a Guid"
|
||||
}
|
||||
}
|
||||
@@ -559,7 +559,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
|
||||
} else if ( type === "array" ) {
|
||||
let itemType
|
||||
|
||||
if ( !value.count() ) { return errors }
|
||||
if ( !listCheck || !value.count() ) { return errors }
|
||||
|
||||
itemType = paramDetails.getIn(["items", "type"])
|
||||
|
||||
@@ -623,13 +623,19 @@ export const parseSearch = () => {
|
||||
continue
|
||||
}
|
||||
i = params[i].split("=")
|
||||
map[decodeURIComponent(i[0])] = decodeURIComponent(i[1])
|
||||
map[decodeURIComponent(i[0])] = (i[1] && decodeURIComponent(i[1])) || ""
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
export const serializeSearch = (searchMap) => {
|
||||
return Object.keys(searchMap).map(k => {
|
||||
return encodeURIComponent(k) + "=" + encodeURIComponent(searchMap[k])
|
||||
}).join("&")
|
||||
}
|
||||
|
||||
export const btoa = (str) => {
|
||||
let buffer
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-rolling" style="background-image: none; background-position: initial initial; background-repeat: initial initial;"><circle cx="50" cy="50" fill="none" ng-attr-stroke="{{config.color}}" ng-attr-stroke-width="{{config.width}}" ng-attr-r="{{config.radius}}" ng-attr-stroke-dasharray="{{config.dasharray}}" stroke="#555555" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></circle></svg>
|
||||
|
After Width: | Height: | Size: 734 B |
@@ -6,7 +6,7 @@ Plugins allow you to add
|
||||
- `statePlugins`
|
||||
- `selectors` - query the state
|
||||
- `reducers` - modify the state
|
||||
- `actions` - fire and forget, that will eventually be handled by a reducer. You *can* rely on the result of async actions. But in general its not reccomended
|
||||
- `actions` - fire and forget, that will eventually be handled by a reducer. You *can* rely on the result of async actions. But in general its not recommended
|
||||
- `wrapActions` - replace an action with a wrapped action (useful for hooking into existing `actions`)
|
||||
- `components` - React components
|
||||
- `fn` - commons functions
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from "prop-types"
|
||||
|
||||
//import "./topbar.less"
|
||||
import Logo from "./logo_small.png"
|
||||
import {parseSearch, serializeSearch} from "../../core/utils"
|
||||
|
||||
export default class Topbar extends React.Component {
|
||||
|
||||
@@ -41,6 +42,15 @@ export default class Topbar extends React.Component {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
setSearch = (spec) => {
|
||||
let search = parseSearch()
|
||||
search["urls.primaryName"] = spec.name
|
||||
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) => {
|
||||
const configs = this.props.getConfigs()
|
||||
const urls = configs.urls || []
|
||||
@@ -52,6 +62,7 @@ export default class Topbar extends React.Component {
|
||||
if(spec.url === selectedUrl)
|
||||
{
|
||||
this.setState({selectedIndex: i})
|
||||
this.setSearch(spec)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export default class StandaloneLayout extends React.Component {
|
||||
let Container = getComponent("Container")
|
||||
let Row = getComponent("Row")
|
||||
let Col = getComponent("Col")
|
||||
let Errors = getComponent("errors", true)
|
||||
|
||||
const Topbar = getComponent("Topbar", true)
|
||||
const BaseLayout = getComponent("BaseLayout", true)
|
||||
@@ -45,8 +46,8 @@ export default class StandaloneLayout extends React.Component {
|
||||
<div className="info">
|
||||
<div className="loading-container">
|
||||
<h4 className="title">Failed to load API definition.</h4>
|
||||
<p>{lastErrMsg}</p>
|
||||
</div>
|
||||
<Errors/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{ loadingStatus === "failedConfig" &&
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
.authorize
|
||||
{
|
||||
padding-right: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-24
@@ -64,35 +64,12 @@
|
||||
|
||||
&.execute
|
||||
{
|
||||
animation: swagger-ui-pulse 2s infinite;
|
||||
will-change: transform;
|
||||
background-color: $btn-execute-background-color;
|
||||
background-color: $btn-execute-background-color-alt;
|
||||
color: $btn-execute-font-color;
|
||||
border-color: $btn-execute-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes swagger-ui-pulse
|
||||
{
|
||||
0%
|
||||
{
|
||||
color: $btn-execute-font-color;
|
||||
background: $btn-execute-background-color-alt;
|
||||
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, .8);
|
||||
}
|
||||
70%
|
||||
{
|
||||
box-shadow: 0 0 0 5px rgba($btn-execute-background-color-alt, 0);
|
||||
}
|
||||
100%
|
||||
{
|
||||
color: $btn-execute-font-color;
|
||||
background: $btn-execute-background-color-alt;
|
||||
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group
|
||||
{
|
||||
display: flex;
|
||||
|
||||
@@ -586,6 +586,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-body
|
||||
{
|
||||
.opblock-loading-animation
|
||||
{
|
||||
display: block;
|
||||
margin: 3em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-body pre
|
||||
{
|
||||
|
||||
@@ -198,6 +198,7 @@ section.models
|
||||
.model-box
|
||||
{
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
|
||||
border-radius: 4px;
|
||||
background: rgba($section-models-model-box-background-color,.1);
|
||||
|
||||
@@ -188,7 +188,7 @@ $section-models-model-title-font-color: $gray-700 !default;
|
||||
|
||||
$prop-type-font-color: #55a !default;
|
||||
|
||||
$prop-format-font-color: $gray-300 !default;
|
||||
$prop-format-font-color: $gray-600 !default;
|
||||
|
||||
// Tables
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.swagger-ui
|
||||
{
|
||||
@import '~tachyons-sass/tachyons.scss';
|
||||
@import 'mixins';
|
||||
@import 'variables';
|
||||
@import 'colors';
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("<LiveResponse/>", function(){
|
||||
accept: "application/xml",
|
||||
mutated: "header"
|
||||
},
|
||||
url: "http://petstore.swagger.io/v2/pet/1"
|
||||
url: "http://mutated.petstore.swagger.io/v2/pet/1"
|
||||
})
|
||||
|
||||
let requests = {
|
||||
@@ -80,6 +80,9 @@ describe("<LiveResponse/>", function(){
|
||||
expect(curl.length).toEqual(1)
|
||||
expect(curl.props().request).toBe(requests[test.expected.request])
|
||||
|
||||
const expectedUrl = requests[test.expected.request].get("url")
|
||||
expect(wrapper.find("div.request-url pre").text()).toEqual(expectedUrl)
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,11 +7,35 @@ import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markd
|
||||
|
||||
describe("Markdown component", function() {
|
||||
describe("Swagger 2.0", function() {
|
||||
it("allows span elements with class attrib", function() {
|
||||
const str = `<span class="method">ONE</span>`
|
||||
const el = render(<Markdown source={str} />)
|
||||
expect(el.html()).toEqual(`<div class="markdown"><p><span class="method">ONE</span></p>\n</div>`)
|
||||
})
|
||||
|
||||
it("allows td elements with colspan attrib", function() {
|
||||
const str = `<table><tr><td>ABC</td></tr></table>`
|
||||
const el = render(<Markdown source={str} />)
|
||||
expect(el.html()).toEqual(`<div class="markdown"><table><tr><td>ABC</td></tr></table></div>`)
|
||||
})
|
||||
|
||||
it("allows image elements", function() {
|
||||
const 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>`)
|
||||
})
|
||||
|
||||
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() {
|
||||
const str = `
|
||||
@@ -39,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>`)
|
||||
})
|
||||
|
||||
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() {
|
||||
const str = `
|
||||
# h1
|
||||
|
||||
@@ -24,7 +24,8 @@ describe("<Models/>", function(){
|
||||
def1: {},
|
||||
def2: {}
|
||||
})
|
||||
}
|
||||
},
|
||||
specResolvedSubtree: () => {}
|
||||
},
|
||||
layoutSelectors: {
|
||||
isShown: createSpy()
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Map } from "immutable"
|
||||
import opsFilter from "corePlugins/filter/opsFilter"
|
||||
import expect from "expect"
|
||||
|
||||
describe("opsFilter", function() {
|
||||
const taggedOps = Map([["pet"], ["store"], ["user"]])
|
||||
|
||||
it("should filter taggedOps by tag name", function () {
|
||||
const filtered = opsFilter(taggedOps, "sto")
|
||||
|
||||
expect(filtered.size).toEqual(1)
|
||||
})
|
||||
|
||||
it("should return all taggedOps when search phrase is empty", function () {
|
||||
const filtered = opsFilter(taggedOps, "")
|
||||
|
||||
expect(filtered.size).toEqual(taggedOps.size)
|
||||
})
|
||||
|
||||
it("should return empty result when there is no match", function () {
|
||||
const filtered = opsFilter(taggedOps, "NoMatch")
|
||||
|
||||
expect(filtered.size).toEqual(0)
|
||||
})
|
||||
})
|
||||
@@ -99,6 +99,173 @@ describe("sampleFromSchema", function() {
|
||||
|
||||
expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(expected)
|
||||
})
|
||||
|
||||
describe("for array type", function() {
|
||||
it("returns array with sample of array type", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "integer"
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ 0 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of examples for array that has example", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
},
|
||||
example: "dog"
|
||||
}
|
||||
|
||||
var expected = [ "dog" ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of examples for array that has examples", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
example: [ "dog", "cat" ]
|
||||
}
|
||||
|
||||
var expected = [ "dog", "cat" ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for oneOf type", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
oneOf: [
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ 0 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for oneOf types", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
oneOf: [
|
||||
{
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ "string", 0 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for oneOf examples", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
oneOf: [
|
||||
{
|
||||
type: "string",
|
||||
example: "dog"
|
||||
},
|
||||
{
|
||||
type: "integer",
|
||||
example: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ "dog", 1 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for anyOf type", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
anyOf: [
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ 0 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for anyOf types", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
anyOf: [
|
||||
{
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ "string", 0 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
|
||||
it("returns array of samples for anyOf examples", function() {
|
||||
var definition = {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
example: "dog"
|
||||
},
|
||||
{
|
||||
type: "integer",
|
||||
example: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var expected = [ "dog", 1 ]
|
||||
|
||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("createXMLExample", function () {
|
||||
|
||||
@@ -177,6 +177,10 @@ describe("spec plugin - actions", function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe("requestResolvedSubtree", () => {
|
||||
it("should return a promise ")
|
||||
})
|
||||
|
||||
it.skip("should call errActions.newErr, if the fn.execute rejects", function(){
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import { fromJS } from "immutable"
|
||||
import { parameterValues, contentTypeValues, operationScheme } from "corePlugins/spec/selectors"
|
||||
import {
|
||||
parameterValues,
|
||||
contentTypeValues,
|
||||
operationScheme,
|
||||
specJsonWithResolvedSubtrees,
|
||||
operationConsumes
|
||||
} from "corePlugins/spec/selectors"
|
||||
|
||||
describe("spec plugin - selectors", function(){
|
||||
|
||||
@@ -24,7 +30,7 @@ describe("spec plugin - selectors", function(){
|
||||
|
||||
// Given
|
||||
const spec = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -55,7 +61,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should return { requestContentType, responseContentType } from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {}
|
||||
@@ -86,7 +92,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should default to the first `produces` array value if current is not set", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -121,7 +127,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should default to `application/json` if a default produces value is not available", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {}
|
||||
@@ -151,7 +157,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should prioritize consumes value first from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -182,7 +188,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should fallback to multipart/form-data if there is no consumes value but there is a file parameter", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -204,7 +210,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should fallback to application/x-www-form-urlencoded if there is no consumes value, no file parameter, but there is a formData parameter", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -238,13 +244,41 @@ describe("spec plugin - selectors", function(){
|
||||
|
||||
})
|
||||
|
||||
describe("operationConsumes", function(){
|
||||
it("should return the operationConsumes for an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
consumes: [
|
||||
"application/xml",
|
||||
"application/something-else"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = operationConsumes(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS()).toEqual([
|
||||
"application/xml",
|
||||
"application/something-else"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("operationScheme", function(){
|
||||
|
||||
it("should return the correct scheme for a remote spec that doesn't specify a scheme", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
url: "https://generator.swagger.io/api/swagger.json",
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -277,4 +311,54 @@ describe("spec plugin - selectors", function(){
|
||||
|
||||
})
|
||||
|
||||
|
||||
describe("specJsonWithResolvedSubtrees", function(){
|
||||
|
||||
it("should return a correctly merged tree", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
json: {
|
||||
definitions: {
|
||||
Asdf: {
|
||||
$ref: "#/some/path",
|
||||
randomKey: "this should be removed b/c siblings of $refs must be removed, per the specification",
|
||||
description: "same for this key"
|
||||
},
|
||||
Fgsfds: {
|
||||
$ref: "#/another/path"
|
||||
},
|
||||
OtherDef: {
|
||||
description: "has no refs"
|
||||
}
|
||||
}
|
||||
},
|
||||
resolvedSubtrees: {
|
||||
definitions: {
|
||||
Asdf: {
|
||||
type: "object",
|
||||
$$ref: "#/some/path"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let result = specJsonWithResolvedSubtrees(state)
|
||||
// Then
|
||||
expect(result.toJS()).toEqual({
|
||||
definitions: {
|
||||
Asdf: {
|
||||
type: "object",
|
||||
$$ref: "#/some/path"
|
||||
},
|
||||
Fgsfds: {
|
||||
$ref: "#/another/path"
|
||||
},
|
||||
OtherDef: {
|
||||
description: "has no refs"
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import System from "core/system"
|
||||
import { fromJS } from "immutable"
|
||||
import { render } from "enzyme"
|
||||
import ViewPlugin from "core/plugins/view/index.js"
|
||||
import filterPlugin from "core/plugins/filter/index.js"
|
||||
import { connect, Provider } from "react-redux"
|
||||
|
||||
describe("bound system", function(){
|
||||
@@ -264,6 +265,22 @@ describe("bound system", function(){
|
||||
|
||||
})
|
||||
|
||||
describe("fn", function() {
|
||||
|
||||
it("should return helper functions", function () {
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: [
|
||||
filterPlugin
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
const fn = system.getSystem().fn.opsFilter
|
||||
expect(typeof fn).toEqual("function")
|
||||
})
|
||||
})
|
||||
|
||||
describe("selectors", function(){
|
||||
|
||||
it("should have the first arg be the nested state, and all other args to follow", function(){
|
||||
@@ -684,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() {
|
||||
it("should encapsulate thrown errors in an afterLoad method", function() {
|
||||
// Given
|
||||
|
||||
@@ -3,6 +3,8 @@ import expect from "expect"
|
||||
import { fromJS, OrderedMap } from "immutable"
|
||||
import {
|
||||
mapToList,
|
||||
parseSearch,
|
||||
serializeSearch,
|
||||
validatePattern,
|
||||
validateMinLength,
|
||||
validateMaxLength,
|
||||
@@ -260,6 +262,9 @@ describe("utils", function() {
|
||||
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
|
||||
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
|
||||
expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
|
||||
expect(validateGuid("6ffefd8e-a018-e811-bbf9-60f67727d806")).toBeFalsy()
|
||||
expect(validateGuid("6FFEFD8E-A018-E811-BBF9-60F67727D806")).toBeFalsy()
|
||||
expect(validateGuid("00000000-0000-0000-0000-000000000000")).toBeFalsy()
|
||||
})
|
||||
|
||||
it("returns a message for invalid input'", function() {
|
||||
@@ -937,6 +942,48 @@ describe("utils", function() {
|
||||
})
|
||||
})
|
||||
|
||||
describe("parse and serialize search", function() {
|
||||
afterEach(function() {
|
||||
win.location.search = ""
|
||||
})
|
||||
|
||||
describe("parsing", function() {
|
||||
it("works with empty search", function() {
|
||||
win.location.search = ""
|
||||
expect(parseSearch()).toEqual({})
|
||||
})
|
||||
|
||||
it("works with only one key", function() {
|
||||
win.location.search = "?foo"
|
||||
expect(parseSearch()).toEqual({foo: ""})
|
||||
})
|
||||
|
||||
it("works with keys and values", function() {
|
||||
win.location.search = "?foo=fooval&bar&baz=bazval"
|
||||
expect(parseSearch()).toEqual({foo: "fooval", bar: "", baz: "bazval"})
|
||||
})
|
||||
|
||||
it("decode url encoded components", function() {
|
||||
win.location.search = "?foo=foo%20bar"
|
||||
expect(parseSearch()).toEqual({foo: "foo bar"})
|
||||
})
|
||||
})
|
||||
|
||||
describe("serializing", function() {
|
||||
it("works with empty map", function() {
|
||||
expect(serializeSearch({})).toEqual("")
|
||||
})
|
||||
|
||||
it("works with multiple keys with and without values", function() {
|
||||
expect(serializeSearch({foo: "", bar: "barval"})).toEqual("foo=&bar=barval")
|
||||
})
|
||||
|
||||
it("encode url components", function() {
|
||||
expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("sanitizeUrl", function() {
|
||||
it("should sanitize a `javascript:` url", function() {
|
||||
const res = sanitizeUrl("javascript:alert('bam!')")
|
||||
|
||||
@@ -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,41 @@
|
||||
describe("render pet api container", function () {
|
||||
let mainPage
|
||||
let apiWrapper
|
||||
beforeEach(function (client, done) {
|
||||
mainPage = client
|
||||
.url("localhost:3200")
|
||||
.page.main()
|
||||
|
||||
client.waitForElementVisible(".download-url-input", 5000)
|
||||
.pause(5000)
|
||||
.clearValue(".download-url-input")
|
||||
.setValue(".download-url-input", "http://localhost:3200/test-specs/callbacks.openapi.yaml")
|
||||
.click("button.download-url-button")
|
||||
.pause(1000)
|
||||
|
||||
apiWrapper = mainPage.section.apiWrapper
|
||||
|
||||
done()
|
||||
})
|
||||
afterEach(function (client, done) {
|
||||
done()
|
||||
})
|
||||
describe("POST /pet", () => {
|
||||
it("should render a callback correctly", function (client) {
|
||||
apiWrapper.waitForElementVisible("#operations-pet-addPet", 10000)
|
||||
// Expand the operation
|
||||
.click("#operations-pet-addPet")
|
||||
.waitForElementVisible("#operations-pet-addPet > div:nth-child(2) > div", 5000)
|
||||
// Switch to Callbacks tab
|
||||
.click("#operations-pet-addPet div.tab-header > div.tab-item.false > h4 > span")
|
||||
.waitForElementVisible("#operations-pet-addPet div.callbacks-container", 5000)
|
||||
.assert.containsText("#operations--post_request_body__url > div > span.opblock-summary-path", "$request.body#/url")
|
||||
.click("#operations--post_request_body__url")
|
||||
.waitForElementVisible("#operations--post_request_body__url div.response-col_description__inner > div > div > p", 5000)
|
||||
.assert.containsText("#operations--post_request_body__url div.response-col_description__inner > div > div > p", "webhook successfully processed and no retries will be performed")
|
||||
|
||||
|
||||
client.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,53 @@
|
||||
describe("render pet api container", function () {
|
||||
let mainPage
|
||||
let apiWrapper
|
||||
beforeEach(function (client, done) {
|
||||
mainPage = client
|
||||
.url("localhost:3200")
|
||||
.page.main()
|
||||
|
||||
client.waitForElementVisible(".download-url-input", 5000)
|
||||
.pause(5000)
|
||||
.clearValue(".download-url-input")
|
||||
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.openapi.yaml")
|
||||
.click("button.download-url-button")
|
||||
.pause(1000)
|
||||
|
||||
apiWrapper = mainPage.section.apiWrapper
|
||||
|
||||
done()
|
||||
})
|
||||
afterEach(function (client, done) {
|
||||
done()
|
||||
})
|
||||
describe("GET /pet/{petId}", () => {
|
||||
it("should render Try-It-Out flow correctly", function (client) {
|
||||
apiWrapper.waitForElementVisible("#operations-pet-updatePetWithForm", 10000)
|
||||
.assert.containsText("#operations-pet-updatePetWithForm > div > span.opblock-summary-path > a > span", "/pet")
|
||||
.click("#operations-pet-updatePetWithForm")
|
||||
.waitForElementVisible("#operations-pet-updatePetWithForm > div:nth-child(2) > div", 5000)
|
||||
.click("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.opblock-section > div.opblock-section-header > div.try-out > button")
|
||||
.waitForElementVisible("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.execute-wrapper > button", 1000)
|
||||
.click("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.execute-wrapper > button")
|
||||
.assert.cssClassNotPresent("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.execute-wrapper > button", "cancel")
|
||||
|
||||
client.end()
|
||||
})
|
||||
|
||||
it("should have stable input values", function (client) {
|
||||
client.waitForElementVisible("#operations-pet-updatePetWithForm", 10000)
|
||||
.assert.containsText("#operations-pet-updatePetWithForm > div > span.opblock-summary-path > a > span", "/pet")
|
||||
.click("#operations-pet-updatePetWithForm")
|
||||
.waitForElementVisible("#operations-pet-updatePetWithForm > div:nth-child(2) > div", 5000)
|
||||
.click("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.opblock-section > div.opblock-section-header > div.try-out > button")
|
||||
.waitForElementVisible("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.execute-wrapper > button", 1000)
|
||||
.setValue("#operations-pet-updatePetWithForm td.parameters-col_description > input", "12345")
|
||||
.click("#operations-pet-updatePetWithForm > div:nth-child(2) > div > div.execute-wrapper > button")
|
||||
.pause(800) // for swagger-api/swagger-ui#4269, which happens above 350ms
|
||||
.assert.containsText("#operations-pet-updatePetWithForm div.responses-inner > div > div > div:nth-child(2) > div > pre", "http://localhost:3204/pet/12345")
|
||||
.assert.value("#operations-pet-updatePetWithForm td.parameters-col_description > input", "12345")
|
||||
|
||||
client.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
describe("Remote $ref rendering", function () {
|
||||
let mainPage
|
||||
beforeEach(function (client, done) {
|
||||
|
||||
mainPage = client
|
||||
// expand the models so we don't have to manually do it
|
||||
.url("localhost:3200?defaultModelsExpandDepth=5")
|
||||
.page.main()
|
||||
|
||||
client.waitForElementVisible(".download-url-input", 5000)
|
||||
.pause(2000)
|
||||
.clearValue(".download-url-input")
|
||||
.setValue(".download-url-input", "http://localhost:3200/test-specs/refs/api1.yaml")
|
||||
.click("button.download-url-button")
|
||||
.pause(1000)
|
||||
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
it("renders a remote $ref correctly", function (client) {
|
||||
mainPage.expect.element("#model-TestResponse > span > div > span > span > span.inner-object > table > tbody > tr:nth-child(2) > td:nth-child(2) > span > span > div > div > p").text.to.equal("this is a api2prop")
|
||||
|
||||
client.end()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
openapi: 3.0.0
|
||||
servers:
|
||||
- url: http://petstore.swagger.io/v2
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
termsOfService: http://swagger.io/terms/
|
||||
contact:
|
||||
email: apiteam@swagger.io
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
paths:
|
||||
"/pet":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Add a new pet to the store
|
||||
description: ''
|
||||
operationId: addPet
|
||||
parameters: []
|
||||
responses:
|
||||
'405':
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
callbacks:
|
||||
myWebhook:
|
||||
'$request.body#/url':
|
||||
post:
|
||||
requestBody:
|
||||
description: Callback payload
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: webhook successfully processed and no retries will be performed
|
||||
@@ -0,0 +1,689 @@
|
||||
---
|
||||
openapi: 3.0.0
|
||||
servers:
|
||||
- url: http://localhost:3204/
|
||||
info:
|
||||
description: 'This is a sample server Petstore server. You can find out more about
|
||||
Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For
|
||||
this sample, you can use the api key `special-key` to test the authorization filters.'
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
termsOfService: http://swagger.io/terms/
|
||||
contact:
|
||||
email: apiteam@swagger.io
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
tags:
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
externalDocs:
|
||||
description: Find out more
|
||||
url: http://swagger.io
|
||||
- name: store
|
||||
description: Access to Petstore orders
|
||||
- name: user
|
||||
description: Operations about user
|
||||
externalDocs:
|
||||
description: Find out more about our store
|
||||
url: http://swagger.io
|
||||
paths:
|
||||
"/pet":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Add a new pet to the store
|
||||
description: ''
|
||||
operationId: addPet
|
||||
responses:
|
||||
'405':
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
requestBody:
|
||||
"$ref": "#/components/requestBodies/Pet"
|
||||
put:
|
||||
tags:
|
||||
- pet
|
||||
summary: Update an existing pet
|
||||
description: ''
|
||||
operationId: updatePet
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
'405':
|
||||
description: Validation exception
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
requestBody:
|
||||
"$ref": "#/components/requestBodies/Pet"
|
||||
"/pet/findByStatus":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by status
|
||||
description: Multiple status values can be provided with comma separated strings
|
||||
operationId: findPetsByStatus
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
description: Status values that need to be considered for filter
|
||||
required: true
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
default: available
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid status value
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/findByTags":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by tags
|
||||
description: Muliple tags can be provided with comma separated strings. Use
|
||||
tag1, tag2, tag3 for testing.
|
||||
operationId: findPetsByTags
|
||||
parameters:
|
||||
- name: tags
|
||||
in: query
|
||||
description: Tags to filter by
|
||||
required: true
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid tag value
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
deprecated: true
|
||||
"/pet/{petId}":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Find pet by ID
|
||||
description: Returns a single pet
|
||||
operationId: getPetById
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to return
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
security:
|
||||
- api_key: []
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Updates a pet in the store with form data
|
||||
description: ''
|
||||
operationId: updatePetWithForm
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet that needs to be updated
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'405':
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
requestBody:
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
description: Updated name of the pet
|
||||
type: string
|
||||
status:
|
||||
description: Updated status of the pet
|
||||
type: string
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: Deletes a pet
|
||||
description: ''
|
||||
operationId: deletePet
|
||||
parameters:
|
||||
- name: api_key
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/{petId}/uploadImage":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: uploads an image
|
||||
description: ''
|
||||
operationId: uploadFile
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to update
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ApiResponse"
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
requestBody:
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
"/store/inventory":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Returns pet inventories by status
|
||||
description: Returns a map of status codes to quantities
|
||||
operationId: getInventory
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
security:
|
||||
- api_key: []
|
||||
"/store/order":
|
||||
post:
|
||||
tags:
|
||||
- store
|
||||
summary: Place an order for a pet
|
||||
description: ''
|
||||
operationId: placeOrder
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
'400':
|
||||
description: Invalid Order
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
description: order placed for purchasing the pet
|
||||
required: true
|
||||
"/store/order/{orderId}":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Find purchase order by ID
|
||||
description: For valid response try integer IDs with value >= 1 and <= 10. Other
|
||||
values will generated exceptions
|
||||
operationId: getOrderById
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of pet that needs to be fetched
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
maximum: 10
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
delete:
|
||||
tags:
|
||||
- store
|
||||
summary: Delete purchase order by ID
|
||||
description: For valid response try integer IDs with positive integer value.
|
||||
Negative or non-integer values will generate API errors
|
||||
operationId: deleteOrder
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of the order that needs to be deleted
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
"/user":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Create user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: createUser
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
description: Created user object
|
||||
required: true
|
||||
"/user/createWithArray":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array
|
||||
description: ''
|
||||
operationId: createUsersWithArrayInput
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
requestBody:
|
||||
"$ref": "#/components/requestBodies/UserArray"
|
||||
"/user/createWithList":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array
|
||||
description: ''
|
||||
operationId: createUsersWithListInput
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
requestBody:
|
||||
"$ref": "#/components/requestBodies/UserArray"
|
||||
"/user/login":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs user into the system
|
||||
description: ''
|
||||
operationId: loginUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: query
|
||||
description: The user name for login
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: password
|
||||
in: query
|
||||
description: The password for login in clear text
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
headers:
|
||||
X-Rate-Limit:
|
||||
description: calls per hour allowed by the user
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
X-Expires-After:
|
||||
description: date in UTC when token expires
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
type: string
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
'400':
|
||||
description: Invalid username/password supplied
|
||||
"/user/logout":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs out current logged in user session
|
||||
description: ''
|
||||
operationId: logoutUser
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/{username}":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get user by user name
|
||||
description: ''
|
||||
operationId: getUserByName
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: 'The name that needs to be fetched. Use user1 for testing. '
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
put:
|
||||
tags:
|
||||
- user
|
||||
summary: Updated user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: updateUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: name that need to be updated
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid user supplied
|
||||
'404':
|
||||
description: User not found
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
description: Updated user object
|
||||
required: true
|
||||
delete:
|
||||
tags:
|
||||
- user
|
||||
summary: Delete user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: deleteUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: The name that needs to be deleted
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: http://swagger.io
|
||||
components:
|
||||
schemas:
|
||||
Order:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
petId:
|
||||
type: integer
|
||||
format: int64
|
||||
quantity:
|
||||
type: integer
|
||||
format: int32
|
||||
shipDate:
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: string
|
||||
description: Order Status
|
||||
enum:
|
||||
- placed
|
||||
- approved
|
||||
- delivered
|
||||
complete:
|
||||
type: boolean
|
||||
default: false
|
||||
xml:
|
||||
name: Order
|
||||
Category:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: Category
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
username:
|
||||
type: string
|
||||
firstName:
|
||||
type: string
|
||||
lastName:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
phone:
|
||||
type: string
|
||||
userStatus:
|
||||
type: integer
|
||||
format: int32
|
||||
description: User Status
|
||||
xml:
|
||||
name: User
|
||||
Tag:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: Tag
|
||||
Pet:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- photoUrls
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
category:
|
||||
"$ref": "#/components/schemas/Category"
|
||||
name:
|
||||
type: string
|
||||
example: doggie
|
||||
photoUrls:
|
||||
type: array
|
||||
xml:
|
||||
name: photoUrl
|
||||
wrapped: true
|
||||
items:
|
||||
type: string
|
||||
tags:
|
||||
type: array
|
||||
xml:
|
||||
name: tag
|
||||
wrapped: true
|
||||
items:
|
||||
"$ref": "#/components/schemas/Tag"
|
||||
status:
|
||||
type: string
|
||||
description: pet status in the store
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
xml:
|
||||
name: Pet
|
||||
ApiResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
type:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
requestBodies:
|
||||
Pet:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
description: Pet object that needs to be added to the store
|
||||
required: true
|
||||
UserArray:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/User"
|
||||
description: List of user object
|
||||
required: true
|
||||
securitySchemes:
|
||||
petstore_auth:
|
||||
type: oauth2
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: http://petstore.swagger.io/oauth/dialog
|
||||
scopes:
|
||||
write:pets: modify pets in your account
|
||||
read:pets: read your pets
|
||||
api_key:
|
||||
type: apiKey
|
||||
name: api_key
|
||||
in: header
|
||||
@@ -0,0 +1,32 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
description: |
|
||||
This is a test to show how model refererence from another file are failing.
|
||||
This file references api2.yaml. If you load this file first in the browser it fails.
|
||||
However, if you load api2.yaml first, then load this one it will work.
|
||||
version: 1.0.0
|
||||
title: API1 Test
|
||||
paths:
|
||||
'/test-api-1':
|
||||
get:
|
||||
summary: Api 1
|
||||
responses:
|
||||
'200':
|
||||
description: 'api 2 response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestResponse'
|
||||
components:
|
||||
schemas:
|
||||
Api1Prop:
|
||||
type: string
|
||||
example: 'api1prop-value'
|
||||
|
||||
TestResponse:
|
||||
type: object
|
||||
properties:
|
||||
api1prop:
|
||||
$ref: '#/components/schemas/Api1Prop'
|
||||
api2prop:
|
||||
$ref: 'api2.yaml#/components/schemas/Api2Prop'
|
||||
@@ -0,0 +1,32 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
description: |
|
||||
This is a test to show how model refererence from another file are failing.
|
||||
This file is referenced api1.yaml. If you load api1.yaml first in the browser it fails.
|
||||
However, if you load this file first, then load api1.yaml it will work.
|
||||
version: 1.0.0
|
||||
title: API2 Test
|
||||
paths:
|
||||
'/test-api-2':
|
||||
get:
|
||||
summary: Api 2
|
||||
responses:
|
||||
'200':
|
||||
description: api 2 response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestResponse2'
|
||||
components:
|
||||
schemas:
|
||||
Api2Prop:
|
||||
type: string
|
||||
description: this is a api2prop
|
||||
example: 'api1prop-value'
|
||||
|
||||
TestResponse2:
|
||||
type: object
|
||||
description: This is a test prop
|
||||
properties:
|
||||
api2prop:
|
||||
$ref: '#/components/schemas/Api2Prop'
|
||||
@@ -63,7 +63,6 @@ module.exports = require("./make-webpack-config")(rules, {
|
||||
},
|
||||
devServer: {
|
||||
port: 3200,
|
||||
contentBase: path.join(__dirname, "dev-helpers"),
|
||||
publicPath: "/",
|
||||
noInfo: true,
|
||||
hot: true,
|
||||
|
||||
Reference in New Issue
Block a user