Compare commits

..
87 Commits
Author SHA1 Message Date
shockey a72fc24075 Merge pull request #3392 from shockey/v/3.0.19
v3.0.19
2017-07-14 21:53:08 -07:00
Kyle Shockey 4c89360c83 Rebuild dist 2017-07-14 21:44:40 -07:00
Kyle Shockey 830bafe9dc Remove animated from Models collapse
This solves a stack overflow problem in Models->Collapse->Collapse->Motion.
2017-07-14 21:15:36 -07:00
Kyle Shockey f60f43b039 Rebuild dist 2017-07-14 19:38:46 -07:00
Kyle Shockey e6587fb03c v3.0.19 2017-07-14 19:32:49 -07:00
shockey b0a67fb579 Merge pull request #3390 from umar-ahmed/master
Fixed bug in module export name for absolute path in swagger-ui-dist
2017-07-14 19:17:46 -07:00
shockey 5a2d5786b9 Merge branch 'master' into master 2017-07-14 19:12:04 -07:00
shockey 7462d069cc Maintain backwards compatibility with absolutePath 2017-07-14 19:11:53 -07:00
shockey 4a70ea03f0 Merge pull request #3391 from shockey/bug/3384-external-documentation-markdown
Render external documentation descriptions as Markdown
2017-07-14 18:26:32 -07:00
Kyle Shockey 7d01a5a8ac Fix external documentation styling 2017-07-14 18:22:01 -07:00
Kyle Shockey 4c6b765fe0 Render external documentation description as Markdown 2017-07-14 18:21:43 -07:00
shockey bbaf49e16a Merge pull request #3355 from owenconti/feature/2926-click-model-name-to-toggle
Fixes #2926 - Click name of model to collapse/expand
2017-07-14 18:04:17 -07:00
shockey 79cc31bd95 Merge branch 'master' into feature/2926-click-model-name-to-toggle 2017-07-14 17:46:33 -07:00
shockey 59dc570d9a Merge pull request #3353 from owenconti/bug/3078-newlines-in-markdown
Fixes #3078 - Newlines in markdown content
2017-07-14 17:36:06 -07:00
shockey cbe1680c6d Merge branch 'master' into bug/3078-newlines-in-markdown 2017-07-14 17:29:10 -07:00
shockey 6ec7006c21 Update markdown.jsx 2017-07-14 17:28:36 -07:00
shockey 81b2aa7979 Merge pull request #3363 from owenconti/bug/3361-non-required-integers
Fixes #3361 - Check for null and undefined values in validateParam
2017-07-14 17:07:04 -07:00
shockey b941882df7 Merge branch 'master' into bug/3361-non-required-integers 2017-07-14 16:52:40 -07:00
shockey 8f752adffa Merge pull request #3388 from shockey/ft/2884-operation-deep-linking
Operation & Tag deep linking
2017-07-14 16:48:21 -07:00
Kyle Shockey 449a7b0023 Remove Shebang functionality from Known Issues 2017-07-14 16:36:59 -07:00
Kyle Shockey abfad69e93 Disable deep linking by default 2017-07-14 16:18:25 -07:00
umar-ahmed d743137491 Modified module export name for absolute path to match documentation in Readme. 2017-07-14 18:51:08 -04:00
shockey ec33480c25 Merge branch 'master' into bug/3361-non-required-integers 2017-07-14 15:41:40 -07:00
shockey e77c980ae8 Merge branch 'master' into ft/2884-operation-deep-linking 2017-07-14 15:40:20 -07:00
Kyle Shockey 11c34d0348 Add docs 2017-07-14 03:25:10 -07:00
Kyle Shockey 66cb3ff2c0 Use swagger-client opId helper to generate layout keys for operations 2017-07-14 03:17:57 -07:00
Kyle Shockey 2906bdd699 Fire layout updates before attempting to scroll (improves docExpansion:none support) 2017-07-14 02:45:40 -07:00
Kyle Shockey 556e40e6d4 Don't place fragment links if deep linking is disabled 2017-07-14 01:50:49 -07:00
Kyle Shockey 73845907a3 Add anchors for tags and operations 2017-07-14 01:13:30 -07:00
Kyle Shockey 8851b138f9 Add config switch for deep linking 2017-07-14 00:51:26 -07:00
Kyle Shockey 41ecceed87 Rewire operation/tag DOM ids; implement hash scroll-to 2017-07-14 00:46:21 -07:00
Kyle Shockey 79f91b2e3e Rearrange isShownKey to make more sense
Tag then ID is closer to representing the underlying data structure
2017-07-14 00:19:02 -07:00
Kyle Shockey d7f6355d85 add setHash helper 2017-07-14 00:08:47 -07:00
Kyle Shockey 617cf7c867 Update URL fragment when operations & tags are shown or hidden 2017-07-13 23:24:19 -07:00
Kyle Shockey c051952081 Use operationIds from swagger-js if available, in layout triggers 2017-07-13 22:48:12 -07:00
shockey 08e84a0048 Merge pull request #3382 from swagger-api/moved-filter
Moved filter search to within layout
2017-07-13 22:31:11 -07:00
shockey f916e3c208 Topbar: Remove unused filter and layoutSelectors 2017-07-13 22:19:17 -07:00
shockey 2ae61e85b8 Update base.jsx 2017-07-13 22:00:16 -07:00
shockey d698aef1cd "Filter Operations" -> "Filter by tag" 2017-07-13 21:57:49 -07:00
shockey d9b0bb7758 Merge branch 'master' into moved-filter 2017-07-13 21:52:56 -07:00
Minasokoni d7c28912e0 updated input styling 2017-07-13 21:50:19 -07:00
Minasokoni 8b5c2cc409 moved filter search to layout 2017-07-13 21:48:20 -07:00
shockey 0eeb52b4b5 Merge pull request #3303 from Adrael/feature/apisSorter
Adding apisSorter options
2017-07-13 21:22:09 -07:00
shockey 28912dded3 Update index.js 2017-07-13 21:14:56 -07:00
shockey e2b73ebf2a Update README.md 2017-07-13 21:10:48 -07:00
shockey d8ba716da8 Merge branch 'master' into feature/apisSorter 2017-07-13 21:08:47 -07:00
Kyle Shockey e6c8eb3c61 Pass tag names to tagsSorter instead of taggedOperations values 2017-07-13 21:02:37 -07:00
Kyle Shockey 1cdc4691f7 Add MDN link to Array.prototype.sort() documentation 2017-07-13 20:38:56 -07:00
Kyle Shockey 571d65a582 apisSorter -> tagsSorter
"tags", not "tag", because it sorts the tags themselves, not the content of each tag.
2017-07-13 20:36:08 -07:00
shockey be7b2448b2 Merge pull request #3356 from owenconti/feature/3300-updated-version-info
Fixes #3300 - Updates to versions.swaggerUi information
2017-07-13 13:51:14 -07:00
shockey ff9e164d11 Merge branch 'master' into feature/3300-updated-version-info 2017-07-13 13:34:07 -07:00
shockey 0ee9f956eb Merge pull request #3377 from owenconti/bug/3375-content-type-fix
Fixes #3375 - Change order of logic for selecting requestContentType
2017-07-13 13:17:39 -07:00
Owen Conti 991ae66b39 Fixes #3375 - Change order of logic for selecting requestContentType to prioritize consumes_value first 2017-07-12 19:56:11 -06:00
shockey a5cab61d99 Merge pull request #3196 from gwynjudd/filter
Added maxRows and filter support
2017-07-11 22:14:38 -07:00
Kyle Shockey 66c8778a29 Remove obsolete prop from validation 2017-07-11 22:05:38 -07:00
Kyle Shockey 28f7a15fe9 Use state instead of component hand-down for managing filter 2017-07-11 21:54:56 -07:00
Kyle Shockey cf12091d93 Add filter to layout state 2017-07-11 21:33:14 -07:00
shockey 7cfc7cb4fc Merge branch 'master' into filter 2017-07-11 21:20:22 -07:00
Owen Conti bd31d3dedf Reimplemented changes after merge with master 2017-07-11 17:02:53 -06:00
Owen Conti fce8b7275b Merge branch 'master' of github.com:swagger-api/swagger-ui into feature/2926-click-model-name-to-toggle
# Conflicts:
#	src/core/components/model.jsx
2017-07-11 16:58:17 -06:00
Raphaël MARQUES d27962c0da Merge branch 'master' into feature/apisSorter 2017-07-11 21:13:45 +02:00
Raphaël MARQUES 297ee61038 Update index.js 2017-07-11 21:13:30 +02:00
Owen Conti 6beaaca6e6 Fixes #3361 - Check for null and undefined values in validateParam 2017-07-10 19:46:32 -06:00
shockey 4a66219782 Merge pull request #3331 from owenconti/bug/3102-unguarded-expressions
Separate model.jsx components to their own files
2017-07-10 18:45:45 -07:00
shockey 025ec662c0 Merge branch 'master' into bug/3102-unguarded-expressions 2017-07-10 18:42:18 -07:00
Owen Conti 914b21f228 Revert back to the 'breaks' fix for newlines in markdown content 2017-07-10 19:23:18 -06:00
Owen Conti b11399a57e Revert original fix and implement fix from @1Map 2017-07-10 17:39:05 -06:00
Minasokoni fe200f8616 styling updates 2017-07-11 09:46:44 +12:00
Gwyn Judd c3f9c094d1 Added maxRows and filter support
Useful for the display of very large swagger specs. Can limit the number of operations to a smaller value and search
2017-07-11 09:38:20 +12:00
Owen Conti d8400f8729 Fixes #3300 - Updates to versions.swaggerUi information 2017-07-09 14:17:18 -06:00
Owen Conti 14b63c5bf9 Fixes #2926 - Add optional title prop to Collapse so collapse elements can render titles to be clickable 2017-07-09 13:59:59 -06:00
Owen Conti 5d9ab6a0a2 Fixes #3078 - Added breaks: true to Remarkable so newlines are rendered as line breaks. Remove margin-top from <p> tags within .model elements to fix the alignment in the model's description text. 2017-07-08 10:48:52 -06:00
Ron fe9af65793 Merge branch 'master' into feature/apisSorter 2017-07-07 15:31:18 -07:00
Raphaël MARQUES ac3b12fd8b Merge branch 'master' into feature/apisSorter 2017-07-05 08:44:38 +02:00
Raphaël MARQUES d031413d92 Fix typo in README 2017-07-04 09:20:55 +02:00
Raphaël MARQUES aa07bb6b50 Complying with request for changes 2017-07-04 09:18:58 +02:00
Raphaël MARQUES 8ce2b309d8 Complying with request for changes 2017-07-04 09:15:43 +02:00
shockey 54f0c067b8 Merge branch 'master' into feature/apisSorter 2017-07-03 17:39:03 -07:00
Kyle Shockey 4bfb392e49 Undo changed defaults 2017-07-03 16:41:10 -07:00
Owen Conti 6fd5839103 Fix PropTypes in model-collapse component 2017-07-03 16:27:47 -06:00
Owen Conti 15e105c931 Merge branch 'master' of github.com:swagger-api/swagger-ui into bug/3102-unguarded-expressions
# Conflicts:
#	src/core/components/model.jsx
#	src/core/components/models.jsx
2017-07-03 16:12:31 -06:00
Raphaël MARQUES cb9ca7d4bf Merge branch 'master' into feature/apisSorter 2017-06-30 09:01:28 +02:00
Owen Conti d27cae0085 Work on #3102. Moved all the components out of model.jsx into their own files so they can be grabbed via getComponent() 2017-06-28 22:07:07 -06:00
Raphaël MARQUES e5aa285547 Merge branch 'master' into feature/apisSorter 2017-06-28 23:45:47 +02:00
Raphaël MARQUES ca9161a159 Merge branch 'master' into feature/apisSorter 2017-06-28 09:53:06 +02:00
Raphaël MARQUES 24bc8c4b85 Complying with CI 2017-06-27 16:45:23 +02:00
Raphaël MARQUES 4fe27786f4 Adding apisSorter options, taking a string or a function as a configuration value 2017-06-27 15:29:07 +02:00
46 changed files with 955 additions and 461 deletions
+1
View File
@@ -5,3 +5,4 @@ node_modules
npm-debug.log*
.eslintcache
package-lock.json
*.iml
+15 -13
View File
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 4 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes | Status
------------------ | ------------ | -------------------------- | ----- | ------
3.0.18 | 2017-07-07 | 2.0 | [tag v3.0.18](https://github.com/swagger-api/swagger-ui/tree/v3.0.18) |
3.0.19 | 2017-07-14 | 2.0 | [tag v3.0.19](https://github.com/swagger-api/swagger-ui/tree/v3.0.19) |
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) |
@@ -67,7 +67,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.
@@ -82,17 +81,17 @@ 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
@@ -137,6 +136,7 @@ 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
@@ -144,6 +144,8 @@ modelPropertyMacro | MUST be a function. Function to set default values to each
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`.
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.
### Plugins
+31 -31
View File
@@ -11,15 +11,15 @@
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
box-sizing: inherit;
}
body {
@@ -34,7 +34,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
@@ -70,34 +70,34 @@
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
window.ui = ui
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
</script>
</body>
+8 -8
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAsyKA;;;;;;AA8qEA;;;;;;;;;;;;;;;;;;;;;;;;;;AA6nTA;;;;;;;;;;;;;;AAu8JA;;;;;;;;;AAkgnBA;;;;;AAigQA;;;;;;AAqpVA","sourceRoot":""}
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAsyKA;;;;;;AAktEA;;;;;;;;;;;;;;;;;;;;;;;;;;AAkqTA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AA4tnBA;;;;;AAmpQA;;;;;;AA+mXA","sourceRoot":""}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA40CA;;;;;;AA4kFA","sourceRoot":""}
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA40CA;;;;;;AAqlFA","sourceRoot":""}
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAopaA","sourceRoot":""}
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAk/aA","sourceRoot":""}
+36
View 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.
+34 -32
View File
@@ -1,11 +1,12 @@
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")
const os = require("os")
var pkg = require('./package.json')
var pkg = require("./package.json")
let gitInfo
@@ -13,7 +14,7 @@ try {
gitInfo = gitDescribeSync(__dirname)
} catch(e) {
gitInfo = {
hash: 'noGit',
hash: "noGit",
dirty: false
}
}
@@ -21,21 +22,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,7 +55,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
}))
}
@@ -78,35 +79,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: {
@@ -114,17 +116,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 +134,7 @@ module.exports = function(rules, options) {
}
},
devtool: specialOptions.sourcemaps ? 'cheap-module-source-map' : null,
devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null,
plugins,
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.0.18",
"version": "3.0.19",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -68,9 +68,10 @@
"redux-logger": "*",
"reselect": "2.5.3",
"sanitize-html": "^1.14.1",
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "3.0.17",
"swagger-client": "3.0.18",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
+47
View 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 } = 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>
}
}
+19
View 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
+26 -2
View File
@@ -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
View 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>
)
}
}
+2 -2
View File
@@ -28,7 +28,7 @@ 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">
@@ -44,7 +44,7 @@ export default class ModelExample extends React.Component {
(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
View 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>
}
}
+7 -272
View File
@@ -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/>&nbsp;&nbsp;&nbsp;{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,
@@ -239,6 +27,9 @@ class Model extends Component {
render () {
let { schema, getComponent, 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
@@ -260,69 +51,13 @@ class Model extends Component {
name={ name || modelName }
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 } required={ required } />
case "string":
case "number":
case "integer":
case "boolean":
default:
return <Primitive { ...this.props } getComponent={ getComponent } schema={ modelSchema } name={ name || modelName } required={ required }/>
return <PrimitiveModel getComponent={ getComponent } schema={ modelSchema } 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>)
}
}
}
+5 -5
View File
@@ -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
@@ -24,15 +24,15 @@ export default class Models extends Component {
return <section className={ showModels ? "models is-open" : "models"}>
<h4 onClick={() => layoutActions.show("models", !showModels)}>
<span>Models</span>
<svg className="arrow" width="20" height="20">
<use xlinkHref={showModels ? "#large-arrow-down" : "#large-arrow"} />
<svg width="20" height="20">
<use xlinkHref="#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 }
+103
View File
@@ -0,0 +1,103 @@
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 required = 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(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>
</ModelCollapse>
</span>
}
}
+20 -8
View File
@@ -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,7 +201,9 @@ 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
+40 -4
View File
@@ -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>
@@ -48,8 +73,16 @@ 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 }
@@ -67,11 +100,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"))
+55
View 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/>&nbsp;&nbsp;&nbsp;{key}: { String(v) }</span>).toArray()
}
</span>): null
}
{
enumArray && <EnumModel value={ enumArray } getComponent={ getComponent } />
}
</span>
}
}
+6 -4
View File
@@ -18,10 +18,12 @@ function Markdown({ source }) {
return null
}
return <Remarkable
options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
source={sanitized}
></Remarkable>
return <div className="markdown">
<Remarkable
options={{html: true, typographer: true, breaks: true, linkify: true, linkTarget: "_blank"}}
source={sanitized}
></Remarkable>
</div>
}
Markdown.propTypes = {
+42 -8
View File
@@ -6,17 +6,46 @@ 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"]
const CONFIGS = [
"url",
"urls",
"urls.primaryName",
"spec",
"validatorUrl",
"onComplete",
"onFailure",
"authorizations",
"docExpansion",
"tagsSorter",
"maxDisplayedTags",
"filter",
"operationsSorter",
"supportedSubmitMethods",
"dom_id",
"defaultModelRendering",
"oauth2RedirectUrl",
"showRequestHeaders",
"custom",
"modelPropertyMacro",
"parameterMacro",
"displayOperationId",
"displayRequestDuration",
"deepLinking",
]
// 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,11 +55,14 @@ 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.
@@ -50,7 +82,9 @@ module.exports = function SwaggerUI(opts) {
store: { },
}
const constructorConfig = deepExtend({}, defaults, opts)
let queryConfig = parseSeach()
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
const storeConfigs = deepExtend({}, constructorConfig.store, {
system: {
@@ -59,7 +93,8 @@ module.exports = function SwaggerUI(opts) {
plugins: constructorConfig.presets,
state: {
layout: {
layout: constructorConfig.layout
layout: constructorConfig.layout,
filter: constructorConfig.filter
},
spec: {
spec: "",
@@ -80,7 +115,6 @@ module.exports = function SwaggerUI(opts) {
store.register([constructorConfig.plugins, inlinePlugin])
var system = store.getSystem()
let queryConfig = parseSeach()
system.initOAuth = system.authActions.configureAuth
+1
View File
@@ -0,0 +1 @@
See `docs/deep-linking.md`.
+7
View File
@@ -0,0 +1,7 @@
export const setHash = (value) => {
if(value) {
return history.pushState(null, null, `#${value}`)
} else {
return window.location.hash = ""
}
}
+18
View 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
}
}
}
}
@@ -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)
}
}
@@ -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
}
+8
View File
@@ -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 {
+3
View File
@@ -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
+2
View File
@@ -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))
+21 -13
View File
@@ -200,15 +200,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 +284,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,
+17 -3
View File
@@ -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,9 +42,15 @@ 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 Markdown from "core/components/providers/markdown"
@@ -88,8 +95,14 @@ export default function() {
curl: Curl,
schemes: Schemes,
modelExample: ModelExample,
model: Model,
models: Models,
ModelWrapper,
ModelCollapse,
Model,
Models,
EnumModel,
ObjectModel,
ArrayModel,
PrimitiveModel,
TryItOutButton,
Markdown,
BaseLayout
@@ -119,6 +132,7 @@ export default function() {
auth,
ast,
SplitPaneModePlugin,
downloadUrlPlugin
downloadUrlPlugin,
deepLinkingPlugin
]
}
+28 -21
View File
@@ -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,13 +451,13 @@ 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"
}
}
@@ -485,6 +485,10 @@ export const validateParam = (param, isXml) => {
return errors
}
if ( value === null || value === undefined ) {
return errors
}
if ( type === "number" ) {
let err = validateNumber(value)
if (!err) return errors
@@ -574,6 +578,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)
}
}
+9
View File
@@ -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")
+2 -2
View File
@@ -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 />
+60 -20
View File
@@ -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);
@@ -106,9 +105,10 @@ body
font-size: 14px;
font-weight: normal;
flex: 1;
padding: 0 10px;
flex: 1;
@include text_body();
}
}
@@ -134,6 +134,8 @@ body
transition: all .5s;
}
.opblock
{
margin: 0 0 15px 0;
@@ -154,24 +156,23 @@ body
.opblock-section-header
{
display: flex;
align-items: center;
padding: 8px 20px;
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;
align-items: center;
@include text_headline();
span
@@ -184,9 +185,10 @@ body
{
font-size: 14px;
flex: 1;
margin: 0;
flex: 1;
@include text_headline();
}
}
@@ -215,11 +217,11 @@ body
font-size: 16px;
display: flex;
align-items: center;
padding: 0 10px;
@include text_code();
align-items: center;
.view-line-link
{
@@ -258,18 +260,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 +318,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 +390,7 @@ body
}
.opblock-description-wrapper,
.opblock-external-docs-wrapper,
.opblock-title_normal
{
font-size: 12px;
@@ -404,6 +419,12 @@ body
}
}
.opblock-external-docs-wrapper {
h4 {
padding-left: 0px;
}
}
.execute-wrapper
{
padding: 20px;
@@ -498,13 +519,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 +552,9 @@ body
.schemes
{
display: flex;
align-items: center;
> label
> label
{
font-size: 12px;
font-weight: bold;
@@ -624,3 +642,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;
}
}
+4
View File
@@ -79,6 +79,10 @@
border-radius: 4px;
background: rgba(#000,.7);
}
p {
margin: 0 0 1em 0;
}
}
-1
View File
@@ -43,7 +43,6 @@
margin: 0;
border: 2px solid #547f00;
border-radius: 4px 0 0 4px;
outline: none;
}
+4
View File
@@ -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")
+67 -1
View File
@@ -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({ })
+38
View File
@@ -214,6 +214,7 @@ describe("utils", function(){
})
it("validates numbers", function() {
// string instead of a number
param = fromJS({
required: false,
type: "number",
@@ -221,9 +222,28 @@ describe("utils", function(){
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a number"] )
// undefined value
param = fromJS({
required: false,
type: "number",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// null value
param = fromJS({
required: false,
type: "number",
value: null
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates integers", function() {
// string instead of integer
param = fromJS({
required: false,
type: "integer",
@@ -231,6 +251,24 @@ describe("utils", function(){
})
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() {