Merge branch 'master' into issue-1334
# Conflicts: # .gitignore # make-webpack-config.js
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,4 +5,4 @@ node_modules
|
||||
npm-debug.log*
|
||||
.eslintcache
|
||||
package-lock.json
|
||||
selenium-debug.log
|
||||
*.iml
|
||||
|
||||
@@ -16,7 +16,7 @@ deploy:
|
||||
email: apiteam@swagger.io
|
||||
skip_cleanup: true
|
||||
api_key:
|
||||
secure: "IJkLaACa+rfERf1O5nwlqOyuo9sbul3FBhBt4Un9P+DvEet3AoDPV9NQVLd8SkmQYKGbGQWF4BIdjrO5nqFD6Te+JTeUX5Uo/DFS/fu9qw1xv0dQpvbJFuoYnnFlbzGTEs4CFa8lbu3ZromFHQGOQxRobjsG1Kf0dWFSSzmND3g="
|
||||
secure: "YKk5L1BL4oAixvLjWp+i85fNFXK85HKOlUt6QypkZkt23My5aywuYsv5VCLjjOtuWc72zbmOzP82DTBsuRswCRViXWCiNYhl42QTdvadHu0uIlM/FL6aNlvPpzXIws4bMvz1aYOTzFTnSnNuvCTzF1daW0+2ClOo3r0nLEdDfFg="
|
||||
on:
|
||||
tags: true
|
||||
repo: swagger-api/swagger-ui
|
||||
|
||||
56
README.md
56
README.md
@@ -18,16 +18,17 @@ This repo publishes to two different NPM packages:
|
||||
For the older version of swagger-ui, refer to the [*2.x branch*](https://github.com/swagger-api/swagger-ui/tree/2.x).
|
||||
|
||||
## Compatibility
|
||||
The OpenAPI Specification has undergone 4 revisions since initial creation in 2010. Compatibility between swagger-ui and the OpenAPI Specification is as follows:
|
||||
The OpenAPI Specification has undergone 5 revisions since initial creation in 2010. Compatibility between swagger-ui and the OpenAPI Specification is as follows:
|
||||
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes | Status
|
||||
------------------ | ------------ | -------------------------- | ----- | ------
|
||||
3.0.17 | 2017-06-23 | 2.0 | [tag v3.0.17](https://github.com/swagger-api/swagger-ui/tree/v3.0.17) |
|
||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10) |
|
||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5) |
|
||||
2.0.24 | 2014-09-12 | 1.1, 1.2 | [tag v2.0.24](https://github.com/swagger-api/swagger-ui/tree/v2.0.24) |
|
||||
1.0.13 | 2013-03-08 | 1.1, 1.2 | [tag v1.0.13](https://github.com/swagger-api/swagger-ui/tree/v1.0.13) |
|
||||
1.0.1 | 2011-10-11 | 1.0, 1.1 | [tag v1.0.1](https://github.com/swagger-api/swagger-ui/tree/v1.0.1) |
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.1.2 | 2017-07-31 | 2.0, 3.0 | [tag v3.1.2](https://github.com/swagger-api/swagger-ui/tree/v3.1.2)
|
||||
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)
|
||||
2.0.24 | 2014-09-12 | 1.1, 1.2 | [tag v2.0.24](https://github.com/swagger-api/swagger-ui/tree/v2.0.24)
|
||||
1.0.13 | 2013-03-08 | 1.1, 1.2 | [tag v1.0.13](https://github.com/swagger-api/swagger-ui/tree/v1.0.13)
|
||||
1.0.1 | 2011-10-11 | 1.0, 1.1 | [tag v1.0.1](https://github.com/swagger-api/swagger-ui/tree/v1.0.1)
|
||||
|
||||
|
||||
### How to run
|
||||
@@ -74,7 +75,6 @@ To help with the migration, here are the currently known issues with 3.X. This l
|
||||
|
||||
- Only part of the [parameters](#parameters) previously supported are available.
|
||||
- The JSON Form Editor is not implemented.
|
||||
- Shebang URL support for operations is missing.
|
||||
- Support for `collectionFormat` is partial.
|
||||
- l10n (translations) is not implemented.
|
||||
- Relative path support for external files is not implemented.
|
||||
@@ -89,22 +89,23 @@ To use swagger-ui's bundles, you should take a look at the [source of swagger-ui
|
||||
|
||||
```javascript
|
||||
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"
|
||||
})
|
||||
url: "http://petstore.swagger.io/v2/swagger.json",
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
```
|
||||
|
||||
#### 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`.
|
||||
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`,
|
||||
`useBasicAuthenticationWithAccessCodeGrant`.
|
||||
|
||||
Config Name | Description
|
||||
--- | ---
|
||||
@@ -114,6 +115,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`
|
||||
|
||||
```
|
||||
const ui = SwaggerUIBundle({...})
|
||||
@@ -144,13 +146,17 @@ spec | A JSON object describing the OpenAPI Specification. When used, the `url`
|
||||
validatorUrl | 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.
|
||||
dom_id | The id of a dom element inside which SwaggerUi will put the user interface for swagger.
|
||||
oauth2RedirectUrl | OAuth redirect URL
|
||||
tagsSorter | Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
|
||||
operationsSorter | Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
|
||||
configUrl | Configs URL
|
||||
parameterMacro | MUST be a function. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
|
||||
modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
|
||||
docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'.
|
||||
displayOperationId | Controls the display of operationId in operations list. The default is `false`.
|
||||
displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`.
|
||||
displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`.
|
||||
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
|
||||
filter | If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be true/false to enable or disable, or an explicit filter string in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
|
||||
deepLinking | If set to `true`, enables dynamic deep linking for tags and operations. [Docs](https://github.com/swagger-api/swagger-ui/blob/master/docs/deep-linking.md)
|
||||
|
||||
### Plugins
|
||||
|
||||
@@ -240,6 +246,10 @@ Access-Control-Allow-Headers: Content-Type, api_key, Authorization
|
||||
|
||||
Only headers with these names will be allowed to be sent by Swagger-UI.
|
||||
|
||||
## Security contact
|
||||
|
||||
Please disclose any security-related issues or vulnerabilities by emailing [security@swagger.io](mailto:security@swagger.io), instead of using the public issue tracker.
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2017 SmartBear Software
|
||||
|
||||
36
docs/deep-linking.md
Normal file
36
docs/deep-linking.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Deep linking
|
||||
|
||||
Swagger-UI allows you to deeply link into tags and operations within a spec. When Swagger-UI is provided a URL fragment at runtime, it will automatically expand and scroll to a specified tag or operation.
|
||||
|
||||
## Usage
|
||||
|
||||
👉🏼 Add `deepLinking: true` to your Swagger-UI configuration to enable this functionality.
|
||||
|
||||
When you expand a tag or operation, Swagger-UI will automatically update its URL fragment with a deep link to the item.
|
||||
Conversely, when you collapse a tag or operation, Swagger-UI will clear the URL fragment.
|
||||
|
||||
You can also right-click a tag name or operation path in order to copy a link to that tag or operation.
|
||||
|
||||
#### Fragment format
|
||||
|
||||
The fragment is formatted in one of two ways:
|
||||
|
||||
- `#/{tagName}`, to trigger the focus of a specific tag
|
||||
- `#/{tagName}/{operationId}`, to trigger the focus of a specific operation within a tag
|
||||
|
||||
`operationId` is the explicit operationId provided in the spec, if one exists.
|
||||
Otherwise, Swagger-UI generates an implicit operationId by combining the operation's path and method, and escaping non-alphanumeric characters.
|
||||
|
||||
## FAQ
|
||||
|
||||
> I'm using Swagger-UI in an application that needs control of the URL fragment. How do I disable deep-linking?
|
||||
|
||||
This functionality is disabled by default, but you can pass `deepLinking: false` into Swagger-UI as a configuration item to be sure.
|
||||
|
||||
> Can I link to multiple tags or operations?
|
||||
|
||||
No, this is not supported.
|
||||
|
||||
> Can I collapse everything except the operation or tag I'm linking to?
|
||||
|
||||
Sure - use `docExpansion: none` to collapse all tags and operations. Your deep link will take precedence over the setting, so only the tag or operation you've specified will be expanded.
|
||||
@@ -1,12 +1,13 @@
|
||||
var path = require('path')
|
||||
var path = require("path")
|
||||
|
||||
var webpack = require('webpack')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
var deepExtend = require('deep-extend')
|
||||
const {gitDescribeSync} = require('git-describe')
|
||||
const os = require("os")
|
||||
|
||||
var pkg = require('./package.json')
|
||||
var pkg = require("./package.json")
|
||||
|
||||
let gitInfo
|
||||
|
||||
@@ -14,7 +15,7 @@ try {
|
||||
gitInfo = gitDescribeSync(__dirname)
|
||||
} catch(e) {
|
||||
gitInfo = {
|
||||
hash: 'noGit',
|
||||
hash: "noGit",
|
||||
dirty: false
|
||||
}
|
||||
}
|
||||
@@ -22,21 +23,21 @@ try {
|
||||
var commonRules = [
|
||||
{ test: /\.(js(x)?)(\?.*)?$/,
|
||||
use: [{
|
||||
loader: 'babel-loader',
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
retainLines: true
|
||||
}
|
||||
}],
|
||||
include: [ path.join(__dirname, 'src') ]
|
||||
include: [ path.join(__dirname, "src") ]
|
||||
},
|
||||
{ test: /\.(txt|yaml)(\?.*)?$/,
|
||||
loader: 'raw-loader' },
|
||||
loader: "raw-loader" },
|
||||
{ test: /\.(png|jpg|jpeg|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader?limit=10000' },
|
||||
loader: "url-loader?limit=10000" },
|
||||
{ test: /\.(woff|woff2)(\?.*)?$/,
|
||||
loader: 'url-loader?limit=100000' },
|
||||
loader: "url-loader?limit=100000" },
|
||||
{ test: /\.(ttf|eot)(\?.*)?$/,
|
||||
loader: 'file-loader' }
|
||||
loader: "file-loader" }
|
||||
]
|
||||
|
||||
module.exports = function(rules, options) {
|
||||
@@ -55,7 +56,7 @@ module.exports = function(rules, options) {
|
||||
|
||||
if( specialOptions.separateStylesheets ) {
|
||||
plugins.push(new ExtractTextPlugin({
|
||||
filename: '[name].css' + (specialOptions.longTermCaching ? '?[contenthash]' : ''),
|
||||
filename: "[name].css" + (specialOptions.longTermCaching ? "?[contenthash]" : ""),
|
||||
allChunks: true
|
||||
}))
|
||||
}
|
||||
@@ -81,35 +82,36 @@ module.exports = function(rules, options) {
|
||||
|
||||
plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: specialOptions.minimize ? JSON.stringify('production') : null,
|
||||
WEBPACK_INLINE_STYLES: !Boolean(specialOptions.separateStylesheets)
|
||||
|
||||
"process.env": {
|
||||
NODE_ENV: specialOptions.minimize ? JSON.stringify("production") : null,
|
||||
WEBPACK_INLINE_STYLES: !specialOptions.separateStylesheets
|
||||
},
|
||||
'buildInfo': JSON.stringify({
|
||||
"buildInfo": JSON.stringify({
|
||||
PACKAGE_VERSION: (pkg.version),
|
||||
GIT_COMMIT: gitInfo.hash,
|
||||
GIT_DIRTY: gitInfo.dirty
|
||||
GIT_DIRTY: gitInfo.dirty,
|
||||
HOSTNAME: os.hostname(),
|
||||
BUILD_TIME: new Date().toUTCString()
|
||||
})
|
||||
}))
|
||||
|
||||
delete options._special
|
||||
|
||||
var completeConfig = deepExtend({
|
||||
var completeConfig = deepExtend({
|
||||
entry: {},
|
||||
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[name].js'
|
||||
path: path.join(__dirname, "dist"),
|
||||
publicPath: "/",
|
||||
filename: "[name].js",
|
||||
chunkFilename: "[name].js"
|
||||
},
|
||||
|
||||
target: 'web',
|
||||
target: "web",
|
||||
|
||||
// yaml-js has a reference to `fs`, this is a workaround
|
||||
node: {
|
||||
fs: 'empty'
|
||||
fs: "empty"
|
||||
},
|
||||
|
||||
module: {
|
||||
@@ -117,17 +119,17 @@ module.exports = function(rules, options) {
|
||||
},
|
||||
|
||||
resolveLoader: {
|
||||
modules: [path.join(__dirname, 'node_modules')],
|
||||
modules: [path.join(__dirname, "node_modules")],
|
||||
},
|
||||
|
||||
externals: {
|
||||
'buffertools': true // json-react-schema/deeper depends on buffertools, which fails.
|
||||
"buffertools": true // json-react-schema/deeper depends on buffertools, which fails.
|
||||
},
|
||||
|
||||
resolve: {
|
||||
modules: [
|
||||
path.join(__dirname, './src'),
|
||||
'node_modules'
|
||||
path.join(__dirname, "./src"),
|
||||
"node_modules"
|
||||
],
|
||||
extensions: [".web.js", ".js", ".jsx", ".json", ".less"],
|
||||
alias: {
|
||||
@@ -135,7 +137,7 @@ module.exports = function(rules, options) {
|
||||
}
|
||||
},
|
||||
|
||||
devtool: specialOptions.sourcemaps ? 'cheap-module-source-map' : null,
|
||||
devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null,
|
||||
|
||||
plugins,
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "2.0.3",
|
||||
"less": "2.7.2",
|
||||
"less-loader": "4.0.4",
|
||||
"license-checker": "^11.0.0",
|
||||
"mocha": "^3.4.2",
|
||||
"nightwatch": "^0.9.16",
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
module.exports = {};
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require("autoprefixer")
|
||||
]
|
||||
}
|
||||
47
src/core/components/array-model.jsx
Normal file
47
src/core/components/array-model.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
export default class ArrayModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
expandDepth: PropTypes.number,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent, required, schema, depth, expandDepth, name } = this.props
|
||||
let items = schema.get("items")
|
||||
let title = schema.get("title") || name
|
||||
let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 )
|
||||
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
const Model = getComponent("Model")
|
||||
|
||||
const titleEl = title &&
|
||||
<span className="model-title">
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
|
||||
return <span className="model">
|
||||
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
|
||||
[
|
||||
<span><Model { ...this.props } schema={ items } required={ false }/></span>
|
||||
]
|
||||
{
|
||||
properties.size ? <span>
|
||||
{ properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={propStyle}>
|
||||
<br />{ `${key}:`}{ String(v) }</span>)
|
||||
}<br /></span>
|
||||
: null
|
||||
}
|
||||
</ModelCollapse>
|
||||
{ required && <span style={{ color: "red" }}>*</span>}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ const noop = ()=>{}
|
||||
export default class ContentType extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
contentTypes: PropTypes.oneOfType([ImPropTypes.list, ImPropTypes.set]),
|
||||
contentTypes: PropTypes.oneOfType([ImPropTypes.list, ImPropTypes.set, ImPropTypes.seq]),
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
className: PropTypes.string
|
||||
@@ -22,7 +22,9 @@ export default class ContentType extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
// Needed to populate the form, initially
|
||||
this.props.onChange(this.props.contentTypes.first())
|
||||
if(this.props.contentTypes) {
|
||||
this.props.onChange(this.props.contentTypes.first())
|
||||
}
|
||||
}
|
||||
|
||||
onChangeWrapper = e => this.props.onChange(e.target.value)
|
||||
|
||||
19
src/core/components/enum-model.jsx
Normal file
19
src/core/components/enum-model.jsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from "react"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
const EnumModel = ({ value, getComponent }) => {
|
||||
let ModelCollapse = getComponent("ModelCollapse")
|
||||
let collapsedContent = <span>Array [ { value.count() } ]</span>
|
||||
return <span className="prop-enum">
|
||||
Enum:<br />
|
||||
<ModelCollapse collapsedContent={ collapsedContent }>
|
||||
[ { value.join(", ") } ]
|
||||
</ModelCollapse>
|
||||
</span>
|
||||
}
|
||||
EnumModel.propTypes = {
|
||||
value: ImPropTypes.iterable,
|
||||
getComponent: ImPropTypes.func
|
||||
}
|
||||
|
||||
export default EnumModel
|
||||
@@ -15,7 +15,7 @@ class Path extends React.Component {
|
||||
|
||||
return (
|
||||
<pre className="base-url">
|
||||
[ Base url: {host}{basePath}]
|
||||
[ Base URL: {host}{basePath} ]
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
@@ -88,12 +88,13 @@ export default class Info extends React.Component {
|
||||
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
|
||||
|
||||
const Markdown = getComponent("Markdown")
|
||||
const VersionStamp = getComponent("VersionStamp")
|
||||
|
||||
return (
|
||||
<div className="info">
|
||||
<hgroup className="main">
|
||||
<h2 className="title" >{ title }
|
||||
{ version && <small><pre className="version"> { version } </pre></small> }
|
||||
{ version && <VersionStamp version={version}></VersionStamp> }
|
||||
</h2>
|
||||
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
|
||||
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
|
||||
|
||||
@@ -129,7 +129,8 @@ export class Select extends React.Component {
|
||||
value: PropTypes.any,
|
||||
onChange: PropTypes.func,
|
||||
multiple: PropTypes.bool,
|
||||
allowEmptyValue: PropTypes.bool
|
||||
allowEmptyValue: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
@@ -142,7 +143,7 @@ export class Select extends React.Component {
|
||||
|
||||
let value
|
||||
|
||||
if (props.value !== undefined) {
|
||||
if (props.value) {
|
||||
value = props.value
|
||||
} else {
|
||||
value = props.multiple ? [""] : ""
|
||||
@@ -178,7 +179,7 @@ export class Select extends React.Component {
|
||||
let value = this.state.value.toJS ? this.state.value.toJS() : this.state.value
|
||||
|
||||
return (
|
||||
<select multiple={ multiple } value={ value } onChange={ this.onChange } >
|
||||
<select className={this.props.className} multiple={ multiple } value={ value } onChange={ this.onChange } >
|
||||
{ allowEmptyValue ? <option value="">--</option> : null }
|
||||
{
|
||||
allowedValues.map(function (item, key) {
|
||||
|
||||
@@ -13,8 +13,13 @@ export default class BaseLayout extends React.Component {
|
||||
getComponent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
onFilterChange =(e) => {
|
||||
let {target: {value}} = e
|
||||
this.props.layoutActions.updateFilter(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { specSelectors, specActions, getComponent } = this.props
|
||||
let { specSelectors, specActions, getComponent, layoutSelectors } = this.props
|
||||
|
||||
let info = specSelectors.info()
|
||||
let url = specSelectors.url()
|
||||
@@ -26,11 +31,20 @@ export default class BaseLayout extends React.Component {
|
||||
|
||||
let Info = getComponent("info")
|
||||
let Operations = getComponent("operations", true)
|
||||
let Models = getComponent("models", true)
|
||||
let Models = getComponent("Models", true)
|
||||
let AuthorizeBtn = getComponent("authorizeBtn", true)
|
||||
let Row = getComponent("Row")
|
||||
let Col = getComponent("Col")
|
||||
let Errors = getComponent("errors", true)
|
||||
|
||||
let isLoading = specSelectors.loadingStatus() === "loading"
|
||||
let isFailed = specSelectors.loadingStatus() === "failed"
|
||||
let filter = layoutSelectors.currentFilter()
|
||||
|
||||
let inputStyle = {}
|
||||
if(isFailed) inputStyle.color = "red"
|
||||
if(isLoading) inputStyle.color = "#aaa"
|
||||
|
||||
const Schemes = getComponent("schemes")
|
||||
|
||||
const isSpecEmpty = !specSelectors.specStr()
|
||||
@@ -57,6 +71,7 @@ export default class BaseLayout extends React.Component {
|
||||
{ schemes && schemes.size ? (
|
||||
<Schemes schemes={ schemes } specActions={ specActions } />
|
||||
) : null }
|
||||
|
||||
{ securityDefinitions ? (
|
||||
<AuthorizeBtn />
|
||||
) : null }
|
||||
@@ -64,6 +79,15 @@ export default class BaseLayout extends React.Component {
|
||||
</div>
|
||||
) : null }
|
||||
|
||||
{
|
||||
filter === null || filter === false ? null :
|
||||
<div className="filter-container">
|
||||
<Col className="filter wrapper" mobile={12}>
|
||||
<input className="operation-filter-input" placeholder="Filter by tag" type="text" onChange={this.onFilterChange} value={filter === true || filter === "true" ? "" : filter} disabled={isLoading} style={inputStyle} />
|
||||
</Col>
|
||||
</div>
|
||||
}
|
||||
|
||||
<Row>
|
||||
<Col mobile={12} desktop={12} >
|
||||
<Operations/>
|
||||
|
||||
47
src/core/components/model-collapse.jsx
Normal file
47
src/core/components/model-collapse.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class ModelCollapse extends Component {
|
||||
static propTypes = {
|
||||
collapsedContent: PropTypes.any,
|
||||
collapsed: PropTypes.bool,
|
||||
children: PropTypes.any,
|
||||
title: PropTypes.element
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
collapsedContent: "{...}",
|
||||
collapsed: true,
|
||||
title: null
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
let { collapsed, collapsedContent } = this.props
|
||||
|
||||
this.state = {
|
||||
collapsed: collapsed !== undefined ? collapsed : ModelCollapse.defaultProps.collapsed,
|
||||
collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent
|
||||
}
|
||||
}
|
||||
|
||||
toggleCollapsed=()=>{
|
||||
this.setState({
|
||||
collapsed: !this.state.collapsed
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const {title} = this.props
|
||||
return (
|
||||
<span>
|
||||
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
|
||||
</span>
|
||||
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -28,23 +28,23 @@ export default class ModelExample extends React.Component {
|
||||
|
||||
render() {
|
||||
let { getComponent, specSelectors, schema, example, isExecute } = this.props
|
||||
const Model = getComponent("model")
|
||||
const ModelWrapper = getComponent("ModelWrapper")
|
||||
|
||||
return <div>
|
||||
<ul className="tab">
|
||||
<li className={ "tabitem" + ( isExecute || this.state.activeTab === "example" ? " active" : "") }>
|
||||
<a className="tablinks" data-name="example" onClick={ this.activeTab }>Example Value</a>
|
||||
</li>
|
||||
<li className={ "tabitem" + ( !isExecute && this.state.activeTab === "model" ? " active" : "") }>
|
||||
{ schema ? <li className={ "tabitem" + ( !isExecute && this.state.activeTab === "model" ? " active" : "") }>
|
||||
<a className={ "tablinks" + ( isExecute ? " inactive" : "" )} data-name="model" onClick={ this.activeTab }>Model</a>
|
||||
</li>
|
||||
</li> : null }
|
||||
</ul>
|
||||
<div>
|
||||
{
|
||||
(isExecute || this.state.activeTab === "example") && example
|
||||
}
|
||||
{
|
||||
!isExecute && this.state.activeTab === "model" && <Model schema={ schema }
|
||||
!isExecute && this.state.activeTab === "model" && <ModelWrapper schema={ schema }
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={ 1 } />
|
||||
|
||||
23
src/core/components/model-wrapper.jsx
Normal file
23
src/core/components/model-wrapper.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React, { Component, } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class ModelComponent extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent } = this.props
|
||||
const Model = getComponent("Model")
|
||||
|
||||
return <div className="model-box">
|
||||
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,219 +1,7 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { List } from "immutable"
|
||||
const braceOpen = "{"
|
||||
const braceClose = "}"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
const EnumModel = ({ value }) => {
|
||||
let collapsedContent = <span>Array [ { value.count() } ]</span>
|
||||
return <span className="prop-enum">
|
||||
Enum:<br />
|
||||
<Collapse collapsedContent={ collapsedContent }>
|
||||
[ { value.join(", ") } ]
|
||||
</Collapse>
|
||||
</span>
|
||||
}
|
||||
|
||||
EnumModel.propTypes = {
|
||||
value: ImPropTypes.iterable
|
||||
}
|
||||
|
||||
class ObjectModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
expandDepth: PropTypes.number,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, name, isRef, getComponent, depth, ...props } = this.props
|
||||
let { expandDepth } = this.props
|
||||
const JumpToPath = getComponent("JumpToPath", true)
|
||||
let description = schema.get("description")
|
||||
let properties = schema.get("properties")
|
||||
let additionalProperties = schema.get("additionalProperties")
|
||||
let title = schema.get("title") || name
|
||||
let required = schema.get("required")
|
||||
const Markdown = getComponent("Markdown")
|
||||
const JumpToPathSection = ({ name }) => <span className="model-jump-to-path"><JumpToPath path={`definitions.${name}`} /></span>
|
||||
let collapsedContent = (<span>
|
||||
<span>{ braceOpen }</span>...<span>{ braceClose }</span>
|
||||
{
|
||||
isRef ? <JumpToPathSection name={ name }/> : ""
|
||||
}
|
||||
</span>)
|
||||
|
||||
return <span className="model">
|
||||
{
|
||||
title && <span className="model-title">
|
||||
{ isRef && schema.get("$$ref") && <span className="model-hint">{ schema.get("$$ref") }</span> }
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
}
|
||||
<Collapse collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
|
||||
<span className="brace-open object">{ braceOpen }</span>
|
||||
{
|
||||
!isRef ? null : <JumpToPathSection name={ name }/>
|
||||
}
|
||||
<span className="inner-object">
|
||||
{
|
||||
<table className="model" style={{ marginLeft: "2em" }}><tbody>
|
||||
{
|
||||
!description ? null : <tr style={{ color: "#999", fontStyle: "italic" }}>
|
||||
<td>description:</td>
|
||||
<td>
|
||||
<Markdown source={ description } />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
{
|
||||
!(properties && properties.size) ? null : properties.entrySeq().map(
|
||||
([key, value]) => {
|
||||
let isRequired = List.isList(required) && required.contains(key)
|
||||
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
|
||||
if ( isRequired ) {
|
||||
propertyStyle.fontWeight = "bold"
|
||||
}
|
||||
|
||||
return (<tr key={key}>
|
||||
<td style={ propertyStyle }>{ key }:</td>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
<Model key={ `object-${name}-${key}_${value}` } { ...props }
|
||||
required={ isRequired }
|
||||
getComponent={ getComponent }
|
||||
schema={ value }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
!additionalProperties || !additionalProperties.size ? null
|
||||
: <tr>
|
||||
<td>{ "< * >:" }</td>
|
||||
<td>
|
||||
<Model { ...props } required={ false }
|
||||
getComponent={ getComponent }
|
||||
schema={ additionalProperties }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody></table>
|
||||
}
|
||||
</span>
|
||||
<span className="brace-close">{ braceClose }</span>
|
||||
</Collapse>
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
class Primitive extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
required: PropTypes.bool
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, getComponent, name, required } = this.props
|
||||
|
||||
if(!schema || !schema.get) {
|
||||
// don't render if schema isn't correctly formed
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
let type = schema.get("type")
|
||||
let format = schema.get("format")
|
||||
let xml = schema.get("xml")
|
||||
let enumArray = schema.get("enum")
|
||||
let title = schema.get("title") || name
|
||||
let description = schema.get("description")
|
||||
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
let style = required ? { fontWeight: "bold" } : {}
|
||||
const Markdown = getComponent("Markdown")
|
||||
|
||||
return <span className="model">
|
||||
{
|
||||
title && <span className="model-title" style={{ marginRight: "2em" }}>
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
}
|
||||
<span className="prop-type" style={ style }>{ type }</span> { required && <span style={{ color: "red" }}>*</span>}
|
||||
{ format && <span className="prop-format">(${format})</span>}
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
<Markdown source={ description } />
|
||||
}
|
||||
{
|
||||
xml && xml.size ? (<span><br /><span style={ propStyle }>xml:</span>
|
||||
{
|
||||
xml.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }><br/> {key}: { String(v) }</span>).toArray()
|
||||
}
|
||||
</span>): null
|
||||
}
|
||||
{
|
||||
enumArray && <EnumModel value={ enumArray } />
|
||||
}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
expandDepth: PropTypes.number,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { required, schema, depth, name, expandDepth } = this.props
|
||||
let items = schema.get("items")
|
||||
let title = schema.get("title") || name
|
||||
let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 )
|
||||
|
||||
return <span className="model">
|
||||
{
|
||||
title && <span className="model-title">
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
}
|
||||
<Collapse collapsed={ depth > expandDepth } collapsedContent="[...]">
|
||||
[
|
||||
<span><Model { ...this.props } name="" schema={ items } required={ false }/></span>
|
||||
]
|
||||
{
|
||||
properties.size ? <span>
|
||||
{ properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={propStyle}>
|
||||
<br />{ `${key}:`}{ String(v) }</span>)
|
||||
}<br /></span>
|
||||
: null
|
||||
}
|
||||
</Collapse>
|
||||
{ required && <span style={{ color: "red" }}>*</span>}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Model extends Component {
|
||||
export default class Model extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
@@ -229,6 +17,9 @@ class Model extends Component {
|
||||
if ( ref.indexOf("#/definitions/") !== -1 ) {
|
||||
return ref.replace(/^.*#\/definitions\//, "")
|
||||
}
|
||||
if ( ref.indexOf("#/components/schemas/") !== -1 ) {
|
||||
return ref.replace("#/components/schemas/", "")
|
||||
}
|
||||
}
|
||||
|
||||
getRefSchema =( model )=> {
|
||||
@@ -238,11 +29,16 @@ class Model extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { schema, getComponent, required, name, isRef } = this.props
|
||||
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
|
||||
let ObjectModel = getComponent("ObjectModel")
|
||||
let ArrayModel = getComponent("ArrayModel")
|
||||
let PrimitiveModel = getComponent("PrimitiveModel")
|
||||
let $$ref = schema && schema.get("$$ref")
|
||||
let modelName = $$ref && this.getModelName( $$ref )
|
||||
let modelSchema, type
|
||||
|
||||
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
|
||||
|
||||
if ( schema && (schema.get("type") || schema.get("properties")) ) {
|
||||
modelSchema = schema
|
||||
} else if ( $$ref ) {
|
||||
@@ -256,73 +52,30 @@ class Model extends Component {
|
||||
|
||||
switch(type) {
|
||||
case "object":
|
||||
return <ObjectModel className="object" { ...this.props } schema={ modelSchema }
|
||||
name={ name || modelName }
|
||||
isRef={ isRef!== undefined ? isRef : !!$$ref }/>
|
||||
return <ObjectModel
|
||||
className="object" { ...this.props }
|
||||
schema={ modelSchema }
|
||||
name={ name || modelName }
|
||||
deprecated={deprecated}
|
||||
isRef={ isRef!== undefined ? isRef : !!$$ref } />
|
||||
case "array":
|
||||
return <ArrayModel className="array" { ...this.props } schema={ modelSchema } name={ name || modelName } required={ required } />
|
||||
return <ArrayModel
|
||||
className="array" { ...this.props }
|
||||
schema={ modelSchema }
|
||||
name={ name || modelName }
|
||||
deprecated={deprecated}
|
||||
required={ required } />
|
||||
case "string":
|
||||
case "number":
|
||||
case "integer":
|
||||
case "boolean":
|
||||
default:
|
||||
return <Primitive { ...this.props } getComponent={ getComponent } schema={ modelSchema } name={ name || modelName } required={ required }/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default class ModelComponent extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
return <div className="model-box">
|
||||
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
class Collapse extends Component {
|
||||
static propTypes = {
|
||||
collapsedContent: PropTypes.any,
|
||||
collapsed: PropTypes.bool,
|
||||
children: PropTypes.any
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
collapsedContent: "{...}",
|
||||
collapsed: true,
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
let { collapsed, collapsedContent } = this.props
|
||||
|
||||
this.state = {
|
||||
collapsed: collapsed !== undefined ? collapsed : Collapse.defaultProps.collapsed,
|
||||
collapsedContent: collapsedContent || Collapse.defaultProps.collapsedContent
|
||||
}
|
||||
}
|
||||
|
||||
toggleCollapsed=()=>{
|
||||
this.setState({
|
||||
collapsed: !this.state.collapsed
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
return (<span>
|
||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
|
||||
</span>
|
||||
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
|
||||
</span>)
|
||||
return <PrimitiveModel
|
||||
{ ...this.props }
|
||||
getComponent={ getComponent }
|
||||
schema={ modelSchema }
|
||||
name={ name || modelName }
|
||||
deprecated={deprecated}
|
||||
required={ required }/> }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class Models extends Component {
|
||||
let { docExpansion } = getConfigs()
|
||||
let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" )
|
||||
|
||||
const Model = getComponent("model")
|
||||
const ModelWrapper = getComponent("ModelWrapper")
|
||||
const Collapse = getComponent("Collapse")
|
||||
|
||||
if (!definitions.size) return null
|
||||
@@ -28,11 +28,11 @@ export default class Models extends Component {
|
||||
<use xlinkHref={showModels ? "#large-arrow-down" : "#large-arrow"} />
|
||||
</svg>
|
||||
</h4>
|
||||
<Collapse isOpened={showModels} animated>
|
||||
<Collapse isOpened={showModels}>
|
||||
{
|
||||
definitions.entrySeq().map( ( [ name, model ])=>{
|
||||
return <div className="model-container" key={ `models-section-${name}` }>
|
||||
<Model name={ name }
|
||||
<ModelWrapper name={ name }
|
||||
schema={ model }
|
||||
isRef={ true }
|
||||
getComponent={ getComponent }
|
||||
|
||||
105
src/core/components/object-model.jsx
Normal file
105
src/core/components/object-model.jsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, { Component, } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
|
||||
const braceOpen = "{"
|
||||
const braceClose = "}"
|
||||
|
||||
export default class ObjectModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
expandDepth: PropTypes.number,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, name, isRef, getComponent, depth, ...props } = this.props
|
||||
let { expandDepth } = this.props
|
||||
let description = schema.get("description")
|
||||
let properties = schema.get("properties")
|
||||
let additionalProperties = schema.get("additionalProperties")
|
||||
let title = schema.get("title") || name
|
||||
let requiredProperties = schema.get("required")
|
||||
|
||||
const JumpToPath = getComponent("JumpToPath", true)
|
||||
const Markdown = getComponent("Markdown")
|
||||
const Model = getComponent("Model")
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
|
||||
const JumpToPathSection = ({ name }) => <span className="model-jump-to-path"><JumpToPath path={`definitions.${name}`} /></span>
|
||||
const collapsedContent = (<span>
|
||||
<span>{ braceOpen }</span>...<span>{ braceClose }</span>
|
||||
{
|
||||
isRef ? <JumpToPathSection name={ name }/> : ""
|
||||
}
|
||||
</span>)
|
||||
|
||||
const titleEl = title && <span className="model-title">
|
||||
{ isRef && schema.get("$$ref") && <span className="model-hint">{ schema.get("$$ref") }</span> }
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
|
||||
return <span className="model">
|
||||
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
|
||||
<span className="brace-open object">{ braceOpen }</span>
|
||||
{
|
||||
!isRef ? null : <JumpToPathSection name={ name }/>
|
||||
}
|
||||
<span className="inner-object">
|
||||
{
|
||||
<table className="model" style={{ marginLeft: "2em" }}><tbody>
|
||||
{
|
||||
!description ? null : <tr style={{ color: "#999", fontStyle: "italic" }}>
|
||||
<td>description:</td>
|
||||
<td>
|
||||
<Markdown source={ description } />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
{
|
||||
!(properties && properties.size) ? null : properties.entrySeq().map(
|
||||
([key, value]) => {
|
||||
let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)
|
||||
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
|
||||
if ( isRequired ) {
|
||||
propertyStyle.fontWeight = "bold"
|
||||
}
|
||||
|
||||
return (<tr key={key}>
|
||||
<td style={ propertyStyle }>
|
||||
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> }
|
||||
</td>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
<Model key={ `object-${name}-${key}_${value}` } { ...props }
|
||||
required={ isRequired }
|
||||
getComponent={ getComponent }
|
||||
schema={ value }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
!additionalProperties || !additionalProperties.size ? null
|
||||
: <tr>
|
||||
<td>{ "< * >:" }</td>
|
||||
<td>
|
||||
<Model { ...props } required={ false }
|
||||
getComponent={ getComponent }
|
||||
schema={ additionalProperties }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody></table>
|
||||
}
|
||||
</span>
|
||||
<span className="brace-close">{ braceClose }</span>
|
||||
</ModelCollapse>
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,8 @@ export default class Operation extends PureComponent {
|
||||
specActions,
|
||||
specSelectors,
|
||||
authActions,
|
||||
authSelectors
|
||||
authSelectors,
|
||||
getConfigs
|
||||
} = this.props
|
||||
|
||||
let summary = operation.get("summary")
|
||||
@@ -141,6 +142,10 @@ export default class Operation extends PureComponent {
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
const Schemes = getComponent( "schemes" )
|
||||
|
||||
const { deepLinking } = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
|
||||
// Merge in Live Response
|
||||
if(response && response.size > 0) {
|
||||
let notDocumented = !responses.get(String(response.get("status")))
|
||||
@@ -152,13 +157,18 @@ export default class Operation extends PureComponent {
|
||||
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
|
||||
|
||||
return (
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey} >
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
|
||||
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||
<span>{path}</span>
|
||||
<JumpToPath path={jumpToKey} />
|
||||
</span>
|
||||
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
href={ isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : ""} >
|
||||
<span>{path}</span>
|
||||
</a>
|
||||
<JumpToPath path={jumpToKey} />
|
||||
</span>
|
||||
|
||||
{ !showSummary ? null :
|
||||
<div className="opblock-summary-description">
|
||||
@@ -191,13 +201,16 @@ export default class Operation extends PureComponent {
|
||||
<div className="opblock-external-docs-wrapper">
|
||||
<h4 className="opblock-title_normal">Find more details</h4>
|
||||
<div className="opblock-external-docs">
|
||||
<span className="opblock-external-docs__description">{ externalDocs.get("description") }</span>
|
||||
<span className="opblock-external-docs__description">
|
||||
<Markdown source={ externalDocs.get("description") } />
|
||||
</span>
|
||||
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
|
||||
</div>
|
||||
</div> : null
|
||||
}
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { this.onTryoutClick }
|
||||
onCancelClick = { this.onCancelClick }
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { helpers } from "swagger-client"
|
||||
|
||||
const { opId } = helpers
|
||||
|
||||
export default class Operations extends React.Component {
|
||||
|
||||
@@ -33,7 +36,29 @@ export default class Operations extends React.Component {
|
||||
const Collapse = getComponent("Collapse")
|
||||
|
||||
let showSummary = layoutSelectors.showSummary()
|
||||
let { docExpansion, displayOperationId, displayRequestDuration } = getConfigs()
|
||||
let {
|
||||
docExpansion,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
maxDisplayedTags,
|
||||
deepLinking
|
||||
} = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
|
||||
let filter = layoutSelectors.currentFilter()
|
||||
|
||||
if (filter) {
|
||||
if (filter !== true) {
|
||||
taggedOps = taggedOps.filter((tagObj, tag) => {
|
||||
return tag.indexOf(filter) !== -1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDisplayedTags && !isNaN(maxDisplayedTags) && maxDisplayedTags >= 0) {
|
||||
taggedOps = taggedOps.slice(0, maxDisplayedTags)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -41,6 +66,8 @@ export default class Operations extends React.Component {
|
||||
taggedOps.map( (tagObj, tag) => {
|
||||
let operations = tagObj.get("operations")
|
||||
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
|
||||
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
|
||||
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
|
||||
|
||||
let isShownKey = ["operations-tag", tag]
|
||||
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
|
||||
@@ -48,14 +75,38 @@ export default class Operations extends React.Component {
|
||||
return (
|
||||
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
|
||||
|
||||
<h4 onClick={() => layoutActions.show(isShownKey, !showTag)} className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }>
|
||||
<span>{tag}</span>
|
||||
<h4
|
||||
onClick={() => layoutActions.show(isShownKey, !showTag)}
|
||||
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
|
||||
id={isShownKey.join("-")}>
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
href={ isDeepLinkingEnabled ? `#/${tag}` : ""}>
|
||||
<span>{tag}</span>
|
||||
</a>
|
||||
{ !tagDescription ? null :
|
||||
<small>
|
||||
{ tagDescription }
|
||||
</small>
|
||||
}
|
||||
|
||||
<div>
|
||||
{ !tagExternalDocsDescription ? null :
|
||||
<small>
|
||||
{ tagExternalDocsDescription }
|
||||
{ tagExternalDocsUrl ? ": " : null }
|
||||
{ tagExternalDocsUrl ?
|
||||
<a
|
||||
href={tagExternalDocsUrl}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
target={"_blank"}
|
||||
>{tagExternalDocsUrl}</a> : null
|
||||
}
|
||||
</small>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button className="expand-operation" title="Expand operation" onClick={() => layoutActions.show(isShownKey, !showTag)}>
|
||||
<svg className="arrow" width="20" height="20">
|
||||
<use xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
|
||||
@@ -67,11 +118,14 @@ export default class Operations extends React.Component {
|
||||
{
|
||||
operations.map( op => {
|
||||
|
||||
const isShownKey = ["operations", op.get("id"), tag]
|
||||
const path = op.get("path", "")
|
||||
const method = op.get("method", "")
|
||||
const jumpToKey = `paths.${path}.${method}`
|
||||
|
||||
const operationId =
|
||||
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
|
||||
const isShownKey = ["operations", tag, operationId]
|
||||
|
||||
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
|
||||
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
|
||||
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
|
||||
|
||||
@@ -49,10 +49,11 @@ export default class ParamBody extends PureComponent {
|
||||
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
|
||||
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : {}
|
||||
let isXml = /xml/i.test(consumesValue)
|
||||
let isJson = /json/i.test(consumesValue)
|
||||
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
|
||||
|
||||
if ( paramValue !== undefined ) {
|
||||
let val = !paramValue && !isXml ? "{}" : paramValue
|
||||
let val = !paramValue && isJson ? "{}" : paramValue
|
||||
this.setState({ value: val })
|
||||
this.onChange(val, {isXml: isXml, isEditBox: isExecute})
|
||||
} else {
|
||||
@@ -79,8 +80,11 @@ export default class ParamBody extends PureComponent {
|
||||
_onChange = (val, isXml) => { (this.props.onChange || NOOP)(this.props.param, val, isXml) }
|
||||
|
||||
handleOnChange = e => {
|
||||
let {consumesValue} = this.props
|
||||
this.onChange(e.target.value.trim(), {isXml: /xml/i.test(consumesValue)})
|
||||
const {consumesValue} = this.props
|
||||
const isJson = /json/i.test(consumesValue)
|
||||
const isXml = /xml/i.test(consumesValue)
|
||||
const inputValue = isJson ? e.target.value.trim() : e.target.value
|
||||
this.onChange(inputValue, {isXml})
|
||||
}
|
||||
|
||||
toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))
|
||||
|
||||
@@ -94,7 +94,7 @@ export default class ParameterRow extends Component {
|
||||
{ param.get("name") }
|
||||
{ !required ? null : <span style={{color: "red"}}> *</span> }
|
||||
</div>
|
||||
<div className="parаmeter__type">{ param.get("type") } { itemType && `[${itemType}]` }</div>
|
||||
<div className="parameter__type">{ param.get("type") } { itemType && `[${itemType}]` }</div>
|
||||
<div className="parameter__in">({ param.get("in") })</div>
|
||||
</td>
|
||||
|
||||
|
||||
@@ -72,7 +72,9 @@ export default class Parameters extends Component {
|
||||
return (
|
||||
<div className="opblock-section">
|
||||
<div className="opblock-section-header">
|
||||
<h4 className="opblock-title">Parameters</h4>
|
||||
<div className="tab-header">
|
||||
<h4 className="opblock-title">Parameters</h4>
|
||||
</div>
|
||||
{ allowTryItOut ? (
|
||||
<TryItOutButton enabled={ tryItOutEnabled } onCancelClick={ onCancelClick } onTryoutClick={ onTryoutClick } />
|
||||
) : null }
|
||||
|
||||
55
src/core/components/primitive-model.jsx
Normal file
55
src/core/components/primitive-model.jsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
export default class Primitive extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
required: PropTypes.bool
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, getComponent, required } = this.props
|
||||
|
||||
if(!schema || !schema.get) {
|
||||
// don't render if schema isn't correctly formed
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
let type = schema.get("type")
|
||||
let format = schema.get("format")
|
||||
let xml = schema.get("xml")
|
||||
let enumArray = schema.get("enum")
|
||||
let description = schema.get("description")
|
||||
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
let style = required ? { fontWeight: "bold" } : {}
|
||||
const Markdown = getComponent("Markdown")
|
||||
const EnumModel = getComponent("EnumModel")
|
||||
|
||||
return <span className="prop">
|
||||
<span className="prop-type" style={ style }>{ type }</span> { required && <span style={{ color: "red" }}>*</span>}
|
||||
{ format && <span className="prop-format">(${format})</span>}
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
<Markdown source={ description } />
|
||||
}
|
||||
{
|
||||
xml && xml.size ? (<span><br /><span style={ propStyle }>xml:</span>
|
||||
{
|
||||
xml.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }><br/> {key}: { String(v) }</span>).toArray()
|
||||
}
|
||||
</span>): null
|
||||
}
|
||||
{
|
||||
enumArray && <EnumModel value={ enumArray } getComponent={ getComponent } />
|
||||
}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,28 @@ import PropTypes from "prop-types"
|
||||
import Remarkable from "react-remarkable"
|
||||
import sanitize from "sanitize-html"
|
||||
|
||||
function Markdown({ source }) {
|
||||
const sanitized = sanitizer(source)
|
||||
|
||||
// sometimes the sanitizer returns "undefined" as a string
|
||||
if(!source || !sanitized || sanitized === "undefined") {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div className="markdown">
|
||||
<Remarkable
|
||||
options={{html: true, typographer: true, breaks: true, linkify: true, linkTarget: "_blank"}}
|
||||
source={sanitized}
|
||||
></Remarkable>
|
||||
</div>
|
||||
}
|
||||
|
||||
Markdown.propTypes = {
|
||||
source: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default Markdown
|
||||
|
||||
const sanitizeOptions = {
|
||||
textFilter: function(text) {
|
||||
return text
|
||||
@@ -10,22 +32,6 @@ const sanitizeOptions = {
|
||||
}
|
||||
}
|
||||
|
||||
function Markdown({ source }) {
|
||||
const sanitized = sanitize(source, sanitizeOptions)
|
||||
|
||||
// sometimes the sanitizer returns "undefined" as a string
|
||||
if(!source || !sanitized || sanitized === "undefined") {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Remarkable
|
||||
options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
|
||||
source={sanitized}
|
||||
></Remarkable>
|
||||
export function sanitizer(str) {
|
||||
return sanitize(str, sanitizeOptions)
|
||||
}
|
||||
|
||||
Markdown.propTypes = {
|
||||
source: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default Markdown
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class ResponseBody extends React.Component {
|
||||
|
||||
// Image
|
||||
} else if (/^image\//i.test(contentType)) {
|
||||
bodyEl = <img src={ url } />
|
||||
bodyEl = <img style={{ maxWidth: "100%" }} src={ window.URL.createObjectURL(content) } />
|
||||
|
||||
// Audio
|
||||
} else if (/^audio\//i.test(contentType)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import { fromJS, Seq } from "immutable"
|
||||
import { getSampleSchema } from "core/utils"
|
||||
|
||||
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
|
||||
@@ -31,6 +31,13 @@ const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
|
||||
}
|
||||
|
||||
export default class Response extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
this.state = {
|
||||
responseContentType: ""
|
||||
}
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
code: PropTypes.string.isRequired,
|
||||
@@ -59,16 +66,29 @@ export default class Response extends React.Component {
|
||||
} = this.props
|
||||
|
||||
let { inferSchema } = fn
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
let schema = inferSchema(response.toJS())
|
||||
let headers = response.get("headers")
|
||||
let examples = response.get("examples")
|
||||
let links = response.get("links")
|
||||
const Headers = getComponent("headers")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
const OperationLink = getComponent("operationLink")
|
||||
const ContentType = getComponent("contentType")
|
||||
|
||||
let sampleResponse = schema ? getSampleSchema(schema, contentType, { includeReadOnly: true }) : null
|
||||
var sampleResponse
|
||||
var schema
|
||||
|
||||
if(isOAS3()) {
|
||||
let oas3SchemaForContentType = response.getIn(["content", this.state.responseContentType, "schema"])
|
||||
sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, { includeReadOnly: true }) : null
|
||||
schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null
|
||||
} else {
|
||||
schema = inferSchema(response.toJS())
|
||||
sampleResponse = schema ? getSampleSchema(schema, contentType, { includeReadOnly: true }) : null
|
||||
}
|
||||
let example = getExampleComponent( sampleResponse, examples, HighlightCode )
|
||||
|
||||
return (
|
||||
@@ -82,6 +102,12 @@ export default class Response extends React.Component {
|
||||
<Markdown source={ response.get( "description" ) } />
|
||||
</div>
|
||||
|
||||
{ isOAS3 ? <ContentType
|
||||
value={this.state.responseContentType}
|
||||
contentTypes={ response.get("content") ? response.get("content").keySeq() : Seq() }
|
||||
onChange={(val) => this.setState({ responseContentType: val })}
|
||||
className="response-content-type" /> : null }
|
||||
|
||||
{ example ? (
|
||||
<ModelExample
|
||||
getComponent={ getComponent }
|
||||
@@ -94,8 +120,15 @@ export default class Response extends React.Component {
|
||||
<Headers headers={ headers }/>
|
||||
) : null}
|
||||
|
||||
</td>
|
||||
|
||||
</td>
|
||||
{specSelectors.isOAS3() ? <td>
|
||||
{ links ?
|
||||
links.toSeq().map((link, key) => {
|
||||
return <OperationLink key={key} name={key} link={ link }/>
|
||||
})
|
||||
: <i>No links</i>}
|
||||
</td> : null}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ export default class Responses extends React.Component {
|
||||
<div className="responses-wrapper">
|
||||
<div className="opblock-section-header">
|
||||
<h4>Responses</h4>
|
||||
<label>
|
||||
{ specSelectors.isOAS3() ? null : <label>
|
||||
<span>Response content type</span>
|
||||
<ContentType value={producesValue}
|
||||
onChange={this.onChangeProducesWrapper}
|
||||
contentTypes={produces}
|
||||
className="execute-content-type"/>
|
||||
</label>
|
||||
</label> }
|
||||
</div>
|
||||
<div className="responses-inner">
|
||||
{
|
||||
@@ -68,6 +68,7 @@ export default class Responses extends React.Component {
|
||||
<tr className="responses-header">
|
||||
<td className="col col_header response-col_status">Code</td>
|
||||
<td className="col col_header response-col_description">Description</td>
|
||||
{ specSelectors.isOAS3() ? <td className="col col_header response-col_description">Links</td> : null }
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -19,8 +19,9 @@ export default class Schemes extends React.Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if ( this.props.operationScheme && !nextProps.schemes.has(this.props.operationScheme) ) {
|
||||
//fire 'change' event if our selected scheme is no longer an option
|
||||
if ( !this.props.operationScheme || !nextProps.schemes.has(this.props.operationScheme) ) {
|
||||
// if we don't have a selected operationScheme or if our selected scheme is no longer an option,
|
||||
// then fire 'change' event and select the first scheme in the list of options
|
||||
this.setScheme(nextProps.schemes.first())
|
||||
}
|
||||
}
|
||||
|
||||
12
src/core/components/version-stamp.jsx
Normal file
12
src/core/components/version-stamp.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
const VersionStamp = ({ version }) => {
|
||||
return <small><pre className="version"> { version } </pre></small>
|
||||
}
|
||||
|
||||
VersionStamp.propTypes = {
|
||||
version: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default VersionStamp
|
||||
@@ -4,19 +4,21 @@ import System from "core/system"
|
||||
import win from "core/window"
|
||||
import ApisPreset from "core/presets/apis"
|
||||
import * as AllPlugins from "core/plugins/all"
|
||||
import { parseSeach, filterConfigs } from "core/utils"
|
||||
|
||||
const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
|
||||
"apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
|
||||
"showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"]
|
||||
import { parseSearch } from "core/utils"
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo
|
||||
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION, HOSTNAME, BUILD_TIME } = buildInfo
|
||||
|
||||
module.exports = function SwaggerUI(opts) {
|
||||
|
||||
win.versions = win.versions || {}
|
||||
win.versions.swaggerUi = `${PACKAGE_VERSION}/${GIT_COMMIT || "unknown"}${GIT_DIRTY ? "-dirty" : ""}`
|
||||
win.versions.swaggerUi = {
|
||||
version: PACKAGE_VERSION,
|
||||
gitRevision: GIT_COMMIT,
|
||||
gitDirty: GIT_DIRTY,
|
||||
buildTimestamp: BUILD_TIME,
|
||||
machine: HOSTNAME
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
// Some general settings, that we floated to the top
|
||||
@@ -26,15 +28,19 @@ module.exports = function SwaggerUI(opts) {
|
||||
urls: null,
|
||||
layout: "BaseLayout",
|
||||
docExpansion: "list",
|
||||
maxDisplayedTags: null,
|
||||
filter: null,
|
||||
validatorUrl: "https://online.swagger.io/validator",
|
||||
configs: {},
|
||||
custom: {},
|
||||
displayOperationId: false,
|
||||
displayRequestDuration: false,
|
||||
deepLinking: false,
|
||||
|
||||
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
|
||||
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
|
||||
presets: [
|
||||
ApisPreset
|
||||
],
|
||||
|
||||
// Plugins; ( loaded after presets )
|
||||
@@ -50,7 +56,9 @@ module.exports = function SwaggerUI(opts) {
|
||||
store: { },
|
||||
}
|
||||
|
||||
const constructorConfig = deepExtend({}, defaults, opts)
|
||||
let queryConfig = parseSearch()
|
||||
|
||||
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
|
||||
|
||||
const storeConfigs = deepExtend({}, constructorConfig.store, {
|
||||
system: {
|
||||
@@ -59,7 +67,8 @@ module.exports = function SwaggerUI(opts) {
|
||||
plugins: constructorConfig.presets,
|
||||
state: {
|
||||
layout: {
|
||||
layout: constructorConfig.layout
|
||||
layout: constructorConfig.layout,
|
||||
filter: constructorConfig.filter
|
||||
},
|
||||
spec: {
|
||||
spec: "",
|
||||
@@ -80,7 +89,6 @@ module.exports = function SwaggerUI(opts) {
|
||||
store.register([constructorConfig.plugins, inlinePlugin])
|
||||
|
||||
var system = store.getSystem()
|
||||
let queryConfig = parseSeach()
|
||||
|
||||
system.initOAuth = system.authActions.configureAuth
|
||||
|
||||
@@ -91,7 +99,7 @@ module.exports = function SwaggerUI(opts) {
|
||||
|
||||
let localConfig = system.specSelectors.getLocalConfig ? system.specSelectors.getLocalConfig() : {}
|
||||
let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig)
|
||||
store.setConfigs(filterConfigs(mergedConfig, CONFIGS))
|
||||
store.setConfigs(mergedConfig)
|
||||
|
||||
if (fetchedConfig !== null) {
|
||||
if (!queryConfig.url && typeof mergedConfig.spec === "object" && Object.keys(mergedConfig.spec).length) {
|
||||
|
||||
@@ -57,7 +57,8 @@ export class JsonSchema_string extends Component {
|
||||
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select allowedValues={ enumValue }
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
allowedValues={ enumValue }
|
||||
value={ value }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
@@ -121,6 +122,7 @@ export class JsonSchema_array extends PureComponent {
|
||||
render() {
|
||||
let { getComponent, required, schema, fn } = this.props
|
||||
|
||||
let errors = schema.errors || []
|
||||
let itemSchema = fn.inferSchema(schema.items)
|
||||
|
||||
const JsonSchemaForm = getComponent("JsonSchemaForm")
|
||||
@@ -131,19 +133,17 @@ export class JsonSchema_array extends PureComponent {
|
||||
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select multiple={ true }
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
multiple={ true }
|
||||
value={ value }
|
||||
allowedValues={ enumValue }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
|
||||
let errors = schema.errors || []
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ !value || value.count() < 1 ?
|
||||
(errors.length ? <span style={{ color: "red", fortWeight: "bold" }}>{ errors[0] }</span> : null) :
|
||||
{ !value || value.count() < 1 ? null :
|
||||
value.map( (item,i) => {
|
||||
let schema = Object.assign({}, itemSchema)
|
||||
if ( errors.length ) {
|
||||
@@ -153,12 +153,12 @@ export class JsonSchema_array extends PureComponent {
|
||||
return (
|
||||
<div key={i} className="json-schema-form-item">
|
||||
<JsonSchemaForm fn={fn} getComponent={getComponent} value={item} onChange={(val) => this.onItemChange(val, i)} schema={schema} />
|
||||
<Button className="json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button>
|
||||
<Button className="btn btn-sm json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button>
|
||||
</div>
|
||||
)
|
||||
}).toArray()
|
||||
}
|
||||
<Button className="json-schema-form-item-add" onClick={this.addItem}> Add item </Button>
|
||||
<Button className={`btn btn-sm json-schema-form-item-add ${errors.length ? "invalid" : null}`} onClick={this.addItem}> Add item </Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -170,12 +170,14 @@ export class JsonSchema_boolean extends Component {
|
||||
|
||||
onEnumChange = (val) => this.props.onChange(val)
|
||||
render() {
|
||||
let { getComponent, required, value } = this.props
|
||||
let { getComponent, value, schema } = this.props
|
||||
let errors = schema.errors || []
|
||||
const Select = getComponent("Select")
|
||||
|
||||
return (<Select value={ String(value) }
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
value={ String(value) }
|
||||
allowedValues={ fromJS(["true", "false"]) }
|
||||
allowEmptyValue={ !required }
|
||||
allowEmptyValue={ true }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,21 @@ export default function authorize ( { auth, authActions, errActions, configs, au
|
||||
|
||||
// pass action authorizeOauth2 and authentication data through window
|
||||
// to authorize with oauth2
|
||||
|
||||
let callback
|
||||
if (flow === "implicit") {
|
||||
callback = authActions.preAuthorizeImplicit
|
||||
} else if (authConfigs.useBasicAuthenticationWithAccessCodeGrant) {
|
||||
callback = authActions.authorizeAccessCodeWithBasicAuthentication
|
||||
} else {
|
||||
callback = authActions.authorizeAccessCodeWithFormParams
|
||||
}
|
||||
|
||||
win.swaggerUIRedirectOauth2 = {
|
||||
auth: auth,
|
||||
state: state,
|
||||
redirectUrl: redirectUrl,
|
||||
callback: flow === "implicit" ? authActions.preAuthorizeImplicit : authActions.authorizeAccessCode,
|
||||
callback: callback,
|
||||
errCb: errActions.newAuthErr
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import get from "lodash/get"
|
||||
|
||||
export function transformPathToArray(property, jsSpec) {
|
||||
if(property.slice(0,9) === "instance.") {
|
||||
var str = property.slice(9)
|
||||
} else { // eslint-disable-next-line no-redeclare
|
||||
var str = property
|
||||
}
|
||||
|
||||
var pathArr = []
|
||||
|
||||
str
|
||||
.split(".")
|
||||
.map(item => {
|
||||
// "key[0]" becomes ["key", "0"]
|
||||
if(item.includes("[")) {
|
||||
let index = parseInt(item.match(/\[(.*)\]/)[1])
|
||||
let keyName = item.slice(0, item.indexOf("["))
|
||||
return [keyName, index.toString()]
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
.reduce(function(a, b) {
|
||||
// flatten!
|
||||
return a.concat(b)
|
||||
}, [])
|
||||
.concat([""]) // add an empty item into the array, so we don't get stuck with something in our buffer below
|
||||
.reduce((buffer, curr) => {
|
||||
let obj = pathArr.length ? get(jsSpec, pathArr) : jsSpec
|
||||
|
||||
if(get(obj, makeAccessArray(buffer, curr))) {
|
||||
if(buffer.length) {
|
||||
pathArr.push(buffer)
|
||||
}
|
||||
if(curr.length) {
|
||||
pathArr.push(curr)
|
||||
}
|
||||
return ""
|
||||
} else {
|
||||
// attach key to buffer
|
||||
return `${buffer}${buffer.length ? "." : ""}${curr}`
|
||||
}
|
||||
}, "")
|
||||
|
||||
if(typeof get(jsSpec, pathArr) !== "undefined") {
|
||||
return pathArr
|
||||
} else {
|
||||
// if our path is not correct (there is no value at the path),
|
||||
// return null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function makeAccessArray(buffer, curr) {
|
||||
let arr = []
|
||||
|
||||
if(buffer.length) {
|
||||
arr.push(buffer)
|
||||
}
|
||||
|
||||
if(curr.length) {
|
||||
arr.push(curr)
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
|
||||
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
|
||||
let form = {
|
||||
grant_type: "password",
|
||||
scopes: encodeURIComponent(auth.scopes.join(scopeSeparator))
|
||||
scope: encodeURIComponent(auth.scopes.join(scopeSeparator))
|
||||
}
|
||||
let query = {}
|
||||
let headers = {}
|
||||
@@ -111,7 +111,7 @@ export const authorizeApplication = ( auth ) => ( { authActions } ) => {
|
||||
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers })
|
||||
}
|
||||
|
||||
export const authorizeAccessCode = ( { auth, redirectUrl } ) => ( { authActions } ) => {
|
||||
export const authorizeAccessCodeWithFormParams = ( { auth, redirectUrl } ) => ( { authActions } ) => {
|
||||
let { schema, name, clientId, clientSecret } = auth
|
||||
let form = {
|
||||
grant_type: "authorization_code",
|
||||
@@ -124,6 +124,21 @@ export const authorizeAccessCode = ( { auth, redirectUrl } ) => ( { authActions
|
||||
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth})
|
||||
}
|
||||
|
||||
export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl } ) => ( { authActions } ) => {
|
||||
let { schema, name, clientId, clientSecret } = auth
|
||||
let headers = {
|
||||
Authorization: "Basic " + btoa(clientId + ":" + clientSecret)
|
||||
}
|
||||
let form = {
|
||||
grant_type: "authorization_code",
|
||||
code: auth.code,
|
||||
client_id: clientId,
|
||||
redirect_uri: redirectUrl
|
||||
}
|
||||
|
||||
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers})
|
||||
}
|
||||
|
||||
export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, authSelectors } ) => {
|
||||
let { body, query={}, headers={}, name, url, auth } = data
|
||||
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
|
||||
|
||||
1
src/core/plugins/deep-linking/README.md
Normal file
1
src/core/plugins/deep-linking/README.md
Normal file
@@ -0,0 +1 @@
|
||||
See `docs/deep-linking.md`.
|
||||
7
src/core/plugins/deep-linking/helpers.js
Normal file
7
src/core/plugins/deep-linking/helpers.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export const setHash = (value) => {
|
||||
if(value) {
|
||||
return history.pushState(null, null, `#${value}`)
|
||||
} else {
|
||||
return window.location.hash = ""
|
||||
}
|
||||
}
|
||||
18
src/core/plugins/deep-linking/index.js
Normal file
18
src/core/plugins/deep-linking/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// import reducers from "./reducers"
|
||||
// import * as actions from "./actions"
|
||||
// import * as selectors from "./selectors"
|
||||
import * as specWrapActions from "./spec-wrap-actions"
|
||||
import * as layoutWrapActions from "./layout-wrap-actions"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapActions: specWrapActions
|
||||
},
|
||||
layout: {
|
||||
wrapActions: layoutWrapActions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/core/plugins/deep-linking/layout-wrap-actions.js
Normal file
36
src/core/plugins/deep-linking/layout-wrap-actions.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { setHash } from "./helpers"
|
||||
|
||||
export const show = (ori, { getConfigs }) => (...args) => {
|
||||
ori(...args)
|
||||
|
||||
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let [thing, shown] = args
|
||||
let [type] = thing
|
||||
|
||||
if(type === "operations-tag" || type === "operations") {
|
||||
if(!shown) {
|
||||
return setHash("/")
|
||||
}
|
||||
|
||||
if(type === "operations") {
|
||||
let [, tag, operationId] = thing
|
||||
setHash(`/${tag}/${operationId}`)
|
||||
}
|
||||
|
||||
if(type === "operations-tag") {
|
||||
let [, tag] = thing
|
||||
setHash(`/${tag}`)
|
||||
}
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
// This functionality is not mission critical, so if something goes wrong
|
||||
// we'll just move on
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
51
src/core/plugins/deep-linking/spec-wrap-actions.js
Normal file
51
src/core/plugins/deep-linking/spec-wrap-actions.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import scrollTo from "scroll-to-element"
|
||||
|
||||
const SCROLL_OFFSET = -5
|
||||
let hasHashBeenParsed = false
|
||||
|
||||
|
||||
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||
ori(...args)
|
||||
|
||||
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
|
||||
return
|
||||
}
|
||||
|
||||
if(window.location.hash && !hasHashBeenParsed ) {
|
||||
let hash = window.location.hash.slice(1) // # is first character
|
||||
|
||||
if(hash[0] === "!") {
|
||||
// Parse UI 2.x shebangs
|
||||
hash = hash.slice(1)
|
||||
}
|
||||
|
||||
if(hash[0] === "/") {
|
||||
// "/pet/addPet" => "pet/addPet"
|
||||
// makes the split result cleaner
|
||||
// also handles forgotten leading slash
|
||||
hash = hash.slice(1)
|
||||
}
|
||||
|
||||
let [tag, operationId] = hash.split("/")
|
||||
|
||||
if(tag && operationId) {
|
||||
// Pre-expand and scroll to the operation
|
||||
layoutActions.show(["operations-tag", tag], true)
|
||||
layoutActions.show(["operations", tag, operationId], true)
|
||||
|
||||
scrollTo(`#operations-${tag}-${operationId}`, {
|
||||
offset: SCROLL_OFFSET
|
||||
})
|
||||
} else if(tag) {
|
||||
// Pre-expand and scroll to the tag
|
||||
layoutActions.show(["operations-tag", tag], true)
|
||||
|
||||
scrollTo(`#operations-tag-${tag}`, {
|
||||
offset: SCROLL_OFFSET
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
hasHashBeenParsed = true
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { normalizeArray } from "core/utils"
|
||||
|
||||
export const UPDATE_LAYOUT = "layout_update_layout"
|
||||
export const UPDATE_FILTER = "layout_update_filter"
|
||||
export const UPDATE_MODE = "layout_update_mode"
|
||||
export const SHOW = "layout_show"
|
||||
|
||||
@@ -13,6 +14,13 @@ export function updateLayout(layout) {
|
||||
}
|
||||
}
|
||||
|
||||
export function updateFilter(filter) {
|
||||
return {
|
||||
type: UPDATE_FILTER,
|
||||
payload: filter
|
||||
}
|
||||
}
|
||||
|
||||
export function show(thing, shown=true) {
|
||||
thing = normalizeArray(thing)
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
UPDATE_LAYOUT,
|
||||
UPDATE_FILTER,
|
||||
UPDATE_MODE,
|
||||
SHOW
|
||||
} from "./actions"
|
||||
@@ -8,6 +9,8 @@ export default {
|
||||
|
||||
[UPDATE_LAYOUT]: (state, action) => state.set("layout", action.payload),
|
||||
|
||||
[UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
|
||||
|
||||
[SHOW]: (state, action) => {
|
||||
let thing = action.payload.thing
|
||||
let shown = action.payload.shown
|
||||
|
||||
@@ -5,6 +5,8 @@ const state = state => state
|
||||
|
||||
export const current = state => state.get("layout")
|
||||
|
||||
export const currentFilter = state => state.get("filter")
|
||||
|
||||
export const isShown = (state, thing, def) => {
|
||||
thing = normalizeArray(thing)
|
||||
return Boolean(state.getIn(["shown", ...thing], def))
|
||||
|
||||
49
src/core/plugins/oas3/components/callbacks.jsx
Normal file
49
src/core/plugins/oas3/components/callbacks.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
const Callbacks = (props) => {
|
||||
let { callbacks, getComponent } = props
|
||||
// const Markdown = getComponent("Markdown")
|
||||
const Operation = getComponent("operation", true)
|
||||
|
||||
if(!callbacks) {
|
||||
return <span>No callbacks</span>
|
||||
}
|
||||
|
||||
let callbackElements = callbacks.map((callback, callbackName) => {
|
||||
return <div key={callbackName}>
|
||||
<h2>{callbackName}</h2>
|
||||
{ callback.map((pathItem, pathItemName) => {
|
||||
return <div key={pathItemName}>
|
||||
{ pathItem.map((operation, method) => {
|
||||
return <Operation
|
||||
operation={operation}
|
||||
key={method}
|
||||
method={method}
|
||||
isShownKey={["callbacks", operation.get("id"), callbackName]}
|
||||
path={pathItemName}
|
||||
allowTryItOut={false}
|
||||
{...props}></Operation>
|
||||
// return <pre>{JSON.stringify(operation)}</pre>
|
||||
}) }
|
||||
</div>
|
||||
}) }
|
||||
</div>
|
||||
// return <div>
|
||||
// <h2>{name}</h2>
|
||||
// {callback.description && <Markdown source={callback.description}/>}
|
||||
// <pre>{JSON.stringify(callback)}</pre>
|
||||
// </div>
|
||||
})
|
||||
return <div>
|
||||
{callbackElements}
|
||||
</div>
|
||||
}
|
||||
|
||||
Callbacks.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
callbacks: PropTypes.array.isRequired
|
||||
|
||||
}
|
||||
|
||||
export default Callbacks
|
||||
9
src/core/plugins/oas3/components/index.js
Normal file
9
src/core/plugins/oas3/components/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Callbacks from "./callbacks"
|
||||
import RequestBody from "./request-body"
|
||||
import OperationLink from "./operation-link.jsx"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
RequestBody,
|
||||
operationLink: OperationLink
|
||||
}
|
||||
37
src/core/plugins/oas3/components/operation-link.jsx
Normal file
37
src/core/plugins/oas3/components/operation-link.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
class OperationLink extends Component {
|
||||
render() {
|
||||
const { link, name } = this.props
|
||||
|
||||
let targetOp = link.get("operationId") || link.get("operationRef")
|
||||
let parameters = link.get("parameters") && link.get("parameters").toJS()
|
||||
let description = link.get("description")
|
||||
|
||||
return <span>
|
||||
<div style={{ padding: "5px 2px" }}>{name}{description ? `: ${description}` : ""}</div>
|
||||
<pre>
|
||||
Operation `{targetOp}`<br /><br />
|
||||
Parameters {padString(0, JSON.stringify(parameters, null, 2)) || "{}"}<br />
|
||||
</pre>
|
||||
</span>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function padString(n, string) {
|
||||
if(typeof string !== "string") { return "" }
|
||||
return string
|
||||
.split("\n")
|
||||
.map((line, i) => i > 0 ? Array(n + 1).join(" ") + line : line)
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
OperationLink.propTypes = {
|
||||
link: ImPropTypes.orderedMap.isRequired,
|
||||
name: PropTypes.String
|
||||
}
|
||||
|
||||
export default OperationLink
|
||||
42
src/core/plugins/oas3/components/request-body.jsx
Normal file
42
src/core/plugins/oas3/components/request-body.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { OrderedMap } from "immutable"
|
||||
import { getSampleSchema } from "core/utils"
|
||||
|
||||
|
||||
const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) => {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
|
||||
const requestBodyDescription = (requestBody && requestBody.get("description")) || null
|
||||
const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap()
|
||||
contentType = contentType || requestBodyContent.keySeq().first()
|
||||
|
||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||
|
||||
const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType)
|
||||
|
||||
return <div>
|
||||
{ requestBodyDescription &&
|
||||
<Markdown source={requestBodyDescription} />
|
||||
}
|
||||
<ModelExample
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={1}
|
||||
schema={mediaTypeValue.get("schema")}
|
||||
example={<HighlightCode value={sampleSchema} />}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
RequestBody.propTypes = {
|
||||
requestBody: ImPropTypes.orderedMap.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
contentType: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default RequestBody
|
||||
36
src/core/plugins/oas3/helpers.js
Normal file
36
src/core/plugins/oas3/helpers.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from "react"
|
||||
|
||||
export function isOAS3(jsSpec) {
|
||||
const oasVersion = jsSpec.get("openapi")
|
||||
if(!oasVersion) {
|
||||
return false
|
||||
}
|
||||
|
||||
return oasVersion.startsWith("3.0.0")
|
||||
}
|
||||
|
||||
export function isSwagger2(jsSpec) {
|
||||
const swaggerVersion = jsSpec.get("swagger")
|
||||
if(!swaggerVersion) {
|
||||
return false
|
||||
}
|
||||
|
||||
return swaggerVersion.startsWith("2")
|
||||
}
|
||||
|
||||
export function OAS3ComponentWrapFactory(Component) {
|
||||
return (Ori, system) => (props) => {
|
||||
if(system && system.specSelectors && system.specSelectors.specJson) {
|
||||
const spec = system.specSelectors.specJson()
|
||||
|
||||
if(isOAS3(spec)) {
|
||||
return <Component {...props} {...system} Ori={Ori}></Component>
|
||||
} else {
|
||||
return <Ori {...props}></Ori>
|
||||
}
|
||||
} else {
|
||||
console.warn("OAS3 wrapper: couldn't get spec")
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/core/plugins/oas3/index.js
Normal file
17
src/core/plugins/oas3/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// import reducers from "./reducers"
|
||||
// import * as actions from "./actions"
|
||||
import * as wrapSelectors from "./wrap-selectors"
|
||||
import components from "./components"
|
||||
import wrapComponents from "./wrap-components"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
components,
|
||||
wrapComponents,
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/core/plugins/oas3/wrap-components/index.js
Normal file
15
src/core/plugins/oas3/wrap-components/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import Markdown from "./markdown"
|
||||
import parameters from "./parameters"
|
||||
import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
import Model from "./model"
|
||||
import TryItOutButton from "./try-it-out-button"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
parameters,
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
onlineValidatorBadge: OnlineValidatorBadge,
|
||||
TryItOutButton
|
||||
}
|
||||
11
src/core/plugins/oas3/wrap-components/markdown.js
Normal file
11
src/core/plugins/oas3/wrap-components/markdown.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from "react"
|
||||
import ReactMarkdown from "react-markdown"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
import { sanitizer } from "core/components/providers/markdown"
|
||||
|
||||
export default OAS3ComponentWrapFactory(({ source }) => { return source ? (
|
||||
<ReactMarkdown
|
||||
source={sanitizer(source)}
|
||||
className={"renderedMarkdown"}
|
||||
/>
|
||||
) : null})
|
||||
37
src/core/plugins/oas3/wrap-components/model.jsx
Normal file
37
src/core/plugins/oas3/wrap-components/model.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
import { Model } from "core/components/model"
|
||||
|
||||
|
||||
class ModelComponent extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema } = this.props
|
||||
let classes = ["model-box"]
|
||||
let isDeprecated = schema.get("deprecated") === true
|
||||
let message = null
|
||||
|
||||
if(isDeprecated) {
|
||||
classes.push("deprecated")
|
||||
message = <span className="model-deprecated-warning">Deprecated:</span>
|
||||
}
|
||||
|
||||
return <div className={classes.join(" ")}>
|
||||
{message}
|
||||
<Model { ...this.props }
|
||||
depth={ 1 }
|
||||
expandDepth={ this.props.expandDepth || 0 }
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default OAS3ComponentWrapFactory(ModelComponent)
|
||||
@@ -0,0 +1,5 @@
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
// We're disabling the Online Validator Badge until the online validator
|
||||
// can handle OAS3 specs.
|
||||
export default OAS3ComponentWrapFactory(() => null)
|
||||
181
src/core/plugins/oas3/wrap-components/parameters.jsx
Normal file
181
src/core/plugins/oas3/wrap-components/parameters.jsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Im, { Map } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
// More readable, just iterate over maps, only
|
||||
const eachMap = (iterable, fn) => iterable.valueSeq().filter(Im.Map.isMap).map(fn)
|
||||
|
||||
class Parameters extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
callbackVisible: false,
|
||||
parametersVisible: true,
|
||||
requestBodyContentType: ""
|
||||
}
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
parameters: ImPropTypes.list.isRequired,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
tryItOutEnabled: PropTypes.bool,
|
||||
allowTryItOut: PropTypes.bool,
|
||||
onTryoutClick: PropTypes.func,
|
||||
onCancelClick: PropTypes.func,
|
||||
onChangeKey: PropTypes.array,
|
||||
pathMethod: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
|
||||
static defaultProps = {
|
||||
onTryoutClick: Function.prototype,
|
||||
onCancelClick: Function.prototype,
|
||||
tryItOutEnabled: false,
|
||||
allowTryItOut: true,
|
||||
onChangeKey: [],
|
||||
}
|
||||
|
||||
onChange = ( param, value, isXml ) => {
|
||||
let {
|
||||
specActions: { changeParam },
|
||||
onChangeKey,
|
||||
} = this.props
|
||||
|
||||
changeParam( onChangeKey, param.get("name"), value, isXml)
|
||||
}
|
||||
|
||||
onChangeConsumesWrapper = ( val ) => {
|
||||
let {
|
||||
specActions: { changeConsumesValue },
|
||||
onChangeKey
|
||||
} = this.props
|
||||
|
||||
changeConsumesValue(onChangeKey, val)
|
||||
}
|
||||
|
||||
toggleTab = (tab) => {
|
||||
if(tab === "parameters"){
|
||||
return this.setState({
|
||||
parametersVisible: true,
|
||||
callbackVisible: false
|
||||
})
|
||||
}else if(tab === "callbacks"){
|
||||
return this.setState({
|
||||
callbackVisible: true,
|
||||
parametersVisible: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
let {
|
||||
onTryoutClick,
|
||||
onCancelClick,
|
||||
parameters,
|
||||
allowTryItOut,
|
||||
tryItOutEnabled,
|
||||
|
||||
fn,
|
||||
getComponent,
|
||||
specSelectors,
|
||||
pathMethod,
|
||||
operation
|
||||
} = this.props
|
||||
|
||||
const ParameterRow = getComponent("parameterRow")
|
||||
const TryItOutButton = getComponent("TryItOutButton")
|
||||
const ContentType = getComponent("contentType")
|
||||
const Callbacks = getComponent("Callbacks", true)
|
||||
const RequestBody = getComponent("RequestBody", true)
|
||||
|
||||
const isExecute = tryItOutEnabled && allowTryItOut
|
||||
const { isOAS3 } = specSelectors
|
||||
|
||||
const requestBody = operation.get("requestBody")
|
||||
return (
|
||||
<div className="opblock-section">
|
||||
<div className="opblock-section-header">
|
||||
<div className="tab-header">
|
||||
<div onClick={() => this.toggleTab("parameters")} className={`tab-item ${this.state.parametersVisible && "active"}`}>
|
||||
<h4 className="opblock-title"><span>Parameters</span></h4>
|
||||
</div>
|
||||
{ operation.get("callbacks") ?
|
||||
(
|
||||
<div onClick={() => this.toggleTab("callbacks")} className={`tab-item ${this.state.callbackVisible && "active"}`}>
|
||||
<h4 className="opblock-title"><span>Callbacks</span></h4>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
{ allowTryItOut ? (
|
||||
<TryItOutButton enabled={ tryItOutEnabled } onCancelClick={ onCancelClick } onTryoutClick={ onTryoutClick } />
|
||||
) : null }
|
||||
</div>
|
||||
{this.state.parametersVisible ? <div className="parameters-container">
|
||||
{ !parameters.count() ? <div className="opblock-description-wrapper"><p>No parameters</p></div> :
|
||||
<div className="table-container">
|
||||
<table className="parameters">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="col col_header parameters-col_name">Name</th>
|
||||
<th className="col col_header parameters-col_description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
eachMap(parameters, (parameter) => (
|
||||
<ParameterRow fn={ fn }
|
||||
getComponent={ getComponent }
|
||||
param={ parameter }
|
||||
key={ parameter.get( "name" ) }
|
||||
onChange={ this.onChange }
|
||||
onChangeConsumes={this.onChangeConsumesWrapper}
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ pathMethod }
|
||||
isExecute={ isExecute }/>
|
||||
)).toArray()
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div> : "" }
|
||||
|
||||
{this.state.callbackVisible ? <div className="callbacks-container opblock-description-wrapper">
|
||||
<Callbacks callbacks={Map(operation.get("callbacks"))} />
|
||||
</div> : "" }
|
||||
{
|
||||
isOAS3() && requestBody && this.state.parametersVisible &&
|
||||
<div className="opblock-section">
|
||||
<div className="opblock-section-header">
|
||||
<h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4>
|
||||
<label>
|
||||
<ContentType
|
||||
value={this.state.requestBodyContentType}
|
||||
contentTypes={ requestBody.get("content").keySeq() }
|
||||
onChange={(val) => this.setState({ requestBodyContentType: val })}
|
||||
className="body-param-content-type" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="opblock-description-wrapper">
|
||||
<RequestBody
|
||||
requestBody={requestBody}
|
||||
contentType={this.state.requestBodyContentType}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default OAS3ComponentWrapFactory(Parameters)
|
||||
@@ -0,0 +1,5 @@
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory(() => {
|
||||
return null
|
||||
})
|
||||
13
src/core/plugins/oas3/wrap-components/version-stamp.jsx
Normal file
13
src/core/plugins/oas3/wrap-components/version-stamp.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from "react"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory((props) => {
|
||||
const { Ori } = props
|
||||
|
||||
return <span>
|
||||
<Ori {...props} />
|
||||
<small style={{ backgroundColor: "#89bf04" }}>
|
||||
<pre className="version">OAS3</pre>
|
||||
</small>
|
||||
</span>
|
||||
})
|
||||
67
src/core/plugins/oas3/wrap-selectors.js
Normal file
67
src/core/plugins/oas3/wrap-selectors.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "./helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (ori, system) => (...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return ori(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const state = state => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
|
||||
const specJson = createSelector(
|
||||
state,
|
||||
spec => spec.get("json", Map())
|
||||
)
|
||||
|
||||
const specResolved = createSelector(
|
||||
state,
|
||||
spec => spec.get("resolved", Map())
|
||||
)
|
||||
|
||||
const spec = state => {
|
||||
let res = specResolved(state)
|
||||
if(res.count() < 1)
|
||||
res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
// Wrappers
|
||||
|
||||
export const definitions = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["components", "schemas"]) || Map()
|
||||
))
|
||||
|
||||
export const host = OAS3NullSelector
|
||||
export const basePath = OAS3NullSelector
|
||||
export const consumes = OAS3NullSelector
|
||||
export const produces = OAS3NullSelector
|
||||
export const schemes = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
export const isOAS3 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isOAS3Helper(spec)
|
||||
}
|
||||
|
||||
export const isSwagger2 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isSwagger2Helper(spec)
|
||||
}
|
||||
@@ -46,6 +46,15 @@ export const spec = state => {
|
||||
return res
|
||||
}
|
||||
|
||||
export const isOAS3 = createSelector(
|
||||
// isOAS3 is stubbed out here to work around an issue with injecting more selectors
|
||||
// in the OAS3 plugin, and to ensure that the function is always available.
|
||||
// It's not perfect, but our hybrid (core+plugin code) implementation for OAS3
|
||||
// needs this. //KS
|
||||
spec,
|
||||
() => false
|
||||
)
|
||||
|
||||
export const info = createSelector(
|
||||
spec,
|
||||
spec => returnSelfOrNewMap(spec && spec.get("info"))
|
||||
@@ -200,15 +209,22 @@ export const operationsWithTags = createSelector(
|
||||
}
|
||||
)
|
||||
|
||||
export const taggedOperations = ( state ) =>( { getConfigs } ) => {
|
||||
let { operationsSorter }= getConfigs()
|
||||
export const taggedOperations = (state) => ({ getConfigs }) => {
|
||||
let { tagsSorter, operationsSorter } = getConfigs()
|
||||
return operationsWithTags(state)
|
||||
.sortBy(
|
||||
(val, key) => key, // get the name of the tag to be passed to the sorter
|
||||
(tagA, tagB) => {
|
||||
let sortFn = (typeof tagsSorter === "function" ? tagsSorter : sorters.tagsSorter[ tagsSorter ])
|
||||
return (!sortFn ? null : sortFn(tagA, tagB))
|
||||
}
|
||||
)
|
||||
.map((ops, tag) => {
|
||||
let sortFn = (typeof operationsSorter === "function" ? operationsSorter : sorters.operationsSorter[ operationsSorter ])
|
||||
let operations = (!sortFn ? ops : ops.sort(sortFn))
|
||||
|
||||
return operationsWithTags(state).map((ops, tag) => {
|
||||
let sortFn = typeof operationsSorter === "function" ? operationsSorter
|
||||
: sorters.operationsSorter[operationsSorter]
|
||||
let operations = !sortFn ? ops : ops.sort(sortFn)
|
||||
|
||||
return Map({tagDetails: tagDetails(state, tag), operations: operations})})
|
||||
return Map({ tagDetails: tagDetails(state, tag), operations: operations })
|
||||
})
|
||||
}
|
||||
|
||||
export const responses = createSelector(
|
||||
@@ -277,12 +293,13 @@ export function parametersIncludeType(parameters, typeValue="") {
|
||||
export function contentTypeValues(state, pathMethod) {
|
||||
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
|
||||
const parameters = op.get("parameters") || new List()
|
||||
const requestContentType = (
|
||||
parametersIncludeType(parameters, "file") ? "multipart/form-data"
|
||||
: parametersIncludeIn(parameters, "formData") ? "application/x-www-form-urlencoded"
|
||||
: op.get("consumes_value")
|
||||
)
|
||||
|
||||
const requestContentType = (
|
||||
op.get("consumes_value") ? op.get("consumes_value")
|
||||
: parametersIncludeType(parameters, "file") ? "multipart/form-data"
|
||||
: parametersIncludeType(parameters, "formData") ? "application/x-www-form-urlencoded"
|
||||
: undefined
|
||||
)
|
||||
|
||||
return fromJS({
|
||||
requestContentType,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import SplitPane from "react-split-pane"
|
||||
import "./split-pane-mode.less"
|
||||
|
||||
const MODE_KEY = ["split-pane-mode"]
|
||||
const MODE_LEFT = "left"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.swagger-ui {
|
||||
.Resizer.vertical.disabled {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { shallowEqualKeys } from "core/utils"
|
||||
import { transformPathToArray } from "core/path-translator"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
fn: { shallowEqualKeys, transformPathToArray }
|
||||
fn: { shallowEqualKeys }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import BasePreset from "./base"
|
||||
import OAS3Plugin from "../plugins/oas3"
|
||||
|
||||
// Just the base, for now.
|
||||
|
||||
@@ -6,5 +7,6 @@ export default function PresetApis() {
|
||||
|
||||
return [
|
||||
BasePreset,
|
||||
OAS3Plugin
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import auth from "core/plugins/auth"
|
||||
import util from "core/plugins/util"
|
||||
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
||||
import downloadUrlPlugin from "core/plugins/download-url"
|
||||
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||
|
||||
import App from "core/components/app"
|
||||
import AuthorizationPopup from "core/components/auth/authorization-popup"
|
||||
@@ -41,10 +42,17 @@ import Footer from "core/components/footer"
|
||||
import ParamBody from "core/components/param-body"
|
||||
import Curl from "core/components/curl"
|
||||
import Schemes from "core/components/schemes"
|
||||
import ModelCollapse from "core/components/model-collapse"
|
||||
import ModelExample from "core/components/model-example"
|
||||
import ModelWrapper from "core/components/model-wrapper"
|
||||
import Model from "core/components/model"
|
||||
import Models from "core/components/models"
|
||||
import EnumModel from "core/components/enum-model"
|
||||
import ObjectModel from "core/components/object-model"
|
||||
import ArrayModel from "core/components/array-model"
|
||||
import PrimitiveModel from "core/components/primitive-model"
|
||||
import TryItOutButton from "core/components/try-it-out-button"
|
||||
import VersionStamp from "core/components/version-stamp"
|
||||
|
||||
import Markdown from "core/components/providers/markdown"
|
||||
|
||||
@@ -88,11 +96,18 @@ export default function() {
|
||||
curl: Curl,
|
||||
schemes: Schemes,
|
||||
modelExample: ModelExample,
|
||||
model: Model,
|
||||
models: Models,
|
||||
ModelWrapper,
|
||||
ModelCollapse,
|
||||
Model,
|
||||
Models,
|
||||
EnumModel,
|
||||
ObjectModel,
|
||||
ArrayModel,
|
||||
PrimitiveModel,
|
||||
TryItOutButton,
|
||||
Markdown,
|
||||
BaseLayout
|
||||
BaseLayout,
|
||||
VersionStamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +134,7 @@ export default function() {
|
||||
auth,
|
||||
ast,
|
||||
SplitPaneModePlugin,
|
||||
downloadUrlPlugin
|
||||
downloadUrlPlugin,
|
||||
deepLinkingPlugin
|
||||
]
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export default class Store {
|
||||
this.boundSystem = Object.assign({},
|
||||
this.getRootInjects(),
|
||||
this.getWrappedAndBoundActions(dispatch),
|
||||
this.getBoundSelectors(getState, this.getSystem),
|
||||
this.getWrappedAndBoundSelectors(getState, this.getSystem),
|
||||
this.getStateThunks(getState),
|
||||
this.getFn(),
|
||||
this.getConfigs()
|
||||
@@ -176,6 +176,36 @@ export default class Store {
|
||||
})
|
||||
}
|
||||
|
||||
getWrappedAndBoundSelectors(getState, getSystem) {
|
||||
let selectorGroups = this.getBoundSelectors(getState, getSystem)
|
||||
return objMap(selectorGroups, (selectors, selectorGroupName) => {
|
||||
let stateName = [selectorGroupName.slice(0, -9)] // selectors = 9 chars
|
||||
let wrappers = this.system.statePlugins[stateName].wrapSelectors
|
||||
if(wrappers) {
|
||||
return objMap(selectors, (selector, selectorName) => {
|
||||
let wrap = wrappers[selectorName]
|
||||
if(!wrap) {
|
||||
return selector
|
||||
}
|
||||
|
||||
if(!Array.isArray(wrap)) {
|
||||
wrap = [wrap]
|
||||
}
|
||||
return wrap.reduce((acc, fn) => {
|
||||
let wrappedSelector = (...args) => {
|
||||
return fn(acc, this.getSystem())(getState().getIn(stateName), ...args)
|
||||
}
|
||||
if(!isFn(wrappedSelector)) {
|
||||
throw new TypeError("wrapSelector needs to return a function that returns a new function (ie the wrapped action)")
|
||||
}
|
||||
return wrappedSelector
|
||||
}, selector || Function.prototype)
|
||||
})
|
||||
}
|
||||
return selectors
|
||||
})
|
||||
}
|
||||
|
||||
getStates(state) {
|
||||
return Object.keys(this.system.statePlugins).reduce((obj, key) => {
|
||||
obj[key] = state.get(key)
|
||||
@@ -197,8 +227,17 @@ export default class Store {
|
||||
}
|
||||
|
||||
getComponents(component) {
|
||||
if(typeof component !== "undefined")
|
||||
const res = this.system.components[component]
|
||||
|
||||
if(Array.isArray(res)) {
|
||||
return res.reduce((ori, wrapper) => {
|
||||
return wrapper(ori, this.getSystem())
|
||||
})
|
||||
}
|
||||
if(typeof component !== "undefined") {
|
||||
return this.system.components[component]
|
||||
}
|
||||
|
||||
return this.system.components
|
||||
}
|
||||
|
||||
@@ -291,6 +330,24 @@ function systemExtend(dest={}, src={}) {
|
||||
return dest
|
||||
}
|
||||
|
||||
// Wrap components
|
||||
// Parses existing components in the system, and prepares them for wrapping via getComponents
|
||||
if(src.wrapComponents) {
|
||||
objMap(src.wrapComponents, (wrapperFn, key) => {
|
||||
const ori = dest.components[key]
|
||||
if(ori && Array.isArray(ori)) {
|
||||
dest.components[key] = ori.concat([wrapperFn])
|
||||
} else if(ori) {
|
||||
dest.components[key] = [ori, wrapperFn]
|
||||
} else {
|
||||
dest.components[key] = null
|
||||
}
|
||||
})
|
||||
|
||||
delete src.wrapComponents
|
||||
}
|
||||
|
||||
|
||||
// Account for wrapActions, make it an array and append to it
|
||||
// Modifies `src`
|
||||
// 80% of this code is just safe traversal. We need to address that ( ie: use a lib )
|
||||
|
||||
@@ -41,7 +41,7 @@ export function fromJSOrdered (js) {
|
||||
return !isObject(js) ? js :
|
||||
Array.isArray(js) ?
|
||||
Im.Seq(js).map(fromJSOrdered).toList() :
|
||||
Im.Seq(js).map(fromJSOrdered).toOrderedMap()
|
||||
Im.OrderedMap(js).map(fromJSOrdered)
|
||||
}
|
||||
|
||||
export function bindToState(obj, state) {
|
||||
@@ -228,13 +228,13 @@ export function highlight (el) {
|
||||
|
||||
var reset = function(el) {
|
||||
var text = el.textContent,
|
||||
pos = 0, // current position
|
||||
pos = 0, // current position
|
||||
next1 = text[0], // next character
|
||||
chr = 1, // current character
|
||||
prev1, // previous character
|
||||
prev2, // the one before the previous
|
||||
token = // current token content
|
||||
el.innerHTML = "", // (and cleaning the node)
|
||||
chr = 1, // current character
|
||||
prev1, // previous character
|
||||
prev2, // the one before the previous
|
||||
token = // current token content
|
||||
el.innerHTML = "", // (and cleaning the node)
|
||||
|
||||
// current token type:
|
||||
// 0: anything else (whitespaces / newlines)
|
||||
@@ -274,11 +274,11 @@ export function highlight (el) {
|
||||
(tokenType > 8 && chr == "\n") ||
|
||||
[ // finalize conditions for other token types
|
||||
// 0: whitespaces
|
||||
/\S/[test](chr), // merged together
|
||||
/\S/[test](chr), // merged together
|
||||
// 1: operators
|
||||
1, // consist of a single character
|
||||
1, // consist of a single character
|
||||
// 2: braces
|
||||
1, // consist of a single character
|
||||
1, // consist of a single character
|
||||
// 3: (key)word
|
||||
!/[$\w]/[test](chr),
|
||||
// 4: regex
|
||||
@@ -341,12 +341,12 @@ export function highlight (el) {
|
||||
// condition)
|
||||
tokenType = 11
|
||||
while (![
|
||||
1, // 0: whitespace
|
||||
1, // 0: whitespace
|
||||
// 1: operator or braces
|
||||
/[\/{}[(\-+*=<>:;|\\.,?!&@~]/[test](chr), // eslint-disable-line no-useless-escape
|
||||
/[\])]/[test](chr), // 2: closing brace
|
||||
/[$\w]/[test](chr), // 3: (key)word
|
||||
chr == "/" && // 4: regex
|
||||
/[\/{}[(\-+*=<>:;|\\.,?!&@~]/[test](chr), // eslint-disable-line no-useless-escape
|
||||
/[\])]/[test](chr), // 2: closing brace
|
||||
/[$\w]/[test](chr), // 3: (key)word
|
||||
chr == "/" && // 4: regex
|
||||
// previous token was an
|
||||
// opening brace or an
|
||||
// operator (otherwise
|
||||
@@ -355,13 +355,13 @@ export function highlight (el) {
|
||||
// workaround for xml
|
||||
// closing tags
|
||||
prev1 != "<",
|
||||
chr == "\"", // 5: string with "
|
||||
chr == "'", // 6: string with '
|
||||
chr == "\"", // 5: string with "
|
||||
chr == "'", // 6: string with '
|
||||
// 7: xml comment
|
||||
chr+next1+text[pos+1]+text[pos+2] == "<!--",
|
||||
chr+next1 == "/*", // 8: multiline comment
|
||||
chr+next1 == "//", // 9: single-line comment
|
||||
chr == "#" // 10: hash-style comment
|
||||
chr+next1 == "/*", // 8: multiline comment
|
||||
chr+next1 == "//", // 9: single-line comment
|
||||
chr == "#" // 10: hash-style comment
|
||||
][--tokenType]);
|
||||
}
|
||||
|
||||
@@ -451,17 +451,35 @@ export const propChecker = (props, nextProps, objectList=[], ignoreList=[]) => {
|
||||
}
|
||||
|
||||
export const validateNumber = ( val ) => {
|
||||
if ( !/^-?\d+(\.?\d+)?$/.test(val)) {
|
||||
if (!/^-?\d+(\.?\d+)?$/.test(val)) {
|
||||
return "Value must be a number"
|
||||
}
|
||||
}
|
||||
|
||||
export const validateInteger = ( val ) => {
|
||||
if ( !/^-?\d+$/.test(val)) {
|
||||
if (!/^-?\d+$/.test(val)) {
|
||||
return "Value must be an integer"
|
||||
}
|
||||
}
|
||||
|
||||
export const validateFile = ( val ) => {
|
||||
if ( val && !(val instanceof win.File) ) {
|
||||
return "Value must be a file"
|
||||
}
|
||||
}
|
||||
|
||||
export const validateBoolean = ( val ) => {
|
||||
if ( !(val === "true" || val === "false" || val === true || val === false) ) {
|
||||
return "Value must be a boolean"
|
||||
}
|
||||
}
|
||||
|
||||
export const validateString = ( val ) => {
|
||||
if ( val && typeof val !== "string" ) {
|
||||
return "Value must be a string"
|
||||
}
|
||||
}
|
||||
|
||||
// validation of parameters before execute
|
||||
export const validateParam = (param, isXml) => {
|
||||
let errors = []
|
||||
@@ -469,43 +487,69 @@ export const validateParam = (param, isXml) => {
|
||||
let required = param.get("required")
|
||||
let type = param.get("type")
|
||||
|
||||
let stringCheck = type === "string" && !value
|
||||
let arrayCheck = type === "array" && Array.isArray(value) && !value.length
|
||||
let listCheck = type === "array" && Im.List.isList(value) && !value.count()
|
||||
if ( required && (stringCheck || arrayCheck || listCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
/*
|
||||
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
|
||||
then we should do our validation routine.
|
||||
Only bother validating the parameter if the type was specified.
|
||||
*/
|
||||
if ( type && (required || value) ) {
|
||||
// These checks should evaluate to true if the parameter's value is valid
|
||||
let stringCheck = type === "string" && value && !validateString(value)
|
||||
let arrayCheck = type === "array" && Array.isArray(value) && value.length
|
||||
let listCheck = type === "array" && Im.List.isList(value) && value.count()
|
||||
let fileCheck = type === "file" && value instanceof win.File
|
||||
let booleanCheck = type === "boolean" && !validateBoolean(value)
|
||||
let numberCheck = type === "number" && !validateNumber(value) // validateNumber returns undefined if the value is a number
|
||||
let integerCheck = type === "integer" && !validateInteger(value) // validateInteger returns undefined if the value is an integer
|
||||
|
||||
if ( type === "number" ) {
|
||||
let err = validateNumber(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "integer" ) {
|
||||
let err = validateInteger(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "array" ) {
|
||||
let itemType
|
||||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
|
||||
if ( !value.count() ) { return errors }
|
||||
if ( type === "string" ) {
|
||||
let err = validateString(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "boolean" ) {
|
||||
let err = validateBoolean(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "number" ) {
|
||||
let err = validateNumber(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "integer" ) {
|
||||
let err = validateInteger(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
} else if ( type === "array" ) {
|
||||
let itemType
|
||||
|
||||
itemType = param.getIn(["items", "type"])
|
||||
if ( !value.count() ) { return errors }
|
||||
|
||||
value.forEach((item, index) => {
|
||||
let err
|
||||
itemType = param.getIn(["items", "type"])
|
||||
|
||||
if (itemType === "number") {
|
||||
err = validateNumber(item)
|
||||
} else if (itemType === "integer") {
|
||||
err = validateInteger(item)
|
||||
}
|
||||
value.forEach((item, index) => {
|
||||
let err
|
||||
|
||||
if ( err ) {
|
||||
errors.push({ index: index, error: err})
|
||||
}
|
||||
})
|
||||
if (itemType === "number") {
|
||||
err = validateNumber(item)
|
||||
} else if (itemType === "integer") {
|
||||
err = validateInteger(item)
|
||||
} else if (itemType === "string") {
|
||||
err = validateString(item)
|
||||
}
|
||||
|
||||
if ( err ) {
|
||||
errors.push({ index: index, error: err})
|
||||
}
|
||||
})
|
||||
} else if ( type === "file" ) {
|
||||
let err = validateFile(value)
|
||||
if (!err) return errors
|
||||
errors.push(err)
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
@@ -531,7 +575,7 @@ export const getSampleSchema = (schema, contentType="", config={}) => {
|
||||
return JSON.stringify(memoizedSampleFromSchema(schema, config), null, 2)
|
||||
}
|
||||
|
||||
export const parseSeach = () => {
|
||||
export const parseSearch = () => {
|
||||
let map = {}
|
||||
let search = window.location.search
|
||||
|
||||
@@ -563,6 +607,9 @@ export const sorters = {
|
||||
operationsSorter: {
|
||||
alpha: (a, b) => a.get("path").localeCompare(b.get("path")),
|
||||
method: (a, b) => a.get("method").localeCompare(b.get("method"))
|
||||
},
|
||||
tagsSorter: {
|
||||
alpha: (a, b) => a.localeCompare(b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,18 +625,6 @@ export const buildFormData = (data) => {
|
||||
return formArr.join("&")
|
||||
}
|
||||
|
||||
export const filterConfigs = (configs, allowed) => {
|
||||
let i, filteredConfigs = {}
|
||||
|
||||
for (i in configs) {
|
||||
if (allowed.indexOf(i) !== -1) {
|
||||
filteredConfigs[i] = configs[i]
|
||||
}
|
||||
}
|
||||
|
||||
return filteredConfigs
|
||||
}
|
||||
|
||||
// Is this really required as a helper? Perhaps. TODO: expose the system of presets.apis in docs, so we know what is supported
|
||||
export const shallowEqualKeys = (a,b, keys) => {
|
||||
return !!find(keys, (key) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ SwaggerUI({
|
||||
})
|
||||
```
|
||||
|
||||
Or if you're updating the core plugins.. you'll add it to [src/js/bootstrap-plugin](https://github.com/SmartBear/swagger-ux/blob/master/src/js/bootstrap-plugin.js)
|
||||
Or if you're updating the core plugins.. you'll add it to the base preset: [src/core/presets/base.js](https://github.com/swagger-api/swagger-ui/blob/master/src/core/presets/base.js)
|
||||
|
||||
Each Plugin is a function that returns an object. That object will get merged with the `system` and later bound to the state.
|
||||
Here is an example of each `type`
|
||||
|
||||
@@ -6,6 +6,10 @@ import Logo from "./logo_small.png"
|
||||
|
||||
export default class Topbar extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
layoutActions: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = { url: props.specSelectors.url(), selectedIndex: 0 }
|
||||
@@ -80,6 +84,11 @@ export default class Topbar extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
onFilterChange =(e) => {
|
||||
let {target: {value}} = e
|
||||
this.props.layoutActions.updateFilter(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { getComponent, specSelectors, getConfigs } = this.props
|
||||
const Button = getComponent("Button")
|
||||
@@ -121,7 +130,7 @@ export default class Topbar extends React.Component {
|
||||
<div className="wrapper">
|
||||
<div className="topbar-wrapper">
|
||||
<Link href="#" title="Swagger UX">
|
||||
<img height="30" width="30" src={ Logo } alt="Swagger UX"/>
|
||||
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
|
||||
<span>swagger</span>
|
||||
</Link>
|
||||
<form className="download-url-wrapper" onSubmit={formOnSubmit}>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
.swagger-ui {
|
||||
.topbar {
|
||||
background-color: #89bf04;
|
||||
}
|
||||
|
||||
.topbar-wrapper {
|
||||
padding: 0.7em;
|
||||
}
|
||||
|
||||
.topbar-logo__img {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.topbar-logo__title {
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
margin: 0.15em 0 0 0.5em;
|
||||
}
|
||||
|
||||
.download-url-wrapper {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.topbar .download-url__text {
|
||||
width: 28em;
|
||||
height: 2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.download-url__btn {
|
||||
background-color: #547f00;
|
||||
border-color: #547f00;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 0.2em 0.3em;
|
||||
color: white;
|
||||
border-radius: 0.1em;
|
||||
|
||||
&:hover {
|
||||
&:extend(.download-url__btn);
|
||||
}
|
||||
}
|
||||
|
||||
.center-700 {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 45em;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import StandaloneLayout from "./layout"
|
||||
import "../style/main.scss"
|
||||
|
||||
import TopbarPlugin from "plugins/topbar"
|
||||
import ConfigsPlugin from "plugins/configs"
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class StandaloneLayout extends React.Component {
|
||||
return (
|
||||
|
||||
<Container className='swagger-ui'>
|
||||
{ Topbar ? <Topbar/> : null }
|
||||
{ Topbar ? <Topbar /> : null }
|
||||
{ loadingStatus === "loading" &&
|
||||
<div className="info">
|
||||
<h4 className="title">Loading...</h4>
|
||||
@@ -45,7 +45,7 @@ export default class StandaloneLayout extends React.Component {
|
||||
<h4 className="title">Failed to load config.</h4>
|
||||
</div>
|
||||
}
|
||||
{ !loadingStatus || loadingStatus === "success" && <BaseLayout/> }
|
||||
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> }
|
||||
<Row>
|
||||
<Col>
|
||||
<OnlineValidatorBadge />
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
|
||||
@include text_headline();
|
||||
|
||||
&.btn-sm
|
||||
{
|
||||
font-size: 12px;
|
||||
padding: 4px 23px;
|
||||
}
|
||||
|
||||
&[disabled]
|
||||
{
|
||||
cursor: not-allowed;
|
||||
@@ -165,6 +171,10 @@
|
||||
button
|
||||
{
|
||||
cursor: pointer;
|
||||
|
||||
outline: none;
|
||||
|
||||
&.invalid
|
||||
{
|
||||
@include invalidFormElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ select
|
||||
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
@include invalidFormElement();
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-body select
|
||||
@@ -42,7 +46,8 @@ label
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=search],
|
||||
input[type=email]
|
||||
input[type=email],
|
||||
input[type=file]
|
||||
{
|
||||
min-width: 100px;
|
||||
margin: 5px 0;
|
||||
@@ -54,10 +59,7 @@ input[type=email]
|
||||
|
||||
&.invalid
|
||||
{
|
||||
animation: shake .4s 1;
|
||||
|
||||
border-color: $_color-delete;
|
||||
background: lighten($_color-delete, 35%);
|
||||
@include invalidFormElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ body
|
||||
.opblock-tag
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 10px 20px 10px 10px;
|
||||
|
||||
@@ -53,8 +54,6 @@ body
|
||||
|
||||
border-bottom: 1px solid rgba(#3b4151, .3);
|
||||
|
||||
align-items: center;
|
||||
|
||||
&:hover
|
||||
{
|
||||
background: rgba(#000,.02);
|
||||
@@ -75,6 +74,11 @@ body
|
||||
{
|
||||
border-color: $color;
|
||||
}
|
||||
|
||||
.tab-header .tab-item.active h4 span:after
|
||||
{
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,14 +110,15 @@ body
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
|
||||
flex: 1;
|
||||
|
||||
padding: 0 10px;
|
||||
|
||||
flex: 1;
|
||||
@include text_body();
|
||||
}
|
||||
}
|
||||
|
||||
.parаmeter__type
|
||||
.parameter__type
|
||||
{
|
||||
font-size: 12px;
|
||||
|
||||
@@ -134,6 +139,8 @@ body
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.opblock
|
||||
{
|
||||
margin: 0 0 15px 0;
|
||||
@@ -142,6 +149,51 @@ body
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 3px rgba(#000,.19);
|
||||
|
||||
.tab-header
|
||||
{
|
||||
display: flex;
|
||||
|
||||
flex: 1;
|
||||
|
||||
.tab-item
|
||||
{
|
||||
padding: 0 40px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:first-of-type
|
||||
{
|
||||
padding: 0 40px 0 0;
|
||||
}
|
||||
&.active
|
||||
{
|
||||
h4
|
||||
{
|
||||
span
|
||||
{
|
||||
position: relative;
|
||||
|
||||
|
||||
&:after
|
||||
{
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
left: 50%;
|
||||
|
||||
width: 120%;
|
||||
height: 4px;
|
||||
|
||||
content: '';
|
||||
transform: translateX(-50%);
|
||||
|
||||
background: #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.is-open
|
||||
{
|
||||
@@ -154,24 +206,26 @@ body
|
||||
.opblock-section-header
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 8px 20px;
|
||||
|
||||
min-height: 50px;
|
||||
|
||||
background: rgba(#fff,.8);
|
||||
box-shadow: 0 1px 2px rgba(#000,.1);
|
||||
|
||||
align-items: center;
|
||||
|
||||
label
|
||||
{
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
|
||||
align-items: center;
|
||||
@include text_headline();
|
||||
|
||||
span
|
||||
@@ -184,9 +238,10 @@ body
|
||||
{
|
||||
font-size: 14px;
|
||||
|
||||
flex: 1;
|
||||
|
||||
margin: 0;
|
||||
|
||||
flex: 1;
|
||||
@include text_headline();
|
||||
}
|
||||
}
|
||||
@@ -215,11 +270,11 @@ body
|
||||
font-size: 16px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 0 10px;
|
||||
|
||||
@include text_code();
|
||||
align-items: center;
|
||||
|
||||
.view-line-link
|
||||
{
|
||||
@@ -258,18 +313,18 @@ body
|
||||
font-size: 13px;
|
||||
|
||||
flex: 1;
|
||||
|
||||
@include text_body();
|
||||
}
|
||||
|
||||
.opblock-summary
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 5px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.opblock-post
|
||||
@@ -316,12 +371,24 @@ body
|
||||
|
||||
.opblock-schemes
|
||||
{
|
||||
padding: 8px 20px;
|
||||
padding: 8px 20px;
|
||||
|
||||
.schemes-title
|
||||
{
|
||||
padding: 0 10px 0 0;
|
||||
}
|
||||
.schemes-title
|
||||
{
|
||||
padding: 0 10px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter
|
||||
{
|
||||
.operation-filter-input
|
||||
{
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
padding: 10px 10px;
|
||||
|
||||
border: 2px solid #d8dde7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +443,7 @@ body
|
||||
}
|
||||
|
||||
.opblock-description-wrapper,
|
||||
.opblock-external-docs-wrapper,
|
||||
.opblock-title_normal
|
||||
{
|
||||
font-size: 12px;
|
||||
@@ -404,6 +472,12 @@ body
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-external-docs-wrapper {
|
||||
h4 {
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.execute-wrapper
|
||||
{
|
||||
padding: 20px;
|
||||
@@ -487,6 +561,15 @@ body
|
||||
{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
@include text_code(#89bf04);
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
color: #81b10c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,13 +581,11 @@ body
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
|
||||
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
white-space: pre-wrap;
|
||||
|
||||
|
||||
border-radius: 4px;
|
||||
background: #41444e;
|
||||
@@ -533,10 +614,9 @@ body
|
||||
.schemes
|
||||
{
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
> label
|
||||
> label
|
||||
{
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
@@ -616,6 +696,18 @@ body
|
||||
}
|
||||
}
|
||||
|
||||
.renderedMarkdown {
|
||||
p {
|
||||
@include text_body();
|
||||
font-size: 14px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.response-content-type {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
@keyframes blinker
|
||||
{
|
||||
@@ -624,3 +716,25 @@ body
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
section
|
||||
{
|
||||
h3
|
||||
{
|
||||
@include text_headline();
|
||||
}
|
||||
}
|
||||
|
||||
a.nostyle {
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
cursor: auto;
|
||||
display: inline;
|
||||
|
||||
&:visited {
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,3 +166,9 @@ $browser-context: 16;
|
||||
@warn 'Breakpoint mixin supports: tablet, mobile, desktop';
|
||||
}
|
||||
}
|
||||
|
||||
@mixin invalidFormElement() {
|
||||
animation: shake .4s 1;
|
||||
border-color: $_color-delete;
|
||||
background: lighten($_color-delete, 35%);
|
||||
}
|
||||
@@ -3,6 +3,13 @@
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
|
||||
.deprecated
|
||||
{
|
||||
span, td {
|
||||
color: #aaa !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include text_code();
|
||||
&-toggle
|
||||
{
|
||||
@@ -79,6 +86,10 @@
|
||||
border-radius: 4px;
|
||||
background: rgba(#000,.7);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +199,11 @@ section.models
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
&.deprecated
|
||||
{
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +214,14 @@ section.models
|
||||
@include text_headline(#555);
|
||||
}
|
||||
|
||||
.model-deprecated-warning
|
||||
{
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-right: 1em;
|
||||
@include text_headline($_color-delete);
|
||||
}
|
||||
|
||||
|
||||
span
|
||||
{
|
||||
|
||||
3
src/style/_split-pane-mode.scss
Normal file
3
src/style/_split-pane-mode.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.Resizer.vertical.disabled {
|
||||
display: none;
|
||||
}
|
||||
@@ -97,6 +97,10 @@ table
|
||||
width: 100%;
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
select {
|
||||
border-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.parameter__name
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
margin: 0;
|
||||
|
||||
border: 2px solid #547f00;
|
||||
border-radius: 4px 0 0 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
@import 'information';
|
||||
@import 'authorize';
|
||||
@import 'errors';
|
||||
@import 'split-pane-mode';
|
||||
}
|
||||
|
||||
@@ -10,4 +10,8 @@ try {
|
||||
// for more information.
|
||||
}
|
||||
|
||||
// `absolutePath` and `getAbsoluteFSPath` are both here because at one point,
|
||||
// we documented having one and actually implemented the other.
|
||||
// They were both retained so we don't break anyone's code.
|
||||
module.exports.absolutePath = require("./absolute-path.js")
|
||||
module.exports.getAbsoluteFSPath = require("./absolute-path.js")
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
env:
|
||||
mocha: true
|
||||
rules:
|
||||
"react/prop-types": 1 # bah humbug
|
||||
"no-unused-vars": 1 # unused vars in tests can be useful for indicating a full signature
|
||||
|
||||
41
test/components/schemes.js
Normal file
41
test/components/schemes.js
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect, { createSpy } from "expect"
|
||||
import { shallow } from "enzyme"
|
||||
import { fromJS } from "immutable"
|
||||
import Schemes from "components/schemes"
|
||||
|
||||
describe("<Schemes/>", function(){
|
||||
it("calls props.specActions.setScheme() when no operationScheme is selected", function(){
|
||||
|
||||
// Given
|
||||
let props = {
|
||||
specActions: {
|
||||
setScheme: createSpy()
|
||||
},
|
||||
schemes: fromJS([
|
||||
"http",
|
||||
"https"
|
||||
]),
|
||||
operationScheme: undefined,
|
||||
path: "/test",
|
||||
method: "get"
|
||||
}
|
||||
|
||||
// When
|
||||
let wrapper = shallow(<Schemes {...props}/>)
|
||||
|
||||
// Then operationScheme should default to first scheme in options list
|
||||
expect(props.specActions.setScheme).toHaveBeenCalledWith("http", "/test" , "get")
|
||||
|
||||
// When the operationScheme is no longer in the list of options
|
||||
props.schemes = fromJS([
|
||||
"https"
|
||||
])
|
||||
wrapper.setProps(props)
|
||||
|
||||
// Then operationScheme should default to first scheme in options list
|
||||
expect(props.specActions.setScheme).toHaveBeenCalledWith("https", "/test", "get")
|
||||
})
|
||||
})
|
||||
@@ -1,183 +0,0 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import { transformPathToArray } from "core/path-translator"
|
||||
|
||||
describe("validation plugin - path translator", function(){
|
||||
|
||||
describe("string paths", function(){
|
||||
|
||||
it("should translate a simple string path to an array", function(){
|
||||
// Given
|
||||
let jsSpec = {
|
||||
one: {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
two: 2
|
||||
}
|
||||
|
||||
let path = "instance.one.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["one", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should translate an ambiguous string path to an array", function(){
|
||||
// Since JSONSchema uses periods to mark different properties,
|
||||
// a key with a period in it is ambiguous, because it can mean at least two things.
|
||||
// In our case, the path can mean:
|
||||
// ["google", "com", "a"] or ["google.com", "a"]
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should translate an doubly ambiguous string path to an array", function(){
|
||||
// Since JSONSchema uses periods to mark different properties,
|
||||
// a key with two periods in it (like "www.google.com") is doubly ambiguous,
|
||||
// because it can mean at least three things.
|
||||
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"www.google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.www.google.com.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["www.google.com", "a"])
|
||||
|
||||
})
|
||||
|
||||
it("should return null for an invalid path", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: "a thing",
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.net.a"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(null)
|
||||
|
||||
})
|
||||
|
||||
it("should return inline array indices in their own value", function(){
|
||||
// "a[1]" => ["a", "1"]
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
"here is the target"
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1]"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1"])
|
||||
|
||||
})
|
||||
|
||||
it("should return the correct path when the last part is ambiguous", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
{
|
||||
"gmail.com": 1234
|
||||
}
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1].gmail.com"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1", "gmail.com"])
|
||||
|
||||
})
|
||||
|
||||
it("should return the correct path when the last part is doubly ambiguous", function(){
|
||||
|
||||
// Given
|
||||
let jsSpec = {
|
||||
"google.com": {
|
||||
a: [
|
||||
"hello",
|
||||
{
|
||||
"www.gmail.com": 1234
|
||||
}
|
||||
],
|
||||
b: "another thing",
|
||||
c: "one more thing"
|
||||
},
|
||||
"gmail.com": {
|
||||
d: "more stuff",
|
||||
e: "even more stuff"
|
||||
}
|
||||
}
|
||||
|
||||
let path = "instance.google.com.a[1].www.gmail.com"
|
||||
|
||||
// Then
|
||||
expect(transformPathToArray(path, jsSpec)).toEqual(["google.com", "a", "1", "www.gmail.com"])
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@@ -52,7 +52,6 @@ describe("spec plugin - selectors", function(){
|
||||
})
|
||||
|
||||
describe("contentTypeValues", function(){
|
||||
|
||||
it("should return { requestContentType, responseContentType } from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
@@ -77,6 +76,73 @@ describe("spec plugin - selectors", function(){
|
||||
})
|
||||
})
|
||||
|
||||
it("should prioritize consumes value first from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"consumes_value": "one",
|
||||
"parameters": [{
|
||||
"type": "file"
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS().requestContentType).toEqual("one")
|
||||
})
|
||||
|
||||
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: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"parameters": [{
|
||||
"type": "file"
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS().requestContentType).toEqual("multipart/form-data")
|
||||
})
|
||||
|
||||
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: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"parameters": [{
|
||||
"type": "formData"
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
|
||||
})
|
||||
|
||||
it("should be ok, if no operation found", function(){
|
||||
// Given
|
||||
let state = fromJS({ })
|
||||
|
||||
@@ -326,6 +326,122 @@ describe("bound system", function(){
|
||||
|
||||
})
|
||||
|
||||
describe("wrapSelectors", () => {
|
||||
it("should wrap a selector and provide a reference to the original", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: [
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
selectors: {
|
||||
wow: () => (system) => {
|
||||
return "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
wrapSelectors: {
|
||||
wow: (ori) => (system) => {
|
||||
// Then
|
||||
return ori() + " wrapper"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var res = system.getSystem().dogeSelectors.wow(1)
|
||||
expect(res).toEqual("original wrapper")
|
||||
|
||||
})
|
||||
|
||||
it("should provide a live reference to the system to a wrapper", function(done){
|
||||
|
||||
// Given
|
||||
const mySystem = new System({
|
||||
plugins: [
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
selectors: {
|
||||
wow: () => (system) => {
|
||||
return "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
wrapSelectors: {
|
||||
wow: (ori, system) => () => {
|
||||
// Then
|
||||
expect(mySystem.getSystem()).toEqual(system.getSystem())
|
||||
done()
|
||||
return ori() + " wrapper"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
mySystem.getSystem().dogeSelectors.wow(1)
|
||||
})
|
||||
|
||||
it("should provide the state as the first argument to the inner function", function(done){
|
||||
|
||||
// Given
|
||||
const mySystem = new System({
|
||||
state: {
|
||||
doge: {
|
||||
abc: "123"
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
selectors: {
|
||||
wow: () => (system) => {
|
||||
return "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
statePlugins: {
|
||||
doge: {
|
||||
wrapSelectors: {
|
||||
wow: (ori, system) => (dogeState) => {
|
||||
// Then
|
||||
expect(dogeState.toJS().abc).toEqual("123")
|
||||
done()
|
||||
return ori() + " wrapper"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
mySystem.getSystem().dogeSelectors.wow(1)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
139
test/core/system/wrapComponent.js
Normal file
139
test/core/system/wrapComponent.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import React from "react"
|
||||
import expect from "expect"
|
||||
import { render } from "enzyme"
|
||||
import System from "core/system"
|
||||
|
||||
describe("wrapComponents", () => {
|
||||
describe("should wrap a component and provide a reference to the original", () => {
|
||||
it("with stateless components", function(){
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: [
|
||||
{
|
||||
components: {
|
||||
wow: ({ name }) => <div>{name} component</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
wrapComponents: {
|
||||
wow: (OriginalComponent) => (props) => {
|
||||
return <container>
|
||||
<OriginalComponent {...props}></OriginalComponent>
|
||||
<OriginalComponent name="Wrapped"></OriginalComponent>
|
||||
</container>
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponents("wow")
|
||||
const wrapper = render(<Component name="Normal" />)
|
||||
|
||||
const container = wrapper.children().first()
|
||||
expect(container[0].name).toEqual("container")
|
||||
|
||||
const children = container.children()
|
||||
expect(children.length).toEqual(2)
|
||||
expect(children.eq(0).text()).toEqual("Normal component")
|
||||
expect(children.eq(1).text()).toEqual("Wrapped component")
|
||||
})
|
||||
|
||||
it("with React classes", function(){
|
||||
class MyComponent extends React.Component {
|
||||
render() {
|
||||
return <div>{this.props.name} component</div>
|
||||
}
|
||||
}
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: [
|
||||
{
|
||||
components: {
|
||||
wow: MyComponent
|
||||
}
|
||||
},
|
||||
{
|
||||
wrapComponents: {
|
||||
wow: (OriginalComponent) => {
|
||||
return class WrapperComponent extends React.Component {
|
||||
render() {
|
||||
return <container>
|
||||
<OriginalComponent {...this.props}></OriginalComponent>
|
||||
<OriginalComponent name="Wrapped"></OriginalComponent>
|
||||
</container>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponents("wow")
|
||||
const wrapper = render(<Component name="Normal" />)
|
||||
|
||||
const container = wrapper.children().first()
|
||||
expect(container[0].name).toEqual("container")
|
||||
|
||||
const children = container.children()
|
||||
expect(children.length).toEqual(2)
|
||||
expect(children.eq(0).text()).toEqual("Normal component")
|
||||
expect(children.eq(1).text()).toEqual("Wrapped component")
|
||||
})
|
||||
})
|
||||
|
||||
it("should provide a reference to the system to the wrapper", function(){
|
||||
|
||||
// Given
|
||||
|
||||
const mySystem = new System({
|
||||
plugins: [
|
||||
{
|
||||
// Make a selector
|
||||
statePlugins: {
|
||||
doge: {
|
||||
selectors: {
|
||||
wow: () => () => {
|
||||
return "WOW much data"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// Create a component
|
||||
components: {
|
||||
wow: () => <div>Original component</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
// Wrap the component and use the system
|
||||
wrapComponents: {
|
||||
wow: (OriginalComponent, system) => (props) => {
|
||||
return <container>
|
||||
<OriginalComponent {...props}></OriginalComponent>
|
||||
<div>{system.dogeSelectors.wow()}</div>
|
||||
</container>
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// Then
|
||||
var Component = mySystem.getSystem().getComponents("wow")
|
||||
const wrapper = render(<Component name="Normal" />)
|
||||
|
||||
const container = wrapper.children().first()
|
||||
expect(container[0].name).toEqual("container")
|
||||
|
||||
const children = container.children()
|
||||
expect(children.length).toEqual(2)
|
||||
expect(children.eq(0).text()).toEqual("Original component")
|
||||
expect(children.eq(1).text()).toEqual("WOW much data")
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,10 @@
|
||||
/* eslint-env mocha */
|
||||
import expect from "expect"
|
||||
import { fromJS } from "immutable"
|
||||
import { mapToList, validateNumber, validateInteger, validateParam } from "core/utils"
|
||||
import { mapToList, validateNumber, validateInteger, validateParam, validateFile, fromJSOrdered } from "core/utils"
|
||||
import win from "core/window"
|
||||
|
||||
describe("utils", function(){
|
||||
describe("utils", function() {
|
||||
|
||||
describe("mapToList", function(){
|
||||
|
||||
@@ -157,11 +158,36 @@ describe("utils", function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateFile", function() {
|
||||
let errorMessage = "Value must be a file"
|
||||
|
||||
it("validates against objects which are instances of 'File'", function() {
|
||||
let fileObj = new win.File([], "Test File")
|
||||
expect(validateFile(fileObj)).toBeFalsy()
|
||||
expect(validateFile(null)).toBeFalsy()
|
||||
expect(validateFile(undefined)).toBeFalsy()
|
||||
expect(validateFile(1)).toEqual(errorMessage)
|
||||
expect(validateFile("string")).toEqual(errorMessage)
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateParam", function() {
|
||||
let param = null
|
||||
let result = null
|
||||
|
||||
it("skips validation when `type` is not specified", function() {
|
||||
// invalid type
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: undefined,
|
||||
value: ""
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required strings", function() {
|
||||
// invalid string
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "string",
|
||||
@@ -169,9 +195,88 @@ describe("utils", function(){
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// valid string
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "string",
|
||||
value: "test string"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates optional strings", function() {
|
||||
// valid (empty) string
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "string",
|
||||
value: ""
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid string
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "string",
|
||||
value: "test"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required files", function() {
|
||||
// invalid file
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "file",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// valid file
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "file",
|
||||
value: new win.File()
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates optional files", function() {
|
||||
// invalid file
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "file",
|
||||
value: "not a file"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be a file"] )
|
||||
|
||||
// valid (empty) file
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "file",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid file
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "file",
|
||||
value: new win.File()
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required arrays", function() {
|
||||
// invalid (empty) array
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "array",
|
||||
@@ -180,37 +285,51 @@ describe("utils", function(){
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// invalid (not an array)
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "array",
|
||||
value: []
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
})
|
||||
|
||||
it("validates numbers", function() {
|
||||
// invalid array, items do not match correct type
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "number",
|
||||
value: "test"
|
||||
required: true,
|
||||
type: "array",
|
||||
value: [1],
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be a number"] )
|
||||
})
|
||||
expect( result ).toEqual( [{index: 0, error: "Value must be a string"}] )
|
||||
|
||||
it("validates integers", function() {
|
||||
// valid array, with no 'type' for items
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "integer",
|
||||
value: "test"
|
||||
required: true,
|
||||
type: "array",
|
||||
value: ["1"]
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be an integer"] )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid array, items match type
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "array",
|
||||
value: ["1"],
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates arrays", function() {
|
||||
// empty array
|
||||
it("validates optional arrays", function() {
|
||||
// valid, empty array
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "array",
|
||||
@@ -219,7 +338,7 @@ describe("utils", function(){
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// numbers
|
||||
// invalid, items do not match correct type
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "array",
|
||||
@@ -231,17 +350,236 @@ describe("utils", function(){
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] )
|
||||
|
||||
// integers
|
||||
// valid
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "array",
|
||||
value: ["not", "numbers"],
|
||||
value: ["test"],
|
||||
items: {
|
||||
type: "integer"
|
||||
type: "string"
|
||||
}
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [{index: 0, error: "Value must be an integer"}, {index: 1, error: "Value must be an integer"}] )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required booleans", function() {
|
||||
// invalid boolean value
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "boolean",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// invalid boolean value (not a boolean)
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "boolean",
|
||||
value: "test string"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// valid boolean value
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "boolean",
|
||||
value: "true"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid boolean value
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "boolean",
|
||||
value: false
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates optional booleans", function() {
|
||||
// valid (empty) boolean value
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "boolean",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// invalid boolean value (not a boolean)
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "boolean",
|
||||
value: "test string"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be a boolean"] )
|
||||
|
||||
// valid boolean value
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "boolean",
|
||||
value: "true"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid boolean value
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "boolean",
|
||||
value: false
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required numbers", function() {
|
||||
// invalid number, string instead of a number
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "number",
|
||||
value: "test"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// invalid number, undefined value
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "number",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// valid number
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "number",
|
||||
value: 10
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates optional numbers", function() {
|
||||
// invalid number, string instead of a number
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "number",
|
||||
value: "test"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be a number"] )
|
||||
|
||||
// valid (empty) number
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "number",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// valid number
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "number",
|
||||
value: 10
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates required integers", function() {
|
||||
// invalid integer, string instead of an integer
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "integer",
|
||||
value: "test"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// invalid integer, undefined value
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "integer",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Required field is not provided"] )
|
||||
|
||||
// valid integer
|
||||
param = fromJS({
|
||||
required: true,
|
||||
type: "integer",
|
||||
value: 10
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
|
||||
it("validates optional integers", function() {
|
||||
// invalid integer, string instead of an integer
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "integer",
|
||||
value: "test"
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( ["Value must be an integer"] )
|
||||
|
||||
// valid (empty) integer
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "integer",
|
||||
value: undefined
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
|
||||
// integers
|
||||
param = fromJS({
|
||||
required: false,
|
||||
type: "integer",
|
||||
value: 10
|
||||
})
|
||||
result = validateParam( param, false )
|
||||
expect( result ).toEqual( [] )
|
||||
})
|
||||
})
|
||||
|
||||
describe("fromJSOrdered", () => {
|
||||
it("should create an OrderedMap from an object", () => {
|
||||
const param = {
|
||||
value: "test"
|
||||
}
|
||||
|
||||
const result = fromJSOrdered(param).toJS()
|
||||
expect( result ).toEqual( { value: "test" } )
|
||||
})
|
||||
|
||||
it("should not use an object's length property for Map size", () => {
|
||||
const param = {
|
||||
length: 5
|
||||
}
|
||||
|
||||
const result = fromJSOrdered(param).toJS()
|
||||
expect( result ).toEqual( { length: 5 } )
|
||||
})
|
||||
|
||||
it("should create an OrderedMap from an array", () => {
|
||||
const param = [1, 1, 2, 3, 5, 8]
|
||||
|
||||
const result = fromJSOrdered(param).toJS()
|
||||
expect( result ).toEqual( [1, 1, 2, 3, 5, 8] )
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,64 +1,32 @@
|
||||
var path = require('path')
|
||||
var rules = [
|
||||
const path = require("path")
|
||||
const styleRules = require("./webpack.dist-style.config.js")
|
||||
|
||||
let rules = [
|
||||
{ test: /\.(worker\.js)(\?.*)?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'worker-loader',
|
||||
loader: "worker-loader",
|
||||
options: {
|
||||
inline: true,
|
||||
name: '[name].js'
|
||||
name: "[name].js"
|
||||
}
|
||||
},
|
||||
{ loader: 'babel-loader' }
|
||||
]
|
||||
},
|
||||
{ test: /\.(css)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
'postcss-loader'
|
||||
]
|
||||
},
|
||||
{ test: /\.(scss)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: { sourceMap: true }
|
||||
},
|
||||
{ loader: 'sass-loader',
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
sourceMap: true,
|
||||
sourceMapContents: 'true'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ test: /\.(less)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
},
|
||||
'less-loader'
|
||||
{ loader: "babel-loader" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = require('./make-webpack-config.js')(rules, {
|
||||
module.exports = require("./make-webpack-config.js")(rules, {
|
||||
_special: {
|
||||
separateStylesheets: false,
|
||||
separateStylesheets: true,
|
||||
minimize: true,
|
||||
sourcemaps: true,
|
||||
},
|
||||
|
||||
entry: {
|
||||
'swagger-ui-bundle': [
|
||||
'./src/polyfills',
|
||||
'./src/core/index.js'
|
||||
"swagger-ui-bundle": [
|
||||
"./src/polyfills",
|
||||
"./src/core/index.js"
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@@ -1,65 +1,32 @@
|
||||
var path = require('path')
|
||||
const path = require("path")
|
||||
const styleRules = require("./webpack.dist-style.config.js")
|
||||
|
||||
var rules = [
|
||||
let rules = [
|
||||
{ test: /\.(worker\.js)(\?.*)?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'worker-loader',
|
||||
loader: "worker-loader",
|
||||
options: {
|
||||
inline: true,
|
||||
name: '[name].js'
|
||||
name: "[name].js"
|
||||
}
|
||||
},
|
||||
{ loader: 'babel-loader' }
|
||||
]
|
||||
},
|
||||
{ test: /\.(css)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
'postcss-loader'
|
||||
]
|
||||
},
|
||||
{ test: /\.(scss)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: { sourceMap: true }
|
||||
},
|
||||
{ loader: 'sass-loader',
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
sourceMap: true,
|
||||
sourceMapContents: 'true'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ test: /\.(less)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
},
|
||||
'less-loader'
|
||||
{ loader: "babel-loader" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = require('./make-webpack-config.js')(rules, {
|
||||
module.exports = require("./make-webpack-config.js")(rules, {
|
||||
_special: {
|
||||
separateStylesheets: false,
|
||||
separateStylesheets: true,
|
||||
minimize: true,
|
||||
sourcemaps: true,
|
||||
},
|
||||
|
||||
entry: {
|
||||
'swagger-ui-standalone-preset': [
|
||||
'./src/polyfills',
|
||||
'./src/standalone/index.js'
|
||||
"swagger-ui-standalone-preset": [
|
||||
"./src/polyfills",
|
||||
"./src/standalone/index.js"
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@@ -1,66 +1,25 @@
|
||||
var path = require('path')
|
||||
var fs = require('fs')
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const nodeModules = fs.readdirSync("node_modules").filter(function(x) { return x !== ".bin" })
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const styleRules = require("./webpack.dist-style.config.js")
|
||||
|
||||
var rules = [
|
||||
let rules = [
|
||||
{ test: /\.(worker\.js)(\?.*)?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'worker-loader',
|
||||
loader: "worker-loader",
|
||||
options: {
|
||||
inline: true,
|
||||
name: '[name].js'
|
||||
name: "[name].js"
|
||||
}
|
||||
},
|
||||
{ loader: 'babel-loader' }
|
||||
{ loader: "babel-loader" }
|
||||
]
|
||||
},
|
||||
{ test: /\.(css)(\?.*)?$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: [
|
||||
'css-loader',
|
||||
'postcss-loader'
|
||||
]
|
||||
})
|
||||
},
|
||||
{ test: /\.(scss)(\?.*)?$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: [
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: { minimize: true }
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: { sourceMap: true }
|
||||
},
|
||||
{ loader: 'sass-loader',
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
sourceMap: true,
|
||||
sourceMapContents: 'true'
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
{ test: /\.(less)(\?.*)?$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: ['css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
},
|
||||
'less-loader'
|
||||
]
|
||||
})
|
||||
}
|
||||
]
|
||||
rules = rules.concat(styleRules)
|
||||
|
||||
module.exports = require('./make-webpack-config.js')(rules, {
|
||||
module.exports = require("./make-webpack-config.js")(rules, {
|
||||
_special: {
|
||||
separateStylesheets: true,
|
||||
minimize: true,
|
||||
|
||||
@@ -1,56 +1,46 @@
|
||||
var path = require('path')
|
||||
const path = require("path")
|
||||
|
||||
var rules = [
|
||||
const rules = [
|
||||
{ test: /\.(worker\.js)(\?.*)?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'worker-loader',
|
||||
loader: "worker-loader",
|
||||
options: {
|
||||
inline: true
|
||||
}
|
||||
},
|
||||
{ loader: 'babel-loader' }
|
||||
{ loader: "babel-loader" }
|
||||
]
|
||||
},
|
||||
{ test: /\.(jsx)(\?.*)?$/,
|
||||
use: [
|
||||
{ loader: 'react-hot-loader' },
|
||||
{ loader: 'babel-loader' }
|
||||
{ loader: "react-hot-loader" },
|
||||
{ loader: "babel-loader" }
|
||||
]
|
||||
},
|
||||
{ test: /\.(css)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
'postcss-loader'
|
||||
"style-loader",
|
||||
"css-loader",
|
||||
"postcss-loader"
|
||||
]
|
||||
},
|
||||
{ test: /\.(scss)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
"style-loader",
|
||||
"css-loader",
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
loader: "postcss-loader",
|
||||
options: { sourceMap: true }
|
||||
},
|
||||
{ loader: 'sass-loader',
|
||||
{ loader: "sass-loader",
|
||||
options: {
|
||||
outputStyle: 'expanded',
|
||||
outputStyle: "expanded",
|
||||
sourceMap: true,
|
||||
sourceMapContents: 'true'
|
||||
sourceMapContents: "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ test: /\.(less)(\?.*)?$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
},
|
||||
'less-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -60,25 +50,26 @@ module.exports = require("./make-webpack-config")(rules, {
|
||||
},
|
||||
devtool: "eval",
|
||||
entry: {
|
||||
'swagger-ui-bundle': [
|
||||
'./src/polyfills',
|
||||
'./src/core/index.js'
|
||||
"swagger-ui-bundle": [
|
||||
"./src/polyfills",
|
||||
"./src/core/index.js"
|
||||
],
|
||||
'swagger-ui-standalone-preset': [
|
||||
'./src/polyfills',
|
||||
'./src/standalone/index.js',
|
||||
"swagger-ui-standalone-preset": [
|
||||
"./src/style/main.scss",
|
||||
"./src/polyfills",
|
||||
"./src/standalone/index.js",
|
||||
]
|
||||
},
|
||||
output: {
|
||||
pathinfo: true,
|
||||
filename: '[name].js',
|
||||
filename: "[name].js",
|
||||
library: "[name]",
|
||||
libraryTarget: "umd",
|
||||
chunkFilename: "[id].js"
|
||||
},
|
||||
devServer: {
|
||||
port: 3200,
|
||||
contentBase: path.join(__dirname, 'dev-helpers'),
|
||||
contentBase: path.join(__dirname, "dev-helpers"),
|
||||
publicPath: "/",
|
||||
noInfo: true,
|
||||
hot: true,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
var config = require("./webpack-dist.config.js")
|
||||
const config = require("./webpack-dist.config.js")
|
||||
|
||||
module.exports = config
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const webpack = require('webpack')
|
||||
const path = require('path')
|
||||
const deepMerge = require('deepmerge')
|
||||
const webpackConfig = require('./webpack-dist-bundle.config.js')
|
||||
const DEPS_CHECK_DIR = require('./package.json').config.deps_check_dir
|
||||
const path = require("path")
|
||||
const deepMerge = require("deepmerge")
|
||||
const webpackConfig = require("./webpack-dist-bundle.config.js")
|
||||
const DEPS_CHECK_DIR = require("./package.json").config.deps_check_dir
|
||||
|
||||
module.exports = deepMerge(
|
||||
webpackConfig, {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = require("./make-webpack-config")({
|
||||
|
||||
});
|
||||
})
|
||||
34
webpack.dist-style.config.js
Normal file
34
webpack.dist-style.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin")
|
||||
|
||||
module.exports = [{
|
||||
test: /\.(css)(\?.*)?$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: "style-loader",
|
||||
use: [
|
||||
"css-loader",
|
||||
"postcss-loader"
|
||||
]
|
||||
})
|
||||
},
|
||||
{ test: /\.(scss)(\?.*)?$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: "style-loader",
|
||||
use: [
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: { minimize: true }
|
||||
},
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
options: { sourceMap: true }
|
||||
},
|
||||
{ loader: "sass-loader",
|
||||
options: {
|
||||
outputStyle: "expanded",
|
||||
sourceMap: true,
|
||||
sourceMapContents: "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}]
|
||||
Reference in New Issue
Block a user