Merge branch 'master' of github.com:swagger-api/swagger-ui into ft/performance

This commit is contained in:
Owen Conti
2017-10-22 12:37:10 -06:00
72 changed files with 1515 additions and 366 deletions

View File

@@ -22,7 +22,7 @@ export default {
securities.entrySeq().forEach( ([ key, security ]) => {
let type = security.getIn(["schema", "type"])
if ( type === "apiKey" ) {
if ( type === "apiKey" || type === "http" ) {
map = map.set(key, security)
} else if ( type === "basic" ) {
let username = security.getIn(["value", "username"])

View File

@@ -10,7 +10,7 @@ export const shownDefinitions = createSelector(
export const definitionsToAuthorize = createSelector(
state,
() =>( { specSelectors } ) => {
() => ( { specSelectors } ) => {
let definitions = specSelectors.securityDefinitions()
let list = List()
@@ -27,7 +27,7 @@ export const definitionsToAuthorize = createSelector(
)
export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors } ) => {
export const getDefinitionsByNames = ( state, securities ) => ( { specSelectors } ) => {
let securityDefinitions = specSelectors.securityDefinitions()
let result = List()
@@ -64,7 +64,7 @@ export const authorized = createSelector(
)
export const isAuthorized = ( state, securities ) =>( { authSelectors } ) => {
export const isAuthorized = ( state, securities ) => ( { authSelectors } ) => {
let authorized = authSelectors.authorized()
if(!List.isList(securities)) {

View File

@@ -7,13 +7,16 @@ export default function downloadUrlPlugin (toolbox) {
let { fn } = toolbox
const actions = {
download: (url)=> ({ errActions, specSelectors, specActions }) => {
download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => {
let { fetch } = fn
const config = getConfigs()
url = url || specSelectors.url()
specActions.updateLoadingStatus("loading")
fetch({
url,
loadSpec: true,
requestInterceptor: config.requestInterceptor || (a => a),
responseInterceptor: config.responseInterceptor || (a => a),
credentials: "same-origin",
headers: {
"Accept": "application/json,*/*"

View File

@@ -0,0 +1,60 @@
import { createSelector } from "reselect"
import { List, Map, fromJS } from "immutable"
import { isOAS3 as isOAS3Helper } from "../helpers"
// Helpers
const state = state => state
function onlyOAS3(selector) {
return (ori, system) => (state, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(system, ...args)
} else {
return ori(...args)
}
}
}
export const definitionsToAuthorize = onlyOAS3(createSelector(
state,
({ specSelectors }) => {
// Coerce our OpenAPI 3.0 definitions into monoflow definitions
// that look like Swagger2 definitions.
let definitions = specSelectors.securityDefinitions()
let list = List()
definitions.entrySeq().forEach( ([ defName, definition ]) => {
const type = definition.get("type")
if(type === "oauth2") {
definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => {
let translatedDef = fromJS({
flow: flowKey,
authorizationUrl: flowVal.get("authorizationUrl"),
tokenUrl: flowVal.get("tokenUrl"),
scopes: flowVal.get("scopes"),
type: definition.get("type")
})
list = list.push(new Map({
[defName]: translatedDef.filter((v) => {
// filter out unset values, sometimes `authorizationUrl`
// and `tokenUrl` come out as `undefined` in the data
return v !== undefined
})
}))
})
}
if(type === "http" || type === "apiKey") {
list = list.push(new Map({
[defName]: definition
}))
}
})
return list
}
))

View File

@@ -0,0 +1,134 @@
import React from "react"
import PropTypes from "prop-types"
export default class HttpAuth extends React.Component {
static propTypes = {
authorized: PropTypes.object,
getComponent: PropTypes.func.isRequired,
errSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func
}
constructor(props, context) {
super(props, context)
let { name, schema } = this.props
let value = this.getValue()
this.state = {
name: name,
schema: schema,
value: value
}
}
getValue () {
let { name, authorized } = this.props
return authorized && authorized.getIn([name, "value"])
}
onChange =(e) => {
let { onChange } = this.props
let { value, name } = e.target
let newValue = this.state.value || {}
if(name) {
newValue[name] = value
} else {
newValue = value
}
this.setState({ value: newValue }, () => onChange(this.state))
}
render() {
let { schema, getComponent, errSelectors, name } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
const AuthError = getComponent("authError")
const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true)
const scheme = schema.get("scheme")
let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
if(scheme === "basic") {
let username = value ? value.get("username") : null
return <div>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(http, Basic)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ username && <h6>Authorized</h6> }
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<label>Username:</label>
{
username ? <code> { username } </code>
: <Col><Input type="text" required="required" name="username" onChange={ this.onChange }/></Col>
}
</Row>
<Row>
<label>Password:</label>
{
username ? <code> ****** </code>
: <Col><Input required="required"
autoComplete="new-password"
name="password"
type="password"
onChange={ this.onChange }/></Col>
}
</Row>
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
}
if(scheme === "bearer") {
return (
<div>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(http, Bearer)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<p>In: <code>{ schema.get("in") }</code></p>
</Row>
<Row>
<label>Value:</label>
{
value ? <code> ****** </code>
: <Col><Input type="text" onChange={ this.onChange }/></Col>
}
</Row>
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
)
}
return <div>
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
</div>
}
}

View File

@@ -3,9 +3,11 @@ import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx"
import Servers from "./servers"
import RequestBodyEditor from "./request-body-editor"
import HttpAuth from "./http-auth"
export default {
Callbacks,
HttpAuth,
RequestBody,
Servers,
RequestBodyEditor,

View File

@@ -6,6 +6,7 @@ import { OrderedMap } from "immutable"
const RequestBody = ({
requestBody,
getComponent,
getConfigs,
specSelectors,
contentType,
isExecute,
@@ -27,6 +28,7 @@ const RequestBody = ({
}
<ModelExample
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
expandDepth={1}
isExecute={isExecute}
@@ -46,6 +48,7 @@ const RequestBody = ({
RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string.isRequired,
isExecute: PropTypes.bool.isRequired,

View File

@@ -1,6 +1,7 @@
// import reducers from "./reducers"
// import * as actions from "./actions"
import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
import * as authWrapSelectors from "./auth-extensions/wrap-selectors"
import * as specSelectors from "./spec-extensions/selectors"
import components from "./components"
import wrapComponents from "./wrap-components"
@@ -17,6 +18,9 @@ export default function() {
wrapSelectors: specWrapSelectors,
selectors: specSelectors
},
auth: {
wrapSelectors: authWrapSelectors
},
oas3: {
actions: oas3Actions,
reducers: oas3Reducers,

View File

@@ -48,6 +48,11 @@ export const definitions = onlyOAS3(createSelector(
spec => spec.getIn(["components", "schemas"]) || Map()
))
export const securityDefinitions = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["components", "securitySchemes"]) || Map()
))
export const host = OAS3NullSelector
export const basePath = OAS3NullSelector
export const consumes = OAS3NullSelector

View File

@@ -0,0 +1,23 @@
import React from "react"
import { OAS3ComponentWrapFactory } from "../helpers"
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
const {
schema, getComponent, errSelectors, authorized, onAuthChange, name
} = props
const HttpAuth = getComponent("HttpAuth")
const type = schema.get("type")
if(type === "http") {
return <HttpAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange }/>
} else {
return <Ori {...props} />
}
})

View File

@@ -1,4 +1,5 @@
import Markdown from "./markdown"
import AuthItem from "./auth-item"
import parameters from "./parameters"
import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge"
@@ -6,6 +7,7 @@ import Model from "./model"
export default {
Markdown,
AuthItem,
parameters,
VersionStamp,
model: Model,

View File

@@ -1,11 +1,32 @@
import React from "react"
import PropTypes from "prop-types"
import ReactMarkdown from "react-markdown"
import { Parser, HtmlRenderer } from "commonmark"
import { OAS3ComponentWrapFactory } from "../helpers"
import { sanitizer } from "core/components/providers/markdown"
export default OAS3ComponentWrapFactory(({ source }) => { return source ? (
<ReactMarkdown
source={sanitizer(source)}
className={"renderedMarkdown"}
/>
) : null})
export const Markdown = ({ source }) => {
if ( source ) {
const parser = new Parser()
const writer = new HtmlRenderer()
const html = writer.render(parser.parse(source || ""))
const sanitized = sanitizer(html)
if ( !source || !html || !sanitized ) {
return null
}
return (
<ReactMarkdown
source={sanitized}
className={"renderedMarkdown"}
/>
)
}
return null
}
Markdown.propTypes = {
source: PropTypes.string
}
export default OAS3ComponentWrapFactory(Markdown)

View File

@@ -49,7 +49,7 @@ class Parameters extends Component {
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), value, isXml)
changeParam( onChangeKey, param.get("name"), param.get("in"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {

View File

@@ -130,17 +130,20 @@ export const formatIntoYaml = () => ({specActions, specSelectors}) => {
}
}
export function changeParam( path, paramName, value, isXml ){
export function changeParam( path, paramName, paramIn, value, isXml ){
return {
type: UPDATE_PARAM,
payload:{ path, value, paramName, isXml }
payload:{ path, value, paramName, paramIn, isXml }
}
}
export function validateParams( payload ){
export const validateParams = ( payload, isOAS3 ) =>{
return {
type: VALIDATE_PARAMS,
payload:{ pathMethod: payload }
payload:{
pathMethod: payload,
isOAS3
}
}
}
@@ -206,7 +209,6 @@ export const executeRequest = (req) =>
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString()
if(op && op.operationId) {
req.operationId = op.operationId
} else if(op && pathName && method) {

View File

@@ -40,9 +40,10 @@ export default {
},
[UPDATE_PARAM]: ( state, {payload} ) => {
let { path, paramName, value, isXml } = payload
let { path, paramName, paramIn, value, isXml } = payload
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
const index = parameters.findIndex(p => p.get( "name" ) === paramName )
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
if (!(value instanceof win.File)) {
value = fromJSOrdered( value )
}
@@ -50,14 +51,14 @@ export default {
})
},
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
let isXml = /xml/i.test(operation.get("consumes_value"))
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => {
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
let errors = validateParam(parameters.get(i), isXml)
let errors = validateParam(parameters.get(i), isXml, isOAS3)
parameters.setIn([i, "errors"], fromJS(errors))
}
})

View File

@@ -260,10 +260,10 @@ export const allowTryItOutFor = () => {
}
// Get the parameter value by parameter name
export function getParameter(state, pathMethod, name) {
export function getParameter(state, pathMethod, name, inType) {
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.filter( (p) => {
return Map.isMap(p) && p.get("name") === name
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
}).first()
}
@@ -280,7 +280,7 @@ export function parameterValues(state, pathMethod, isXml) {
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
return params.reduce( (hash, p) => {
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
return hash.set(p.get("name"), value)
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
}, fromJS({}))
}

View File

@@ -13,3 +13,7 @@ export const executeRequest = (ori, { specActions }) => (req) => {
specActions.logRequest(req)
return ori(req)
}
export const validateParams = (ori, { specSelectors }) => (req) => {
return ori(req, specSelectors.isOAS3())
}