Compare commits

...
34 Commits
Author SHA1 Message Date
Kyle Shockey 11c14ea1b0 Merge branch 'master' of github.com:swagger-api/swagger-ui 2018-01-26 19:38:54 -08:00
kyle 9653258bc1 v3.9.3 (#4170)
* default to empty `ImmutableMap` when grabbing op metadata

* pass `errors` into JsonSchema components

* Account for Immutable data structure in JavaScriptonSchema...

...and create empty Lists instead of Maps by default.

* Pass ImmutableList through to JsonSchema child components

* v3.9.3
2018-01-26 19:38:41 -08:00
Kyle Shockey 017556d80f Merge branch 'master' of github.com:swagger-api/swagger-ui 2018-01-26 19:18:23 -08:00
kyle 1e1b7f6063 fix(try-it-out): pass Parameter validation messages around in props for OAS3 (#4162)
* default to empty `ImmutableMap` when grabbing op metadata

* pass `errors` into JsonSchema components

* Account for Immutable data structure in JavaScriptonSchema...

...and create empty Lists instead of Maps by default.

* Pass ImmutableList through to JsonSchema child components
2018-01-25 20:14:35 -08:00
Kyle Shockey 0589349813 Pass ImmutableList through to JsonSchema child components 2018-01-25 18:04:13 -08:00
Kyle Shockey 46b97903a5 Account for Immutable data structure in JavaScriptonSchema...
...and create empty Lists instead of Maps by default.
2018-01-25 18:01:18 -08:00
Kyle Shockey 5730288112 pass errors into JsonSchema components 2018-01-25 17:54:25 -08:00
Kyle Shockey 5bd9846006 default to empty ImmutableMap when grabbing op metadata 2018-01-25 17:53:02 -08:00
maryscar 2f236982f8 improve(a11y): visibility accessibility via aria-label (#4127)
* Added aria-label for visual readers.

* Increased font weight for Models section span to meet contrast ratio visibility requirements.
2018-01-25 12:41:26 -08:00
Helder Sepulveda 4599033b5a fix(rendering): operation expand/collapse button title (#4130) 2018-01-24 16:48:04 -08:00
Rafael Lüder 6110a7da12 fix(docs): typos! (#4148) 2018-01-24 16:44:46 -08:00
kyle 10ee5c46c0 improve(pr-template) (#4156) 2018-01-24 16:40:39 -08:00
Helder Sepulveda 2ab6fcc49b fix(render-tags): always preserve small element (#4129) 2018-01-23 16:58:20 -08:00
Helen Kosova 179699277d fix(parameter-rendering): improve punctuation and spacing (#4145) 2018-01-23 16:33:27 -08:00
Mike Fidel 3f647875fd bug(docs) correct typo in installation.md (#4144) 2018-01-23 16:26:36 -08:00
kyle 22ccdfff10 release: v3.9.2 (#4141)
* v3.9.2

* rebuild dist for v3.9.2
2018-01-20 02:05:59 -06:00
kyle efc1194e4a improve(response-rendering): give Content-Disposition priority (#4140)
* Remove produces/consumes setter from OperationContainer

* Store consumes/produces information in `meta` key

* Migrate produces value state usage to `meta` key

* use meta consumes data for isXml check

* Fix failing tests

* normalize action name casing

* restore correct produces fallback value logic

* default to first server if current server is invalid

* improve(response-rendering): give Content-Disposition render priority
2018-01-19 23:08:19 -06:00
kyle bb84da4e1d fix(oas3-editor-rendering): default to first server if current server becomes invalid (#4139)
* Remove produces/consumes setter from OperationContainer

* Store consumes/produces information in `meta` key

* Migrate produces value state usage to `meta` key

* use meta consumes data for isXml check

* Fix failing tests

* normalize action name casing

* restore correct produces fallback value logic

* default to first server if current server is invalid
2018-01-19 21:38:12 -06:00
Andrew Luca e531c40356 fix(styles): stop wrapper from overflowing on smaller devices (#4121)
When using `swagger-ui` for consumption by JavaScript web projects
that include module bundlers, instead of `swagger-ui-dist`
there is no `box-sizing: border-box` on `.wrapper`
and it's width overflows because of additional `padding: 0 20px`
2018-01-19 13:54:01 -06:00
Paulo Schneider f29202fed8 improve(docker): support multiple API URLs option (#4044)
The API_URLS environment variable can be used to run the Docker image with multiple urls.

Example:
docker run -p 80:8080 -e API_URLS=[{name:"First API", url:"http://firstapiurl"},{name:"Second API", url:"http://secondapiurl"}] swaggerapi/swagger-ui
2018-01-19 12:35:20 -06:00
Can Taşdemir c28213d457 fix(docs): correct plugin api typo (#4138) 2018-01-19 10:53:06 -06:00
Drew Ballance 286c0e0f72 fix: execution error in IE (#4128) 2018-01-18 21:35:43 -06:00
kyle 7af7121e64 fix: streamline management of user-selected produces and consumes values (#4137)
* Remove produces/consumes setter from OperationContainer

* Store consumes/produces information in `meta` key

* Migrate produces value state usage to `meta` key

* use meta consumes data for isXml check

* Fix failing tests

* normalize action name casing

* restore correct produces fallback value logic
2018-01-18 20:37:44 -06:00
kyle ec4890299c Merge pull request #4111 from swagger-api/swagger-ui/patch-1
Fix example
2018-01-15 17:37:15 -06:00
Alexandre Barbier 31ccaf3ea3 Fix example 2018-01-15 12:07:29 +01:00
kyle 253573200a release: 3.9.1 (#4106)
* fix(core-components): make `onToggle` a non-required prop in ModelCollapse

* release: 3.9.1
2018-01-13 01:30:37 -06:00
diegospd 024b7db81e fix(docs): correct tranditional typo (#4104) 2018-01-12 16:39:13 -06:00
David Muto 5b1dbec9b0 feature(docker-image): allow custom nginx port within container (#4086)
* Use PORT to override nginx listen port when defined
* Add dockerignore to make build context smaller
2018-01-12 13:52:09 -06:00
Lukasr29 64f99356a5 improve(tio): extract file name from Content-Disposition (#4035)
* Added extraction of quoted file name from content disposition header

* Added extraction of quoted file name from content disposition header - PR Fixes

* Added extraction of quoted file name from content disposition header - PR Fixes

* Added extraction of quoted file name from content disposition header - PR Fixes
2018-01-11 19:39:42 -06:00
linelson b3e80cc500 fix(tic-validation): uuid format parameter validation should be case insensitive (#4102)
* fix uuid validation casing (#4099)
* add test
2018-01-11 18:42:37 -06:00
kyle 53ea2003c1 improve(npm-module): include standalone preset in swagger-ui (#4101) 2018-01-11 18:39:13 -06:00
Udit Desai a037c7205b improve(docker-image): symlink swagger.json from app directory to nginx (#4024) 2018-01-11 17:06:56 -06:00
kyle 63d5f164c6 fix(core-components): make onToggle a non-required prop in ModelCollapse (#4100) 2018-01-11 16:56:36 -06:00
Helen Kosova 2e816e0440 fix(docs): broken link in the issue template (#4094) 2018-01-10 18:32:33 -06:00
37 changed files with 334 additions and 154 deletions
+7
View File
@@ -0,0 +1,7 @@
/.git
/.github
/dev-helpers
/docs
/src
/swagger-ui-dist-package
/test
+1 -1
View File
@@ -18,7 +18,7 @@ or anything that violates the specifications.
<!--- Provide a general summary of the issue in the title above -->
<!---
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/version-detection.md
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md
--->
+26 -13
View File
@@ -24,19 +24,32 @@
### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] No code changes (changes to documentation, CI, metadata, etc)
- [ ] Dependency changes (any modification to dependencies in `package.json`)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
### Checklist:
## Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
### My PR contains...
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] No code changes (`src/` is unmodified: changes to documentation, CI, metadata, etc.)
- [ ] Dependency changes (any modification to dependencies in `package.json`)
- [ ] Bug fixes (non-breaking change which fixes an issue)
- [ ] Improvements (misc. changes to existing features)
- [ ] Features (non-breaking change which adds functionality)
### My changes...
- [ ] are breaking changes to a public API (config options, System API, major UI change, etc).
- [ ] are breaking changes to a private API (Redux, component props, utility functions, etc.).
- [ ] are breaking changes to a developer API (npm script behavior changes, new dev system dependencies, etc).
- [ ] are not breaking changes.
### Documentation
- [ ] My changes do not require a change to the project documentation.
- [ ] My changes require a change to the project documentation.
- [ ] If yes to above: I have updated the documentation accordingly.
### Automated tests
- [ ] My changes can not or do not need to be tested.
- [ ] My changes can and should be tested by unit and/or integration tests.
- [ ] If yes to above: I have added tests to cover my changes.
- [ ] If yes to above: I have taken care to cover edge cases in my tests.
- [ ] All new and existing tests passed.
+2
View File
@@ -4,6 +4,8 @@
!package.json
!dist/swagger-ui.js
!dist/swagger-ui.js.map
!dist/swagger-ui-standalone-preset.js
!dist/swagger-ui-standalone-preset.js.map
!dist/swagger-ui.css
!dist/swagger-ui.css.map
!dist/oauth2-redirect.html
+2 -1
View File
@@ -5,6 +5,7 @@ MAINTAINER fehguy
ENV VERSION "v2.2.10"
ENV FOLDER "swagger-ui-2.2.10"
ENV API_URL "http://petstore.swagger.io/v2/swagger.json"
ENV API_URLS ""
ENV API_KEY "**None**"
ENV OAUTH_CLIENT_ID "**None**"
ENV OAUTH_CLIENT_SECRET "**None**"
@@ -12,7 +13,7 @@ ENV OAUTH_REALM "**None**"
ENV OAUTH_APP_NAME "**None**"
ENV OAUTH_ADDITIONAL_PARAMS "**None**"
ENV SWAGGER_JSON "/app/swagger.json"
ENV PORT 80
ENV PORT 8080
ENV BASE_URL ""
RUN apk add --update nginx
+1 -1
View File
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | -----
3.9.0 | 2018-01-05 | 2.0, 3.0 | [tag v3.9.0](https://github.com/swagger-api/swagger-ui/tree/v3.9.0)
3.9.3 | 2018-01-26 | 2.0, 3.0 | [tag v3.9.3](https://github.com/swagger-api/swagger-ui/tree/v3.9.3)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
+14 -14
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+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
File diff suppressed because one or more lines are too long
+11 -1
View File
@@ -36,7 +36,7 @@ if [ "$OAUTH_ADDITIONAL_PARAMS" != "**None**" ]; then
fi
if [[ -f $SWAGGER_JSON ]]; then
cp $SWAGGER_JSON $NGINX_ROOT
cp -s $SWAGGER_JSON $NGINX_ROOT
REL_PATH="./$(basename $SWAGGER_JSON)"
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
sed -i "s|http://example.com/api|$REL_PATH|g" $INDEX_FILE
@@ -53,4 +53,14 @@ if [[ -n "$VALIDATOR_URL" ]]; then
unset TMP_VU
fi
# replace `url` with `urls` option if API_URLS is set
if [[ -n "$API_URLS" ]]; then
sed -i "s|url: .*,|urls: $API_URLS,|g" $INDEX_FILE
fi
# replace the PORT that nginx listens on if PORT is supplied
if [[ -n "${PORT}" ]]; then
sed -i "s|8080|${PORT}|g" /etc/nginx/nginx.conf
fi
exec nginx -g 'daemon off;'
+6 -6
View File
@@ -20,16 +20,16 @@ class OperationsLayout extends React.Component {
const Operations = getComponent("Operations", true)
return {
return (
<div>
<Operations />
</div>
}
)
}
}
// Create the plugin that provides our layout component
const OperationsLayoutPlugin = function() {
const OperationsLayoutPlugin = () => {
return {
components: {
OperationsLayout: OperationsLayout
@@ -62,19 +62,19 @@ class AugmentingLayout extends React.Component {
const BaseLayout = getComponent("BaseLayout", true)
return {
return (
<div>
<div className="myCustomHeader">
<h1>I have a custom header above Swagger-UI!</h1>
</div>
<BaseLayout />
</div>
}
)
}
}
// Create the plugin that provides our layout component
const AugmentingLayoutPlugin = function() {
const AugmentingLayoutPlugin = () => {
return {
components: {
AugmentingLayout: AugmentingLayout
+2 -2
View File
@@ -334,7 +334,7 @@ const MyWrapComponentPlugin = function(system) {
if(props.number > 10) {
return <div>
<h3>Warning! Big number ahead.</h3>
<OriginalComponent {...props} />
<Original {...props} />
</div>
} else {
return <Original {...props} />
@@ -353,7 +353,7 @@ const MyWrapComponentPlugin = function(system) {
if(props.number > 10) {
return <div>
<h3>Warning! Big number ahead.</h3>
<OriginalComponent {...props} />
<Original {...props} />
</div>
} else {
return <Original {...props} />
+3 -3
View File
@@ -27,7 +27,7 @@ The module's contents mirrors the `dist` folder you see in the Git repository. T
```javascript
const express = require('express')
const pathToSwaggerUi = require('swagger-ui').absolutePath()
const pathToSwaggerUi = require('swagger-ui-dist').absolutePath()
const app = express()
@@ -37,7 +37,7 @@ app.listen(3000)
```
The module also exports `SwaggerUIBundle` and `SwaggerUIStandalonePreset`, so
if you're in a JavaScript project that can't handle a tranditional npm module,
if you're in a JavaScript project that can't handle a traditional npm module,
you could do something like this:
```js
@@ -83,7 +83,7 @@ This will serve Swagger UI at `/swagger` instead of `/`.
### unpkg
You can embed Swagger-UI's code directly in your HTML by using unkpg's interface:
You can embed Swagger-UI's code directly in your HTML by using unpkg's interface:
```html
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.9.0",
"version": "3.9.3",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -82,7 +82,7 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.4.4",
"swagger-client": "^3.4.6",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
@@ -20,7 +20,9 @@ export default class AuthorizeOperationBtn extends React.Component {
let { isAuthorized } = this.props
return (
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"}
aria-label={isAuthorized ? "authorization button locked" : "authorization button unlocked"}
onClick={this.onClick}>
<svg width="20" height="20">
<use href={ isAuthorized ? "#locked" : "#unlocked" } xlinkHref={ isAuthorized ? "#locked" : "#unlocked" } />
</svg>
+3 -2
View File
@@ -8,13 +8,14 @@ export default class ModelCollapse extends Component {
children: PropTypes.any,
title: PropTypes.element,
modelName: PropTypes.string,
onToggle: PropTypes.func.isRequired
onToggle: PropTypes.func
}
static defaultProps = {
collapsedContent: "{...}",
expanded: false,
title: null
title: null,
onToggle: () => {}
}
constructor(props, context) {
+1 -1
View File
@@ -12,7 +12,7 @@ export default class ObjectModel extends Component {
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
expanded: PropTypes.bool,
onToggle: PropTypes.func.isRequired,
onToggle: PropTypes.func,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
+1 -1
View File
@@ -248,7 +248,7 @@ export default class Operation extends PureComponent {
oas3Actions={oas3Actions}
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
producesValue={ specSelectors.currentProducesFor([path, method]) }
specPath={specPath.push("responses")}
path={ path }
method={ method }
+2 -2
View File
@@ -86,7 +86,7 @@ export default class Operations extends React.Component {
isShown={showTag}
path={tag}
text={tag} />
{ !tagDescription ? null :
{ !tagDescription ? <small></small> :
<small>
<Markdown source={tagDescription} />
</small>
@@ -108,7 +108,7 @@ export default class Operations extends React.Component {
}
</div>
<button className="expand-operation" title="Expand operation" onClick={() => layoutActions.show(isShownKey, !showTag)}>
<button className="expand-operation" title={showTag ? "Collapse operation": "Expand operation"} onClick={() => layoutActions.show(isShownKey, !showTag)}>
<svg className="arrow" width="20" height="20">
<use href={showTag ? "#large-arrow-down" : "#large-arrow"} xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
</svg>
+5 -4
View File
@@ -156,19 +156,19 @@ export default class ParameterRow extends Component {
{ (bodyParam || !isExecute) && isDisplayParamItemsEnum ?
<Markdown source={
"<i>Available values</i> : " + paramItemsEnum.map(function(item) {
"<i>Available values</i>: " + paramItemsEnum.map(function(item) {
return item
}).toArray()}/>
}).toArray().join(", ")}/>
: null
}
{ (bodyParam || !isExecute) && paramDefaultValue !== undefined ?
<Markdown source={"<i>Default value</i> : " + paramDefaultValue}/>
<Markdown source={"<i>Default value</i>: " + paramDefaultValue}/>
: null
}
{ (bodyParam || !isExecute) && paramExample !== undefined ?
<Markdown source={"<i>Example</i> : " + paramExample}/>
<Markdown source={"<i>Example</i>: " + paramExample}/>
: null
}
@@ -181,6 +181,7 @@ export default class ParameterRow extends Component {
required={ required }
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
errors={ param.get("errors") }
schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
}
+36 -35
View File
@@ -2,6 +2,7 @@ import React from "react"
import PropTypes from "prop-types"
import formatXml from "xml-but-prettier"
import lowerCase from "lodash/lowerCase"
import { extractFileNameFromContentDispositionHeader } from "core/utils"
export default class ResponseBody extends React.Component {
@@ -19,8 +20,41 @@ export default class ResponseBody extends React.Component {
let body, bodyEl
url = url || ""
// JSON
if (/json/i.test(contentType)) {
if (
/^application\/octet-stream/i.test(contentType) ||
(headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"])) ||
(headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"])) ||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
// Download
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (!isSafari && "Blob" in window) {
let type = contentType || "text/html"
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
let href = window.URL.createObjectURL(blob)
let fileName = url.substr(url.lastIndexOf("/") + 1)
let download = [type, fileName, href].join(":")
// Use filename from response header,
// First check if filename is quoted (e.g. contains space), if no, fallback to not quoted check
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
if (typeof disposition !== "undefined") {
let responseFilename = extractFileNameFromContentDispositionHeader(disposition)
if (responseFilename !== null) {
download = responseFilename
}
}
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
} else {
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
}
// Anything else (CORS)
} else if (/json/i.test(contentType)) {
// JSON
try {
body = JSON.stringify(JSON.parse(content), null, " ")
} catch (error) {
@@ -52,39 +86,6 @@ export default class ResponseBody extends React.Component {
// Audio
} else if (/^audio\//i.test(contentType)) {
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
// Download
} else if (
/^application\/octet-stream/i.test(contentType) ||
(headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"])) ||
(headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"])) ||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (!isSafari && "Blob" in window) {
let type = contentType || "text/html"
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
let href = window.URL.createObjectURL(blob)
let fileName = url.substr(url.lastIndexOf("/") + 1)
let download = [type, fileName, href].join(":")
// Use filename from response header
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
if (typeof disposition !== "undefined") {
let responseFilename = /filename=([^;]*);?/i.exec(disposition)
if (responseFilename !== null && responseFilename.length > 1) {
download = responseFilename[1]
}
}
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
} else {
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
}
// Anything else (CORS)
} else if (typeof content === "string") {
bodyEl = <HighlightCode value={ content } />
} else if ( content.size > 0 ) {
@@ -82,27 +82,9 @@ export default class OperationContainer extends PureComponent {
}
componentWillReceiveProps(nextProps) {
const defaultContentType = "application/json"
let { specActions, path, method, op } = nextProps
let operation = op.get("operation")
let producesValue = operation.get("produces_value")
let produces = operation.get("produces")
let consumes = operation.get("consumes")
let consumesValue = operation.get("consumes_value")
if(nextProps.response !== this.props.response) {
this.setState({ executeInProgress: false })
}
if (producesValue === undefined) {
producesValue = produces && produces.size ? produces.first() : defaultContentType
specActions.changeProducesValue([path, method], producesValue)
}
if (consumesValue === undefined) {
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
specActions.changeConsumesValue([path, method], consumesValue)
}
}
toggleShown =() => {
+21 -15
View File
@@ -1,6 +1,7 @@
import React, { PureComponent, Component } from "react"
import PropTypes from "prop-types"
import { List, fromJS } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
//import "less/json-schema-form"
const noop = ()=> {}
@@ -11,6 +12,7 @@ const JsonSchemaPropShape = {
keyName: PropTypes.any,
fn: PropTypes.object.isRequired,
schema: PropTypes.object,
errors: ImPropTypes.list,
required: PropTypes.bool,
description: PropTypes.any
}
@@ -20,7 +22,8 @@ const JsonSchemaDefaultProps = {
onChange: noop,
schema: {},
keyName: "",
required: false
required: false,
errors: List()
}
export class JsonSchemaForm extends Component {
@@ -29,7 +32,7 @@ export class JsonSchemaForm extends Component {
static defaultProps = JsonSchemaDefaultProps
render() {
let { schema, value, onChange, getComponent, fn } = this.props
let { schema, errors, value, onChange, getComponent, fn } = this.props
if(schema.toJS)
schema = schema.toJS()
@@ -37,7 +40,7 @@ export class JsonSchemaForm extends Component {
let { type, format="" } = schema
let Comp = (format ? getComponent(`JsonSchema_${type}_${format}`) : getComponent(`JsonSchema_${type}`)) || getComponent("JsonSchema_string")
return <Comp { ...this.props } fn={fn} getComponent={getComponent} value={value} onChange={onChange} schema={schema}/>
return <Comp { ...this.props } errors={errors} fn={fn} getComponent={getComponent} value={value} onChange={onChange} schema={schema}/>
}
}
@@ -51,14 +54,15 @@ export class JsonSchema_string extends Component {
}
onEnumChange = (val) => this.props.onChange(val)
render() {
let { getComponent, value, schema, required, description } = this.props
let { getComponent, value, schema, errors, required, description } = this.props
let enumValue = schema["enum"]
let errors = schema.errors || []
errors = errors.toJS ? errors.toJS() : []
if ( enumValue ) {
const Select = getComponent("Select")
return (<Select className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
title={ errors.length ? errors : ""}
allowedValues={ enumValue }
value={ value }
allowEmptyValue={ !required }
@@ -70,14 +74,14 @@ export class JsonSchema_string extends Component {
if (schema["type"] === "file") {
return (<Input type="file"
className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
title={ errors.length ? errors : ""}
onChange={ this.onChange }
disabled={isDisabled}/>)
}
else {
return (<Input type={ schema.format === "password" ? "password" : "text" }
className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
title={ errors.length ? errors : ""}
value={value}
placeholder={description}
onChange={ this.onChange }
@@ -131,9 +135,10 @@ export class JsonSchema_array extends PureComponent {
}
render() {
let { getComponent, required, schema, fn } = this.props
let { getComponent, required, schema, errors, fn } = this.props
errors = errors.toJS ? errors.toJS() : []
let errors = schema.errors || []
let itemSchema = fn.inferSchema(schema.items)
const JsonSchemaForm = getComponent("JsonSchemaForm")
@@ -145,7 +150,7 @@ export class JsonSchema_array extends PureComponent {
if ( enumValue ) {
const Select = getComponent("Select")
return (<Select className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
title={ errors.length ? errors : ""}
multiple={ true }
value={ value }
allowedValues={ enumValue }
@@ -160,7 +165,7 @@ export class JsonSchema_array extends PureComponent {
let schema = Object.assign({}, itemSchema)
if ( errors.length ) {
let err = errors.filter((err) => err.index === i)
if (err.length) schema.errors = [ err[0].error + i ]
if (err.length) errors = [ err[0].error + i ]
}
return (
<div key={i} className="json-schema-form-item">
@@ -182,12 +187,13 @@ export class JsonSchema_boolean extends Component {
onEnumChange = (val) => this.props.onChange(val)
render() {
let { getComponent, value, schema } = this.props
let errors = schema.errors || []
let { getComponent, value, errors, schema } = this.props
errors = errors.toJS ? errors.toJS() : []
const Select = getComponent("Select")
return (<Select className={ errors.length ? "invalid" : ""}
title={ errors.length ? errors : ""}
title={ errors.length ? errors : ""}
value={ String(value) }
allowedValues={ fromJS(schema.enum || ["true", "false"]) }
allowEmptyValue={ !this.props.required }
+1 -1
View File
@@ -44,7 +44,7 @@ export default {
let { auth, token } = payload
let parsedAuth
auth.token = token
auth.token = Object.assign({}, token)
parsedAuth = fromJS(auth)
return state.setIn( [ "authorized", parsedAuth.get("name") ], parsedAuth )
+6 -1
View File
@@ -35,7 +35,11 @@ export default class Servers extends React.Component {
if(this.props.currentServer !== nextProps.currentServer) {
// Server has changed, we may need to set default values
let currentServerDefinition = servers
.find(v => v.get("url") === nextProps.currentServer) || OrderedMap()
.find(v => v.get("url") === nextProps.currentServer)
if(!currentServerDefinition) {
return this.setServer(servers.first().get("url"))
}
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
@@ -90,6 +94,7 @@ export default class Servers extends React.Component {
getEffectiveServerValue
} = this.props
let currentServerDefinition = servers.find(v => v.get("url") === currentServer) || OrderedMap()
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
+3 -3
View File
@@ -19,7 +19,7 @@ export const LOG_REQUEST = "spec_log_request"
export const CLEAR_RESPONSE = "spec_clear_response"
export const CLEAR_REQUEST = "spec_clear_request"
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
export const UPDATE_OPERATION_VALUE = "spec_update_operation_value"
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
export const UPDATE_RESOLVED = "spec_update_resolved"
export const SET_SCHEME = "set_scheme"
@@ -150,14 +150,14 @@ export function clearValidateParams( payload ){
export function changeConsumesValue(path, value) {
return {
type: UPDATE_OPERATION_VALUE,
type: UPDATE_OPERATION_META_VALUE,
payload:{ path, value, key: "consumes_value" }
}
}
export function changeProducesValue(path, value) {
return {
type: UPDATE_OPERATION_VALUE,
type: UPDATE_OPERATION_META_VALUE,
payload:{ path, value, key: "produces_value" }
}
}
+11 -6
View File
@@ -12,7 +12,7 @@ import {
SET_REQUEST,
SET_MUTATED_REQUEST,
UPDATE_RESOLVED,
UPDATE_OPERATION_VALUE,
UPDATE_OPERATION_META_VALUE,
CLEAR_RESPONSE,
CLEAR_REQUEST,
CLEAR_VALIDATE_PARAMS,
@@ -52,8 +52,8 @@ export default {
},
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
let isXml = /xml/i.test(operation.get("consumes_value"))
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
let isXml = /xml/i.test(meta.get("consumes_value"))
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => {
@@ -68,7 +68,7 @@ export default {
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => {
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
parameters.setIn([i, "errors"], fromJS({}))
parameters.setIn([i, "errors"], fromJS([]))
}
})
})
@@ -107,12 +107,17 @@ export default {
return state.setIn( [ "mutatedRequests", path, method ], fromJSOrdered(req))
},
[UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
// path is a pathMethod tuple... can't change the name now.
let operationPath = ["resolved", "paths", ...path]
let metaPath = ["meta", "paths", ...path]
if(!state.getIn(operationPath)) {
// do nothing if the operation does not exist
return state
}
return state.setIn([...operationPath, key], fromJS(value))
return state.setIn([...metaPath, key], fromJS(value))
},
[CLEAR_RESPONSE]: (state, { payload: { path, method } } ) =>{
+23 -2
View File
@@ -306,10 +306,13 @@ export function parametersIncludeType(parameters, typeValue="") {
export function contentTypeValues(state, pathMethod) {
pathMethod = pathMethod || []
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
let producesValue = currentProducesFor(state, pathMethod)
const parameters = op.get("parameters") || new List()
const requestContentType = (
op.get("consumes_value") ? op.get("consumes_value")
meta.get("consumes_value") ? meta.get("consumes_value")
: parametersIncludeType(parameters, "file") ? "multipart/form-data"
: parametersIncludeType(parameters, "formData") ? "application/x-www-form-urlencoded"
: undefined
@@ -317,7 +320,7 @@ export function contentTypeValues(state, pathMethod) {
return fromJS({
requestContentType,
responseContentType: op.get("produces_value")
responseContentType: producesValue
})
}
@@ -327,6 +330,24 @@ export function operationConsumes(state, pathMethod) {
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
}
// Get the currently selected produces value for an operation
export function currentProducesFor(state, pathMethod) {
pathMethod = pathMethod || []
const operation = spec(state).getIn(["paths", ...pathMethod], null)
if(operation === null) {
// return nothing if the operation does not exist
return
}
const currentProducesValue = state.getIn(["meta", "paths", ...pathMethod, "produces_value"], null)
const firstProducesArrayItem = operation.getIn(["produces", 0], null)
return currentProducesValue || firstProducesArrayItem || "application/json"
}
export const operationScheme = ( state, path, method ) => {
let url = state.get("url")
let matchResult = url.match(/^([a-z][a-z0-9+\-.]*):/)
+12
View File
@@ -342,6 +342,17 @@ export function mapToList(map, keyNames="key", collectedKeys=Im.Map()) {
return list
}
export function extractFileNameFromContentDispositionHeader(value){
let responseFilename = /filename="([^;]*);?"/i.exec(value)
if (responseFilename === null) {
responseFilename = /filename=([^;]*);?/i.exec(value)
}
if (responseFilename !== null && responseFilename.length > 1) {
return responseFilename[1]
}
return null
}
// PascalCase, aka UpperCamelCase
export function pascalCase(str) {
return upperFirst(camelCase(str))
@@ -433,6 +444,7 @@ export const validateDateTime = (val) => {
}
export const validateGuid = (val) => {
val = val.toString().toLowerCase()
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
return "Value must be a Guid"
}
+1
View File
@@ -4,6 +4,7 @@
max-width: 1460px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box;
}
.opblock-tag-section
+1 -1
View File
@@ -171,4 +171,4 @@ $browser-context: 16;
animation: shake .4s 1;
border-color: $_color-delete;
background: lighten($_color-delete, 35%);
}
}
+1 -1
View File
@@ -175,7 +175,7 @@ $section-models-border-color: $gray-custom-2 !default;
$section-models-isopen-h4-border-bottom-color: $section-models-border-color !default;
$section-models-h4-font-color: $gray-500 !default;
$section-models-h4-font-color: $gray-600 !default;
$section-models-h4-background-color-hover: $gray-base !default;
$section-models-h5-font-color: $gray-500 !default;
@@ -5,9 +5,9 @@ import reducer from "corePlugins/spec/reducers"
describe("spec plugin - reducer", function(){
describe("update operation value", function() {
it("should update the operation at the specified key", () => {
const updateOperationValue = reducer["spec_update_operation_value"]
describe("update operation meta value", function() {
it("should update the operation metadata at the specified key", () => {
const updateOperationValue = reducer["spec_update_operation_meta_value"]
const state = fromJS({
resolved: {
@@ -34,7 +34,15 @@ describe("spec plugin - reducer", function(){
"paths": {
"/pet": {
"post": {
"description": "my operation",
"description": "my operation"
}
}
}
},
meta: {
paths: {
"/pet": {
post: {
"consumes_value": "application/json"
}
}
@@ -46,7 +54,7 @@ describe("spec plugin - reducer", function(){
})
it("shouldn't throw an error if we try to update the consumes_value of a null operation", () => {
const updateOperationValue = reducer["spec_update_operation_value"]
const updateOperationValue = reducer["spec_update_operation_meta_value"]
const state = fromJS({
resolved: {
+85 -5
View File
@@ -56,6 +56,13 @@ describe("spec plugin - selectors", function(){
// Given
let state = fromJS({
resolved: {
paths: {
"/one": {
get: {}
}
}
},
meta: {
paths: {
"/one": {
get: {
@@ -76,6 +83,71 @@ describe("spec plugin - selectors", function(){
})
})
it("should default to the first `produces` array value if current is not set", function(){
// Given
let state = fromJS({
resolved: {
paths: {
"/one": {
get: {
produces: [
"application/xml",
"application/whatever"
]
}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one"
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: "one",
responseContentType: "application/xml"
})
})
it("should default to `application/json` if a default produces value is not available", function(){
// Given
let state = fromJS({
resolved: {
paths: {
"/one": {
get: {}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one"
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: "one",
responseContentType: "application/json"
})
})
it("should prioritize consumes value first from an operation", function(){
// Given
let state = fromJS({
@@ -83,13 +155,21 @@ describe("spec plugin - selectors", function(){
paths: {
"/one": {
get: {
"consumes_value": "one",
"parameters": [{
"parameters": [{
"type": "file"
}],
}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one",
}
}
}
}
})
@@ -106,7 +186,7 @@ describe("spec plugin - selectors", function(){
paths: {
"/one": {
get: {
"parameters": [{
"parameters": [{
"type": "file"
}],
}
@@ -128,7 +208,7 @@ describe("spec plugin - selectors", function(){
paths: {
"/one": {
get: {
"parameters": [{
"parameters": [{
"type": "formData"
}],
}
@@ -143,7 +223,7 @@ describe("spec plugin - selectors", function(){
expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
})
it("should be ok, if no operation found", function(){
it("should return nothing, if the operation does not exist", function(){
// Given
let state = fromJS({ })
+23 -1
View File
@@ -18,7 +18,8 @@ import {
getAcceptControllingResponse,
createDeepLinkPath,
escapeDeepLinkPath,
sanitizeUrl
sanitizeUrl,
extractFileNameFromContentDispositionHeader
} from "core/utils"
import win from "core/window"
@@ -90,6 +91,26 @@ describe("utils", function() {
})
describe("extractFileNameFromContentDispositionHeader", function(){
it("should extract quoted filename", function(){
let cdHeader = "attachment; filename=\"file name.jpg\""
let expectedResult = "file name.jpg"
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
it("should extract filename", function(){
let cdHeader = "attachment; filename=filename.jpg"
let expectedResult = "filename.jpg"
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
it("should not extract filename and return null", function(){
let cdHeader = "attachment; no file name provided"
let expectedResult = null
expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
})
})
describe("validateMaximum", function() {
let errorMessage = "Value must be less than Maximum"
@@ -238,6 +259,7 @@ describe("utils", function() {
it("doesn't return for valid guid", function() {
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
})
it("returns a message for invalid input'", function() {