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 |
+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.11.0 | 2018-02-09 | 2.0, 3.0 | [tag v3.11.0](https://github.com/swagger-api/swagger-ui/tree/v3.11.0)
|
||||
3.13.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)
|
||||
|
||||
Vendored
+14
-20
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
||||
# Plug points
|
||||
|
||||
Swagger-UI exposes most of its internal logic through the plugin system.
|
||||
|
||||
Often, it is beneficial to override the core internals to achieve custom behavior.
|
||||
|
||||
### Note: Semantic Versioning
|
||||
|
||||
Swagger-UI's internal APIs are _not_ part of our public contract, which means that they can change without the major version changing.
|
||||
|
||||
If your custom plugins wrap, extend, override, or consume any internal core APIs, we recommend specifying a specific minor version of Swagger-UI to use in your application, because they will _not_ change between patch versions.
|
||||
|
||||
If you're installing Swagger-UI via NPM, for example, you can do this by using a tilde:
|
||||
|
||||
```js
|
||||
{
|
||||
"dependencies": {
|
||||
"swagger-ui": "~3.11.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `fn.opsFilter`
|
||||
|
||||
When using the `filter` option, tag names will be filtered by the user-provided value. If you'd like to customize this behavior, you can override the default `opsFilter` function.
|
||||
|
||||
For example, you can implement a multiple-phrase filter:
|
||||
|
||||
```js
|
||||
const MultiplePhraseFilterPlugin = function() {
|
||||
return {
|
||||
fn: {
|
||||
opsFilter: (taggedOps, phrase) => {
|
||||
const phrases = phrase.split(", ")
|
||||
|
||||
return taggedOps.filter((val, key) => {
|
||||
return phrases.some(item => key.indexOf(item) > -1)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -2,6 +2,22 @@
|
||||
|
||||
A plugin is a function that returns an object - more specifically, the object may contain functions and components that augment and modify Swagger-UI's functionality.
|
||||
|
||||
### 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 `stateKey` is a name for a piece of state:
|
||||
|
||||
@@ -59,6 +59,7 @@ Parameter Name | Description
|
||||
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
|
||||
`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
|
||||
|
||||
@@ -77,3 +78,11 @@ Parameter Name | Description
|
||||
--- | ---
|
||||
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
|
||||
`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({...})
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.11.0",
|
||||
"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",
|
||||
@@ -83,14 +84,14 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "^3.5.0",
|
||||
"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",
|
||||
@@ -144,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">
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class Models extends Component {
|
||||
|
||||
const content = <ModelWrapper name={ name }
|
||||
expandDepth={ defaultModelsExpandDepth }
|
||||
schema={ schema }
|
||||
schema={ schema || Im.Map() }
|
||||
specPath={Im.List([...specPathBase, name])}
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
|
||||
@@ -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} />
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ export default class Operation extends PureComponent {
|
||||
|
||||
<Collapse isOpened={isShown}>
|
||||
<div className="opblock-body">
|
||||
{ operation && operation.size ? null :
|
||||
{ (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>}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,18 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let { specSelectors, pathMethod, param } = props
|
||||
let defaultValue = param.get("default")
|
||||
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) {
|
||||
@@ -98,16 +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 value = param ? param.get("value") : ""
|
||||
let value = paramWithMeta ? paramWithMeta.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
|
||||
let paramItems // undefined
|
||||
let paramItemsEnum // undefined
|
||||
let isDisplayParamItemsEnum = false
|
||||
|
||||
@@ -46,6 +46,7 @@ const sanitizeOptions = {
|
||||
"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>
|
||||
}
|
||||
|
||||
@@ -81,24 +81,33 @@ export default class OperationContainer extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { isShown } = this.props
|
||||
const resolvedSubtree = this.getResolvedSubtree()
|
||||
|
||||
if(isShown && resolvedSubtree === undefined) {
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { path, method, specActions, specSelectors, response, isShown } = nextProps
|
||||
const resolvedSubtree = specSelectors.specResolvedSubtree(["paths", path, method])
|
||||
const { response, isShown } = nextProps
|
||||
const resolvedSubtree = this.getResolvedSubtree()
|
||||
|
||||
if(response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if(isShown && resolvedSubtree === undefined) {
|
||||
specActions.requestResolvedSubtree(["paths", path, method])
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, specActions, tag, operationId, path, method, isShown } = this.props
|
||||
let { layoutActions, tag, operationId, isShown } = this.props
|
||||
if(!isShown) {
|
||||
// transitioning from collapsed to expanded
|
||||
specActions.requestResolvedSubtree(["paths", path, method])
|
||||
this.requestResolvedSubtree()
|
||||
}
|
||||
layoutActions.show(["operations", tag, operationId], !isShown)
|
||||
}
|
||||
@@ -117,6 +126,37 @@ 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: unresolvedOp,
|
||||
@@ -151,10 +191,10 @@ export default class OperationContainer extends PureComponent {
|
||||
|
||||
const Operation = getComponent( "operation" )
|
||||
|
||||
const resolvedSubtree = specSelectors.specResolvedSubtree(["paths", path, method]) || Map()
|
||||
const resolvedSubtree = this.getResolvedSubtree() || Map()
|
||||
|
||||
const operationProps = fromJS({
|
||||
op: resolvedSubtree || Map(),
|
||||
op: resolvedSubtree,
|
||||
tag,
|
||||
path,
|
||||
summary: unresolvedOp.getIn(["operation", "summary"]) || "",
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,7 +28,10 @@ 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)
|
||||
@@ -35,6 +40,38 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkPossibleFailReasons() {
|
||||
try {
|
||||
let specUrl
|
||||
|
||||
if("URL" in win ) {
|
||||
specUrl = new URL(url)
|
||||
} else {
|
||||
// legacy browser, use <a href> to parse the URL
|
||||
specUrl = document.createElement("a")
|
||||
specUrl.href = url
|
||||
}
|
||||
|
||||
if(specUrl.protocol !== "https:" && win.location.protocol === "https:") {
|
||||
const error = Object.assign(
|
||||
new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${specUrl.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),
|
||||
{source: "fetch"}
|
||||
)
|
||||
errActions.newThrownErr(error)
|
||||
return
|
||||
}
|
||||
if(specUrl.origin !== win.location.origin) {
|
||||
const error = Object.assign(
|
||||
new Error(`Possible cross-origin (CORS) issue? The URL origin (${specUrl.origin}) does not match the page (${win.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),
|
||||
{source: "fetch"}
|
||||
)
|
||||
errActions.newThrownErr(error)
|
||||
}
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
updateLoadingStatus: (status) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -82,7 +82,7 @@ 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 resolveIn instead!`)
|
||||
console.warn(`specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!`)
|
||||
hasWarnedAboutResolveSpecDeprecation = true
|
||||
}
|
||||
|
||||
@@ -162,11 +162,23 @@ const debResolveSubtrees = debounce(async () => {
|
||||
|
||||
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)
|
||||
const { errors, spec } = await resolveSubtree(specWithCurrentSubtrees, path, {
|
||||
baseDoc: specSelectors.url(),
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
})
|
||||
|
||||
if(errSelectors.allErrors().size) {
|
||||
errActions.clear({
|
||||
|
||||
@@ -46,9 +46,31 @@ 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().merge(spec.get("json"), spec.get("resolvedSubtrees"))
|
||||
spec => Map().mergeWith(
|
||||
mergerFn,
|
||||
spec.get("json"),
|
||||
spec.get("resolvedSubtrees")
|
||||
)
|
||||
)
|
||||
|
||||
// Default Spec ( as an object )
|
||||
@@ -369,7 +391,7 @@ export function contentTypeValues(state, pathMethod) {
|
||||
// Get the consumes/produces by path
|
||||
export function operationConsumes(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
return state.getIn(["meta", ...pathMethod, "consumes"], fromJS({}))
|
||||
return specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
||||
}
|
||||
|
||||
// Get the currently selected produces value for an operation
|
||||
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
|
||||
+9
-3
@@ -350,7 +350,7 @@ export function extractFileNameFromContentDispositionHeader(value){
|
||||
if (responseFilename !== null && responseFilename.length > 1) {
|
||||
return responseFilename[1]
|
||||
}
|
||||
return null
|
||||
return null
|
||||
}
|
||||
|
||||
// PascalCase, aka UpperCamelCase
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
.swagger-ui
|
||||
{
|
||||
@import '~tachyons-sass/tachyons.scss';
|
||||
@import 'mixins';
|
||||
@import 'variables';
|
||||
@import 'colors';
|
||||
|
||||
@@ -24,6 +24,18 @@ describe("Markdown component", function() {
|
||||
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 = `
|
||||
@@ -51,6 +63,18 @@ describe("Markdown component", function() {
|
||||
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -238,6 +244,34 @@ 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(){
|
||||
@@ -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,
|
||||
@@ -940,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