From 63b2a9751c13da166f29096a124b2febcf679cf8 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sat, 8 Jul 2017 08:29:34 -0600 Subject: [PATCH 01/17] Fixes #3329 - Tweak webpack config to share CSS loaders for production builders. Updated production builds to use `ExtractTextPlugin` so styles are not built into JS. --- make-webpack-config.js | 62 +++++++++++++++---------------- webpack-dist-bundle.config.js | 57 +++++++--------------------- webpack-dist-standalone.config.js | 56 ++++++---------------------- webpack-dist.config.js | 59 +++++------------------------ webpack-hot-dev-server.config.js | 54 +++++++++++++-------------- webpack-watch.config.js | 2 +- webpack.check.js | 9 ++--- webpack.config.js | 2 +- webpack.dist-style.config.js | 45 ++++++++++++++++++++++ 9 files changed, 142 insertions(+), 204 deletions(-) create mode 100644 webpack.dist-style.config.js diff --git a/make-webpack-config.js b/make-webpack-config.js index f2628bc0..3ed81a0f 100644 --- a/make-webpack-config.js +++ b/make-webpack-config.js @@ -1,11 +1,11 @@ -var path = require('path') +var path = require("path") -var webpack = require('webpack') -var ExtractTextPlugin = require('extract-text-webpack-plugin') -var deepExtend = require('deep-extend') -const {gitDescribeSync} = require('git-describe'); +var webpack = require("webpack") +var ExtractTextPlugin = require("extract-text-webpack-plugin") +var deepExtend = require("deep-extend") +const {gitDescribeSync} = require("git-describe") -var pkg = require('./package.json') +var pkg = require("./package.json") let gitInfo @@ -13,7 +13,7 @@ try { gitInfo = gitDescribeSync(__dirname) } catch(e) { gitInfo = { - hash: 'noGit', + hash: "noGit", dirty: false } } @@ -21,21 +21,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) { @@ -54,13 +54,12 @@ 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 })) } if( specialOptions.minimize ) { - plugins.push( new webpack.optimize.UglifyJsPlugin({ sourceMap: true, @@ -78,12 +77,11 @@ 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 @@ -92,21 +90,21 @@ module.exports = function(rules, options) { 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: { @@ -114,17 +112,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: { @@ -132,7 +130,7 @@ module.exports = function(rules, options) { } }, - devtool: specialOptions.sourcemaps ? 'cheap-module-source-map' : null, + devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null, plugins, diff --git a/webpack-dist-bundle.config.js b/webpack-dist-bundle.config.js index f805c8bf..f876d0ef 100644 --- a/webpack-dist-bundle.config.js +++ b/webpack-dist-bundle.config.js @@ -1,64 +1,33 @@ -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" } ] } ] +rules = rules.concat(styleRules) -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" ] }, diff --git a/webpack-dist-standalone.config.js b/webpack-dist-standalone.config.js index 66469098..65aa1839 100644 --- a/webpack-dist-standalone.config.js +++ b/webpack-dist-standalone.config.js @@ -1,65 +1,33 @@ -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" } ] } ] +rules = rules.concat(styleRules) -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" ] }, diff --git a/webpack-dist.config.js b/webpack-dist.config.js index b2e730dc..3aa65ed2 100644 --- a/webpack-dist.config.js +++ b/webpack-dist.config.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, diff --git a/webpack-hot-dev-server.config.js b/webpack-hot-dev-server.config.js index cb8e4b70..3e61628b 100644 --- a/webpack-hot-dev-server.config.js +++ b/webpack-hot-dev-server.config.js @@ -1,55 +1,55 @@ -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', + "style-loader", + "css-loader", { - loader: 'postcss-loader', + loader: "postcss-loader", }, - 'less-loader' + "less-loader" ] } ] @@ -60,25 +60,25 @@ 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/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, diff --git a/webpack-watch.config.js b/webpack-watch.config.js index 4406b0aa..ed5c711c 100644 --- a/webpack-watch.config.js +++ b/webpack-watch.config.js @@ -1,3 +1,3 @@ -var config = require("./webpack-dist.config.js") +const config = require("./webpack-dist.config.js") module.exports = config diff --git a/webpack.check.js b/webpack.check.js index 02a47376..6f10188b 100644 --- a/webpack.check.js +++ b/webpack.check.js @@ -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, { diff --git a/webpack.config.js b/webpack.config.js index 0d22493e..f0ca3601 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,3 @@ module.exports = require("./make-webpack-config")({ -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/webpack.dist-style.config.js b/webpack.dist-style.config.js new file mode 100644 index 00000000..1a3424fd --- /dev/null +++ b/webpack.dist-style.config.js @@ -0,0 +1,45 @@ +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" + } + } + ] + }) +}, +{ test: /\.(less)(\?.*)?$/, + use: ExtractTextPlugin.extract({ + fallback: "style-loader", + use: ["css-loader", + { + loader: "postcss-loader", + }, + "less-loader" + ] + }) +}] \ No newline at end of file From 7000c20765eb1e92039b30aafd64472b56d51bff Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Tue, 11 Jul 2017 20:17:06 -0600 Subject: [PATCH 02/17] Moved split-pane-mode.less file into a .scss file. Removed remaining unused .less files. Removed reference to less file in webpack config. Removed dependency on less-loader. Updated standalone and bundle builds to no longer compile any styles. --- package.json | 1 - .../components/split-pane-mode.jsx | 1 - .../components/split-pane-mode.less | 5 -- src/plugins/topbar/topbar.less | 52 ------------------- src/standalone/index.js | 2 - src/style/_split-pane-mode.scss | 3 ++ src/style/main.scss | 1 + webpack-dist-bundle.config.js | 1 - webpack-dist-standalone.config.js | 1 - webpack-hot-dev-server.config.js | 10 ---- webpack.dist-style.config.js | 11 ---- 11 files changed, 4 insertions(+), 84 deletions(-) delete mode 100644 src/core/plugins/split-pane-mode/components/split-pane-mode.less delete mode 100644 src/plugins/topbar/topbar.less create mode 100644 src/style/_split-pane-mode.scss diff --git a/package.json b/package.json index 895e22dd..5ad9552f 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,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", "node-sass": "^4.5.0", diff --git a/src/core/plugins/split-pane-mode/components/split-pane-mode.jsx b/src/core/plugins/split-pane-mode/components/split-pane-mode.jsx index 73f2acbc..c1a8fa9c 100644 --- a/src/core/plugins/split-pane-mode/components/split-pane-mode.jsx +++ b/src/core/plugins/split-pane-mode/components/split-pane-mode.jsx @@ -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" diff --git a/src/core/plugins/split-pane-mode/components/split-pane-mode.less b/src/core/plugins/split-pane-mode/components/split-pane-mode.less deleted file mode 100644 index d5437b37..00000000 --- a/src/core/plugins/split-pane-mode/components/split-pane-mode.less +++ /dev/null @@ -1,5 +0,0 @@ -.swagger-ui { - .Resizer.vertical.disabled { - display: none; - } -} diff --git a/src/plugins/topbar/topbar.less b/src/plugins/topbar/topbar.less deleted file mode 100644 index 9818940b..00000000 --- a/src/plugins/topbar/topbar.less +++ /dev/null @@ -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; - } -} diff --git a/src/standalone/index.js b/src/standalone/index.js index 6616a744..40e9a50c 100644 --- a/src/standalone/index.js +++ b/src/standalone/index.js @@ -1,6 +1,4 @@ import StandaloneLayout from "./layout" -import "../style/main.scss" - import TopbarPlugin from "plugins/topbar" import ConfigsPlugin from "plugins/configs" diff --git a/src/style/_split-pane-mode.scss b/src/style/_split-pane-mode.scss new file mode 100644 index 00000000..4a53d4c6 --- /dev/null +++ b/src/style/_split-pane-mode.scss @@ -0,0 +1,3 @@ +.Resizer.vertical.disabled { + display: none; +} \ No newline at end of file diff --git a/src/style/main.scss b/src/style/main.scss index f6ff00fb..443cc37c 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -14,4 +14,5 @@ @import 'information'; @import 'authorize'; @import 'errors'; + @import 'split-pane-mode'; } diff --git a/webpack-dist-bundle.config.js b/webpack-dist-bundle.config.js index f876d0ef..27a26046 100644 --- a/webpack-dist-bundle.config.js +++ b/webpack-dist-bundle.config.js @@ -15,7 +15,6 @@ let rules = [ ] } ] -rules = rules.concat(styleRules) module.exports = require("./make-webpack-config.js")(rules, { _special: { diff --git a/webpack-dist-standalone.config.js b/webpack-dist-standalone.config.js index 65aa1839..e19f428b 100644 --- a/webpack-dist-standalone.config.js +++ b/webpack-dist-standalone.config.js @@ -15,7 +15,6 @@ let rules = [ ] } ] -rules = rules.concat(styleRules) module.exports = require("./make-webpack-config.js")(rules, { _special: { diff --git a/webpack-hot-dev-server.config.js b/webpack-hot-dev-server.config.js index 3e61628b..c964de7e 100644 --- a/webpack-hot-dev-server.config.js +++ b/webpack-hot-dev-server.config.js @@ -41,16 +41,6 @@ const rules = [ } } ] - }, - { test: /\.(less)(\?.*)?$/, - use: [ - "style-loader", - "css-loader", - { - loader: "postcss-loader", - }, - "less-loader" - ] } ] diff --git a/webpack.dist-style.config.js b/webpack.dist-style.config.js index 1a3424fd..2b36430a 100644 --- a/webpack.dist-style.config.js +++ b/webpack.dist-style.config.js @@ -31,15 +31,4 @@ module.exports = [{ } ] }) -}, -{ test: /\.(less)(\?.*)?$/, - use: ExtractTextPlugin.extract({ - fallback: "style-loader", - use: ["css-loader", - { - loader: "postcss-loader", - }, - "less-loader" - ] - }) }] \ No newline at end of file From 87bb4420e2d4c6a0b32d227b77b339eb19a64e25 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Tue, 11 Jul 2017 20:30:52 -0600 Subject: [PATCH 03/17] Add `main.scss` as an entry point for the dev build --- webpack-hot-dev-server.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpack-hot-dev-server.config.js b/webpack-hot-dev-server.config.js index c964de7e..e002628f 100644 --- a/webpack-hot-dev-server.config.js +++ b/webpack-hot-dev-server.config.js @@ -55,6 +55,7 @@ module.exports = require("./make-webpack-config")(rules, { "./src/core/index.js" ], "swagger-ui-standalone-preset": [ + "./src/style/main.scss", "./src/polyfills", "./src/standalone/index.js", ] From a8f1f4979a22ddc586532d194acd96e3b19b6383 Mon Sep 17 00:00:00 2001 From: shockey Date: Mon, 17 Jul 2017 20:03:11 -0700 Subject: [PATCH 04/17] Add security contact to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0144744e..f7908130 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,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 From f7e6cadff3a1a6e9a6e67126828afe668ff399f1 Mon Sep 17 00:00:00 2001 From: shockey Date: Mon, 17 Jul 2017 20:20:46 -0700 Subject: [PATCH 05/17] Add deepLinking to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f7908130..dac53d35 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ displayOperationId | Controls the display of operationId in operations list. The 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 From 0bc382b78a83ce9b145a72e02f26a3741d6d4848 Mon Sep 17 00:00:00 2001 From: TANAKA Koichi Date: Tue, 18 Jul 2017 16:52:29 +0900 Subject: [PATCH 06/17] Fix oauth2 password flow fix filed name for scope of grant request body. --- src/core/plugins/auth/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/plugins/auth/actions.js b/src/core/plugins/auth/actions.js index 221d80a8..772e858c 100644 --- a/src/core/plugins/auth/actions.js +++ b/src/core/plugins/auth/actions.js @@ -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 = {} From c076a07092854ba66f07d5d47f3d3bb4ad47a8b7 Mon Sep 17 00:00:00 2001 From: Damiano Albani Date: Tue, 18 Jul 2017 15:23:44 +0200 Subject: [PATCH 07/17] Embellish "base URL" caption --- src/core/components/info.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 6ea1ed48..5e2b368a 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -15,7 +15,7 @@ class Path extends React.Component { return (
-        [ Base url: {host}{basePath}]
+        [ Base URL: {host}{basePath} ]
       
) } From 2c35ba2c8f0dd4ce373a70b2b692b45f83abcb47 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Tue, 18 Jul 2017 19:58:02 -0600 Subject: [PATCH 08/17] Fixes #3405 - Changed logic for schemes.jsx component to select default scheme when there is no selected scheme. Added test for functionality. --- src/core/components/schemes.jsx | 5 ++-- test/components/schemes.js | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/components/schemes.js diff --git a/src/core/components/schemes.jsx b/src/core/components/schemes.jsx index 8be4180a..f9fe8f81 100644 --- a/src/core/components/schemes.jsx +++ b/src/core/components/schemes.jsx @@ -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()) } } diff --git a/test/components/schemes.js b/test/components/schemes.js new file mode 100644 index 00000000..a21c0628 --- /dev/null +++ b/test/components/schemes.js @@ -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("", 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() + + // 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") + }) +}) From 2ef7b2ad8d9b802d9044164bed8eda1b6ce1f661 Mon Sep 17 00:00:00 2001 From: Kyle Shockey Date: Tue, 18 Jul 2017 20:32:09 -0700 Subject: [PATCH 09/17] Remove path translator --- src/core/path-translator.js | 67 ---------------------------------- src/core/plugins/util/index.js | 3 +- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 src/core/path-translator.js diff --git a/src/core/path-translator.js b/src/core/path-translator.js deleted file mode 100644 index 4d1ed27b..00000000 --- a/src/core/path-translator.js +++ /dev/null @@ -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 -} diff --git a/src/core/plugins/util/index.js b/src/core/plugins/util/index.js index 57e163f4..033288a6 100644 --- a/src/core/plugins/util/index.js +++ b/src/core/plugins/util/index.js @@ -1,8 +1,7 @@ import { shallowEqualKeys } from "core/utils" -import { transformPathToArray } from "core/path-translator" export default function() { return { - fn: { shallowEqualKeys, transformPathToArray } + fn: { shallowEqualKeys } } } From 6a48e43ca526bac923da561f412ae05a96e5621c Mon Sep 17 00:00:00 2001 From: Kyle Shockey Date: Tue, 18 Jul 2017 20:42:46 -0700 Subject: [PATCH 10/17] Delete path translator tests --- test/core/path-translator.js | 183 ----------------------------------- 1 file changed, 183 deletions(-) delete mode 100644 test/core/path-translator.js diff --git a/test/core/path-translator.js b/test/core/path-translator.js deleted file mode 100644 index 7abc1d8e..00000000 --- a/test/core/path-translator.js +++ /dev/null @@ -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"]) - - }) - - }) - -}) From 25c63b5f76128d177d59f932da0befe875d96598 Mon Sep 17 00:00:00 2001 From: Leon Weidauer Date: Thu, 20 Jul 2017 12:01:00 +0200 Subject: [PATCH 11/17] Fix length issue in OrderedMaps Previously, a definiton with a 'length' property with numeric value would result in an OrderedMap of that length. This is now fixed and covered by tests --- src/core/utils.js | 2 +- test/core/utils.js | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 95771fb4..f2e28948 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -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) { diff --git a/test/core/utils.js b/test/core/utils.js index a63da13d..fbfb24ab 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -1,10 +1,10 @@ /* eslint-env mocha */ import expect from "expect" import { fromJS } from "immutable" -import { mapToList, validateNumber, validateInteger, validateParam, validateFile } 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(){ @@ -306,4 +306,31 @@ describe("utils", function(){ expect( result ).toEqual( [{index: 0, error: "Value must be an integer"}, {index: 1, error: "Value must be an integer"}] ) }) }) + + 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] ) + }) + }) }) From 1e5304790ede020e31fcedfdf34f165c715cf91a Mon Sep 17 00:00:00 2001 From: shockey Date: Thu, 20 Jul 2017 17:20:01 -0700 Subject: [PATCH 12/17] Enable deep linking in dist/index.html --- dist/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/index.html b/dist/index.html index f6aaf9e4..52590951 100644 --- a/dist/index.html +++ b/dist/index.html @@ -76,6 +76,7 @@ window.onload = function() { const ui = SwaggerUIBundle({ url: "http://petstore.swagger.io/v2/swagger.json", dom_id: '#swagger-ui', + deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset From 666e22f9895ec70095d4cf29ade8fd625b96b1fb Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Thu, 20 Jul 2017 21:33:57 -0600 Subject: [PATCH 13/17] Update parameter elements. Rework validateParam() function. Added .btn-sm class for "Add item" and "Remove item" buttons in array parameters. Reduce border-width on + ) @@ -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 () } } diff --git a/src/core/utils.js b/src/core/utils.js index 95771fb4..9e114dae 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -468,6 +468,18 @@ export const validateFile = ( val ) => { } } +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 = [] @@ -475,52 +487,66 @@ 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() - let fileCheck = type === "file" && !(value instanceof win.File) + // If the parameter is required OR the parameter has a value (meaning optional, but filled in) + // then we should do our validation routine + if ( 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 ( required && (stringCheck || arrayCheck || listCheck || fileCheck) ) { - errors.push("Required field is not provided") - return errors - } + if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) { + errors.push("Required field is not provided") + return errors + } - if ( value === null || value === undefined ) { - 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 - 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 ( !value.count() ) { return errors } - if ( !value.count() ) { return errors } + itemType = param.getIn(["items", "type"]) - itemType = param.getIn(["items", "type"]) + value.forEach((item, index) => { + let err - value.forEach((item, index) => { - let err + if (itemType === "number") { + err = validateNumber(item) + } else if (itemType === "integer") { + err = validateInteger(item) + } else if (itemType === "string") { + err = validateString(item) + } - if (itemType === "number") { - err = validateNumber(item) - } else if (itemType === "integer") { - err = validateInteger(item) - } - - if ( err ) { - errors.push({ index: index, error: err}) - } - }) - } else if ( type === "file" ) { - let err = validateFile(value) - if (!err) return errors - errors.push(err) + 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 diff --git a/src/style/_buttons.scss b/src/style/_buttons.scss index 2470f8b0..bfb85023 100644 --- a/src/style/_buttons.scss +++ b/src/style/_buttons.scss @@ -14,6 +14,11 @@ @include text_headline(); + &.btn-sm { + font-size: 12px; + padding: 4px 23px; + } + &[disabled] { cursor: not-allowed; @@ -165,6 +170,9 @@ button { cursor: pointer; - outline: none; + + &.invalid { + @include invalidFormElement(); + } } diff --git a/src/style/_form.scss b/src/style/_form.scss index 185b836e..e54d5438 100644 --- a/src/style/_form.scss +++ b/src/style/_form.scss @@ -21,6 +21,10 @@ select background: #f7f7f7; } + + &.invalid { + @include invalidFormElement(); + } } .opblock-body select @@ -53,12 +57,8 @@ input[type=file] border-radius: 4px; background: #fff; - &.invalid - { - animation: shake .4s 1; - - border-color: $_color-delete; - background: lighten($_color-delete, 35%); + &.invalid { + @include invalidFormElement(); } } diff --git a/src/style/_mixins.scss b/src/style/_mixins.scss index a2a27d48..8fd01720 100644 --- a/src/style/_mixins.scss +++ b/src/style/_mixins.scss @@ -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%); +} diff --git a/src/style/_table.scss b/src/style/_table.scss index 3d58f3d8..d1481709 100644 --- a/src/style/_table.scss +++ b/src/style/_table.scss @@ -97,6 +97,10 @@ table width: 100%; max-width: 340px; } + + select { + border-width: 1px; + } } .parameter__name diff --git a/test/core/utils.js b/test/core/utils.js index a63da13d..65ed7435 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -176,6 +176,7 @@ describe("utils", function(){ let result = null it("validates required strings", function() { + // invalid string param = fromJS({ required: true, type: "string", @@ -183,9 +184,39 @@ 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", @@ -193,9 +224,48 @@ describe("utils", function(){ }) 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", @@ -204,75 +274,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() { - // string instead of a number + // 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"}] ) - // undefined value + // valid array, with no 'type' for items param = fromJS({ - required: false, - type: "number", - value: undefined + required: true, + type: "array", + value: ["1"] }) result = validateParam( param, false ) expect( result ).toEqual( [] ) - // null value + // valid array, items match type param = fromJS({ - required: false, - type: "number", - value: null + required: true, + type: "array", + value: ["1"], + items: { + type: "string" + } }) result = validateParam( param, false ) expect( result ).toEqual( [] ) }) - it("validates integers", function() { - // string instead of integer - param = fromJS({ - required: false, - type: "integer", - value: "test" - }) - result = validateParam( param, false ) - expect( result ).toEqual( ["Value must be an integer"] ) - - // undefined value - param = fromJS({ - required: false, - type: "integer", - value: undefined - }) - result = validateParam( param, false ) - expect( result ).toEqual( [] ) - - // null value - param = fromJS({ - required: false, - type: "integer", - value: null - }) - 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", @@ -281,7 +327,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", @@ -293,17 +339,209 @@ 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( [] ) + + // valid number + param = fromJS({ + required: false, + type: "integer", + value: 10 + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) }) }) }) From c82f85f1234cf6fd0c08a161c056e865c9780e1c Mon Sep 17 00:00:00 2001 From: shockey Date: Fri, 21 Jul 2017 16:01:52 -0700 Subject: [PATCH 14/17] Update topbar.jsx --- src/plugins/topbar/topbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/topbar/topbar.jsx b/src/plugins/topbar/topbar.jsx index fd3654bb..ec9db730 100644 --- a/src/plugins/topbar/topbar.jsx +++ b/src/plugins/topbar/topbar.jsx @@ -130,7 +130,7 @@ export default class Topbar extends React.Component {
- Swagger UX + Swagger UI swagger
From 0010bf20a62f37d8284211471d37c3936df10606 Mon Sep 17 00:00:00 2001 From: shockey Date: Fri, 21 Jul 2017 16:04:58 -0700 Subject: [PATCH 15/17] Update add-plugin.md --- src/plugins/add-plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/add-plugin.md b/src/plugins/add-plugin.md index 7a77cb30..2e2d52e1 100644 --- a/src/plugins/add-plugin.md +++ b/src/plugins/add-plugin.md @@ -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` From eb1f3b1fa613d68e18c965d1cb48d6f886d01f39 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Fri, 21 Jul 2017 17:23:28 -0600 Subject: [PATCH 16/17] Fixes #3422 - Fixed regression bug on models.jsx expand/collapse arrows due to bad merge --- src/core/components/models.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/models.jsx b/src/core/components/models.jsx index c61ae0c9..86c3256d 100644 --- a/src/core/components/models.jsx +++ b/src/core/components/models.jsx @@ -24,8 +24,8 @@ export default class Models extends Component { return

layoutActions.show("models", !showModels)}> Models - - + +

From b9bb4c124ec76bc3e565344c8abb5ce26432e9f6 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Fri, 21 Jul 2017 18:46:50 -0600 Subject: [PATCH 17/17] Fixes #3434 - Change link style color for .response-col_description__inner links --- src/style/_layout.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/style/_layout.scss b/src/style/_layout.scss index 90b9a088..43000ec6 100644 --- a/src/style/_layout.scss +++ b/src/style/_layout.scss @@ -508,6 +508,15 @@ body { margin: 0; } + + a + { + @include text_code(#89bf04); + text-decoration: underline; + &:hover { + color: #81b10c; + } + } } }