Compare commits

..
Author SHA1 Message Date
Kyle Shockey 1e2baeb91b this.downloadJSON -> this.downloadText
we're always saving as `.txt`, so JSON is a misnomer
2018-04-12 17:56:35 -07:00
Kyle Shockey ac43ee5feb center "Download" text in button 2018-04-12 17:56:07 -07:00
Kyle Shockey 64b83738ce prevent HighlightCode from scrolling entire document 2018-04-12 17:52:27 -07:00
kyle 0cdd54c24d Merge branch 'master' into auto-hide-contents 2018-04-12 17:17:20 -07:00
giancarlokc a3ef0d609f CSS fix. 2018-04-05 23:31:35 -07:00
giancarlokc 80e994ef79 Code clean up. 2018-04-05 23:21:00 -07:00
giancarlokc 115c5305ea Removed collapsing, added scrolling. 2018-04-05 23:16:46 -07:00
giancarlokc cd065d674c Merge branch 'auto-hide-contents' of https://github.com/giancarlokc/swagger-ui into auto-hide-contents 2018-04-05 22:39:19 -07:00
giancarlokc 6f061d8de5 Fix dist 2018-04-05 22:38:43 -07:00
Giancarlo Klemm Camilo 11eb6ef002 Merge branch 'master' into auto-hide-contents 2018-04-05 22:38:11 -07:00
Graeme 177464e06f Fix the angry linter 2018-04-05 21:07:07 -07:00
Graeme 6173d3b966 Better downloading
now downloads file on button click
2018-04-05 20:51:12 -07:00
Graeme 485d04ff3c Added basic downloading
Slightly broken
2018-04-03 15:21:05 -07:00
giancarlokc 0b8dab2d5e Auto hidding content that is longer than 600 characters long. 2018-04-02 23:56:26 -07:00
18 changed files with 54 additions and 248 deletions
+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 Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | ----- ------------------ | ------------ | -------------------------- | -----
3.13.4 | 2018-04-15 | 2.0, 3.0 | [tag v3.13.4](https://github.com/swagger-api/swagger-ui/tree/v3.13.4) 3.13.3 | 2018-03-23 | 2.0, 3.0 | [tag v3.13.3](https://github.com/swagger-api/swagger-ui/tree/v3.13.3)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21) 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.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.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
File diff suppressed because one or more lines are too long
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
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "swagger-ui", "name": "swagger-ui",
"version": "3.13.4", "version": "3.13.3",
"main": "dist/swagger-ui.js", "main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git", "repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [ "contributors": [
@@ -52,7 +52,6 @@
"getbase": "^2.8.2", "getbase": "^2.8.2",
"ieee754": "^1.1.8", "ieee754": "^1.1.8",
"immutable": "^3.x.x", "immutable": "^3.x.x",
"js-file-download": "^0.4.1",
"js-yaml": "^3.5.5", "js-yaml": "^3.5.5",
"lodash": "4.17.5", "lodash": "4.17.5",
"matcher": "^0.1.2", "matcher": "^0.1.2",
@@ -84,7 +83,7 @@
"scroll-to-element": "^2.0.0", "scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0", "serialize-error": "2.0.0",
"shallowequal": "0.2.2", "shallowequal": "0.2.2",
"swagger-client": "^3.7.0", "swagger-client": "^3.6.0",
"url-parse": "^1.1.8", "url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1", "whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1", "worker-loader": "^0.7.1",
@@ -117,6 +116,7 @@
"eslint-plugin-react": "^7.1.0", "eslint-plugin-react": "^7.1.0",
"extract-text-webpack-plugin": "^2.1.2", "extract-text-webpack-plugin": "^2.1.2",
"file-loader": "0.11.2", "file-loader": "0.11.2",
"file-saver": "^1.3.8",
"git-describe": "^4.0.1", "git-describe": "^4.0.1",
"imports-loader": "0.7.1", "imports-loader": "0.7.1",
"json-loader": "0.5.4", "json-loader": "0.5.4",
+6 -10
View File
@@ -1,13 +1,12 @@
import React, { Component } from "react" import React, { Component } from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { highlight } from "core/utils" import { highlight } from "core/utils"
import saveAs from "js-file-download" import { saveAs } from "file-saver"
export default class HighlightCode extends Component { export default class HighlightCode extends Component {
static propTypes = { static propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
className: PropTypes.string, className: PropTypes.string
downloadable: PropTypes.bool
} }
componentDidMount() { componentDidMount() {
@@ -23,7 +22,8 @@ export default class HighlightCode extends Component {
} }
downloadText = () => { downloadText = () => {
saveAs(this.props.value, "response.txt") let fileToSave = new Blob([this.props.value], {type: "text/plain"})
saveAs(fileToSave, "response")
} }
preventYScrollingBeyondElement = (e) => { preventYScrollingBeyondElement = (e) => {
@@ -45,16 +45,12 @@ export default class HighlightCode extends Component {
} }
render () { render () {
let { value, className, downloadable } = this.props let { value, className } = this.props
className = className || "" className = className || ""
return ( return (
<div className="highlight-code"> <div className="highlight-code">
{ !downloadable ? null : <div className="download-contents" onClick={this.downloadText}>Download</div>
<div className="download-contents" onClick={this.downloadText}>
Download
</div>
}
<pre <pre
ref={this.initializeComponent} ref={this.initializeComponent}
onWheel={this.preventYScrollingBeyondElement} onWheel={this.preventYScrollingBeyondElement}
+1 -1
View File
@@ -197,7 +197,7 @@ export default class ParameterRow extends Component {
getConfigs={ getConfigs } getConfigs={ getConfigs }
isExecute={ isExecute } isExecute={ isExecute }
specSelectors={ specSelectors } specSelectors={ specSelectors }
schema={ param.get("schema") } schema={ schema }
example={ bodyParam }/> example={ bodyParam }/>
: null : null
} }
+6 -55
View File
@@ -5,10 +5,7 @@ import lowerCase from "lodash/lowerCase"
import { extractFileNameFromContentDispositionHeader } from "core/utils" import { extractFileNameFromContentDispositionHeader } from "core/utils"
import win from "core/window" import win from "core/window"
export default class ResponseBody extends React.PureComponent { export default class ResponseBody extends React.Component {
state = {
parsedContent: null
}
static propTypes = { static propTypes = {
content: PropTypes.any.isRequired, content: PropTypes.any.isRequired,
@@ -18,39 +15,8 @@ export default class ResponseBody extends React.PureComponent {
url: PropTypes.string url: PropTypes.string
} }
updateParsedContent = (prevContent) => {
const { content } = this.props
if(prevContent === content) {
return
}
if(content && content instanceof Blob) {
var reader = new FileReader()
reader.onload = () => {
this.setState({
parsedContent: reader.result
})
}
reader.readAsText(content)
} else {
this.setState({
parsedContent: content.toString()
})
}
}
componentDidMount() {
this.updateParsedContent(null)
}
componentDidUpdate(prevProps) {
this.updateParsedContent(prevProps.content)
}
render() { render() {
let { content, contentType, url, headers={}, getComponent } = this.props let { content, contentType, url, headers={}, getComponent } = this.props
const { parsedContent } = this.state
const HighlightCode = getComponent("highlightCode") const HighlightCode = getComponent("highlightCode")
let body, bodyEl let body, bodyEl
url = url || "" url = url || ""
@@ -100,7 +66,7 @@ export default class ResponseBody extends React.PureComponent {
body = "can't parse JSON. Raw result:\n\n" + content body = "can't parse JSON. Raw result:\n\n" + content
} }
bodyEl = <HighlightCode downloadable value={ body } /> bodyEl = <HighlightCode value={ body } />
// XML // XML
} else if (/xml/i.test(contentType)) { } else if (/xml/i.test(contentType)) {
@@ -108,11 +74,11 @@ export default class ResponseBody extends React.PureComponent {
textNodesOnSameLine: true, textNodesOnSameLine: true,
indentor: " " indentor: " "
}) })
bodyEl = <HighlightCode downloadable value={ body } /> bodyEl = <HighlightCode value={ body } />
// HTML or Plain Text // HTML or Plain Text
} else if (lowerCase(contentType) === "text/html" || /text\/plain/.test(contentType)) { } else if (lowerCase(contentType) === "text/html" || /text\/plain/.test(contentType)) {
bodyEl = <HighlightCode downloadable value={ content } /> bodyEl = <HighlightCode value={ content } />
// Image // Image
} else if (/^image\//i.test(contentType)) { } else if (/^image\//i.test(contentType)) {
@@ -126,25 +92,10 @@ export default class ResponseBody extends React.PureComponent {
} else if (/^audio\//i.test(contentType)) { } else if (/^audio\//i.test(contentType)) {
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre> bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
} else if (typeof content === "string") { } else if (typeof content === "string") {
bodyEl = <HighlightCode downloadable value={ content } /> bodyEl = <HighlightCode value={ content } />
} else if ( content.size > 0 ) { } else if ( content.size > 0 ) {
// We don't know the contentType, but there was some content returned // We don't know the contentType, but there was some content returned
if(parsedContent) { bodyEl = <div>Unknown response type</div>
// We were able to squeeze something out of content
// in `updateParsedContent`, so let's display it
bodyEl = <div>
<p className="i">
Unrecognized response type; displaying content as text.
</p>
<HighlightCode downloadable value={ parsedContent } />
</div>
} else {
// Give up
bodyEl = <p className="i">
Unrecognized response type; unable to display.
</p>
}
} else { } else {
// We don't know the contentType and there was no content returned // We don't know the contentType and there was no content returned
bodyEl = null bodyEl = null
+2 -2
View File
@@ -37,12 +37,12 @@ export default class Schemes extends React.Component {
} }
render() { render() {
let { schemes, currentScheme } = this.props let { schemes } = this.props
return ( return (
<label htmlFor="schemes"> <label htmlFor="schemes">
<span className="schemes-title">Schemes</span> <span className="schemes-title">Schemes</span>
<select onChange={ this.onChange } value={currentScheme}> <select onChange={ this.onChange }>
{ schemes.valueSeq().map( { schemes.valueSeq().map(
( scheme ) => <option value={ scheme } key={ scheme }>{ scheme }</option> ( scheme ) => <option value={ scheme } key={ scheme }>{ scheme }</option>
).toArray()} ).toArray()}
@@ -33,8 +33,7 @@ export default class HttpAuth extends React.Component {
let { onChange } = this.props let { onChange } = this.props
let { value, name } = e.target let { value, name } = e.target
let newValue = Object.assign({}, this.state.value) let newValue = this.state.value || {}
if(name) { if(name) {
newValue[name] = value newValue[name] = value
} else { } else {
@@ -54,7 +53,7 @@ export default class HttpAuth extends React.Component {
const Markdown = getComponent( "Markdown" ) const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true) const JumpToPath = getComponent("JumpToPath", true)
const scheme = (schema.get("scheme") || "").toLowerCase() const scheme = schema.get("scheme")
let value = this.getValue() let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name) let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
@@ -126,7 +125,7 @@ export default class HttpAuth extends React.Component {
) )
} }
return <div> return <div>
<em><b>{name}</b> HTTP authentication: unsupported scheme {`'${scheme}'`}</em> <em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
</div> </div>
} }
} }
-1
View File
@@ -632,7 +632,6 @@
> .microlight { > .microlight {
overflow-y: auto; overflow-y: auto;
max-height: 400px; max-height: 400px;
min-height: 6em;
} }
} }
-44
View File
@@ -1,44 +0,0 @@
describe("bug #4196: HTTP basic auth credential retention", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input", 5000)
.pause(80)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4196.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("should display the most recent auth data across modal close/opens", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.click("button.btn.authorize") // Open modal
.waitForElementVisible("section>input", 5000)
.setValue("section>input", "aaa") // Set user
.waitForElementVisible(`section>input[type="password"]`, 5000)
.setValue(`section>input[type="password"]`, "aaa") // Set password
.click(".auth-btn-wrapper button:nth-child(1)") // Click Authorize
.assert.containsText("div.wrapper:nth-child(4)>code", "aaa")
.click(".auth-btn-wrapper button:nth-child(2)") // Close modal
.pause(50)
.click("button.btn.authorize") // Open modal
.pause(50)
.click(".auth-btn-wrapper button:nth-child(1)") // Logout
.waitForElementVisible("section>input", 5000)
.setValue("section>input", "bbb") // Set user
.waitForElementVisible(`section>input[type="password"]`, 5000)
.setValue(`section>input[type="password"]`, "bbb") // Set password
.click(".auth-btn-wrapper button:nth-child(1)") // Click Authorize
.pause(5000)
.assert.containsText("div.wrapper:nth-child(4)>code", "bbb")
client.end()
})
})
+9 -19
View File
@@ -71,16 +71,6 @@ describe("render pet api container", function () {
client.end() client.end()
}) })
it("Testing post /pet api model values", function (client) {
apiWrapper.waitForElementVisible("@petOperationPostContainer", 5000)
.click("@petOperationPostCollpase")
.waitForElementVisible("@petOperationPostCollapseContainer", 5000)
.click("a.tablinks[data-name=model]")
.assert.containsText("span.model-title > .model-title__text", `Pet`)
client.end()
})
it("render put /pet api container", function (client) { it("render put /pet api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000) apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.assert.containsText("@petOperationPutTitle", "/pet") .assert.containsText("@petOperationPutTitle", "/pet")
@@ -91,10 +81,10 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationPutExecuteBtn", 1000) .waitForElementVisible("@petOperationPutExecuteBtn", 1000)
.click("@petOperationPutTryBtn") .click("@petOperationPutTryBtn")
.assert.cssClassNotPresent("@petOperationPutTryBtn", "cancel") .assert.cssClassNotPresent("@petOperationPutTryBtn", "cancel")
client.end() client.end()
}) })
it("Testing put /pet api Mock data", function (client) { it("Testing put /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000) apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.click("@petOperationPutCollpase") .click("@petOperationPutCollpase")
@@ -125,7 +115,7 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000) .waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000)
.click("@petOperationGetByTagTryBtn") .click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel") .assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end() client.end()
}) })
@@ -148,7 +138,7 @@ describe("render pet api container", function () {
client.end() client.end()
}) })
it("render get by ID /pet/{petId} api container", function (client) { it("render get by ID /pet/{petId} api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", 5000) apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", 5000)
.assert.containsText("@petOperationGetByIdTitle", "/pet/{petId}") .assert.containsText("@petOperationGetByIdTitle", "/pet/{petId}")
@@ -158,7 +148,7 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000) .waitForElementVisible("@petOperationGetByTagExecuteBtn", 1000)
.click("@petOperationGetByTagTryBtn") .click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel") .assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end() client.end()
}) })
@@ -177,7 +167,7 @@ describe("render pet api container", function () {
.assert.attributeEquals("@petOperationGetByIdParameter", "title", "Value must be an integer") .assert.attributeEquals("@petOperationGetByIdParameter", "title", "Value must be an integer")
.click("@petOperationGetByTagTryBtn") .click("@petOperationGetByTagTryBtn")
.assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel") .assert.cssClassNotPresent("@petOperationGetByTagTryBtn", "cancel")
client.end() client.end()
}) })
@@ -190,10 +180,10 @@ describe("render pet api container", function () {
.waitForElementVisible("@petOperationDeleteExecuteBtn", 1000) .waitForElementVisible("@petOperationDeleteExecuteBtn", 1000)
.click("@petOperationDeleteTryBtn") .click("@petOperationDeleteTryBtn")
.assert.cssClassNotPresent("@petOperationDeleteTryBtn", "cancel") .assert.cssClassNotPresent("@petOperationDeleteTryBtn", "cancel")
client.end() client.end()
}) })
it("Testing delete /pet api Mock data", function (client) { it("Testing delete /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationDeleteContainer", 3000) apiWrapper.waitForElementVisible("@petOperationDeleteContainer", 3000)
.click("@petOperationDeleteCollpase") .click("@petOperationDeleteCollpase")
@@ -213,4 +203,4 @@ describe("render pet api container", function () {
client.end() client.end()
}) })
}) })
-85
View File
@@ -1,85 +0,0 @@
openapi: 3.0.0
info:
title: Demo API
description: First test
termsOfService: 'http://demo.io/terms-of-service/'
contact:
name: Demo Support
email: support@demo.io
version: 1.0.0
servers:
- url: '{server}/v1'
variables:
server:
default: https://api.demo.io
description: the API endpoint
paths:
/session:
put:
summary: Returns a new authentication token
tags:
- session
security:
- basicAuth: []
responses:
'201':
description: A session object
content:
application/json:
schema:
allOf:
- type: object
properties:
user_id:
type: string
format: uuid
readOnly: true
example: 110e8400-e29b-11d4-a716-446655440000
- $ref: '#/components/schemas/Session'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
basicAuth:
type: http
scheme: basic
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Session:
required:
- token
properties:
token:
type: string
readOnly: true
example: >-
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE
Error:
required:
- message
properties:
message:
description: a human readable message explaining the error
type: string
reason:
description: a functionnal key about the error
type: string
responses:
Unauthorized:
description: Not authenticated
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'