@@ -75,7 +75,7 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "3.0.20",
|
||||
"swagger-client": "swagger-api/swagger-js#ft/oas3-tio",
|
||||
"url-parse": "^1.1.8",
|
||||
"whatwg-fetch": "0.11.1",
|
||||
"worker-loader": "^0.7.1",
|
||||
|
||||
@@ -8,6 +8,8 @@ export default class BaseLayout extends React.Component {
|
||||
errActions: PropTypes.object.isRequired,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
@@ -19,7 +21,14 @@ export default class BaseLayout extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { specSelectors, specActions, getComponent, layoutSelectors } = this.props
|
||||
let {
|
||||
specSelectors,
|
||||
specActions,
|
||||
getComponent,
|
||||
layoutSelectors,
|
||||
oas3Selectors,
|
||||
oas3Actions
|
||||
} = this.props
|
||||
|
||||
let info = specSelectors.info()
|
||||
let url = specSelectors.url()
|
||||
@@ -28,6 +37,7 @@ export default class BaseLayout extends React.Component {
|
||||
let securityDefinitions = specSelectors.securityDefinitions()
|
||||
let externalDocs = specSelectors.externalDocs()
|
||||
let schemes = specSelectors.schemes()
|
||||
let servers = specSelectors.servers()
|
||||
|
||||
let Info = getComponent("info")
|
||||
let Operations = getComponent("operations", true)
|
||||
@@ -35,6 +45,7 @@ export default class BaseLayout extends React.Component {
|
||||
let AuthorizeBtn = getComponent("authorizeBtn", true)
|
||||
let Row = getComponent("Row")
|
||||
let Col = getComponent("Col")
|
||||
let Servers = getComponent("Servers")
|
||||
let Errors = getComponent("errors", true)
|
||||
|
||||
let isLoading = specSelectors.loadingStatus() === "loading"
|
||||
@@ -82,6 +93,22 @@ export default class BaseLayout extends React.Component {
|
||||
</div>
|
||||
) : null }
|
||||
|
||||
{ servers && servers.size ? (
|
||||
<div className="server-container">
|
||||
<Col className="servers wrapper" mobile={12}>
|
||||
<Servers
|
||||
servers={servers}
|
||||
currentServer={oas3Selectors.selectedServer()}
|
||||
setSelectedServer={oas3Actions.setSelectedServer}
|
||||
setServerVariableValue={oas3Actions.setServerVariableValue}
|
||||
getServerVariable={oas3Selectors.serverVariableValue}
|
||||
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
|
||||
/>
|
||||
</Col>
|
||||
</div>
|
||||
|
||||
) : null}
|
||||
|
||||
{
|
||||
filter === null || filter === false ? null :
|
||||
<div className="filter-container">
|
||||
|
||||
35
src/core/plugins/oas3/actions.js
Normal file
35
src/core/plugins/oas3/actions.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Actions conform to FSA (flux-standard-actions)
|
||||
// {type: string,payload: Any|Error, meta: obj, error: bool}
|
||||
|
||||
export const UPDATE_SELECTED_SERVER = "oas3_set_servers"
|
||||
export const UPDATE_REQUEST_BODY_VALUE = "oas3_set_request_body_value"
|
||||
export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
|
||||
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
|
||||
|
||||
export function setSelectedServer (selectedServerUrl) {
|
||||
return {
|
||||
type: UPDATE_SELECTED_SERVER,
|
||||
payload: selectedServerUrl
|
||||
}
|
||||
}
|
||||
|
||||
export function setRequestBodyValue ({ value, pathMethod }) {
|
||||
return {
|
||||
type: UPDATE_REQUEST_BODY_VALUE,
|
||||
payload: { value, pathMethod }
|
||||
}
|
||||
}
|
||||
|
||||
export function setRequestContentType ({ value, pathMethod }) {
|
||||
return {
|
||||
type: UPDATE_REQUEST_CONTENT_TYPE,
|
||||
payload: { value, pathMethod }
|
||||
}
|
||||
}
|
||||
|
||||
export function setServerVariableValue ({ server, key, val }) {
|
||||
return {
|
||||
type: UPDATE_SERVER_VARIABLE_VALUE,
|
||||
payload: { server, key, val }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import Callbacks from "./callbacks"
|
||||
import RequestBody from "./request-body"
|
||||
import OperationLink from "./operation-link.jsx"
|
||||
import Servers from "./servers"
|
||||
import RequestBodyEditor from "./request-body-editor"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
RequestBody,
|
||||
Servers,
|
||||
RequestBodyEditor,
|
||||
operationLink: OperationLink
|
||||
}
|
||||
|
||||
114
src/core/plugins/oas3/components/request-body-editor.jsx
Normal file
114
src/core/plugins/oas3/components/request-body-editor.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import { getSampleSchema } from "core/utils"
|
||||
|
||||
const NOOP = Function.prototype
|
||||
|
||||
export default class RequestBodyEditor extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
requestBody: PropTypes.object.isRequired,
|
||||
mediaType: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
isExecute: PropTypes.bool,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
mediaType: "application/json",
|
||||
requestBody: fromJS({}),
|
||||
onChange: NOOP,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
this.state = {
|
||||
isEditBox: false,
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setValueToSample.call(this)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.props.mediaType !== nextProps.mediaType) {
|
||||
// media type was changed
|
||||
this.setValueToSample(nextProps.mediaType)
|
||||
}
|
||||
|
||||
if(!this.props.isExecute && nextProps.isExecute) {
|
||||
// we just entered execute mode,
|
||||
// so enable editing for convenience
|
||||
this.setState({ isEditBox: true })
|
||||
}
|
||||
}
|
||||
|
||||
setValueToSample = (explicitMediaType) => {
|
||||
this.onChange(this.sample(explicitMediaType))
|
||||
}
|
||||
|
||||
sample = (explicitMediaType) => {
|
||||
let { requestBody, mediaType } = this.props
|
||||
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
|
||||
|
||||
return getSampleSchema(schema, explicitMediaType || mediaType, {
|
||||
includeWriteOnly: true
|
||||
})
|
||||
}
|
||||
|
||||
onChange = (value) => {
|
||||
this.setState({value})
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
handleOnChange = e => {
|
||||
const { mediaType } = this.props
|
||||
const isJson = /json/i.test(mediaType)
|
||||
const inputValue = isJson ? e.target.value.trim() : e.target.value
|
||||
|
||||
this.onChange(inputValue)
|
||||
}
|
||||
|
||||
toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))
|
||||
|
||||
render() {
|
||||
let {
|
||||
isExecute,
|
||||
getComponent,
|
||||
} = this.props
|
||||
|
||||
const Button = getComponent("Button")
|
||||
const TextArea = getComponent("TextArea")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
|
||||
let { value, isEditBox } = this.state
|
||||
|
||||
return (
|
||||
<div className="body-param">
|
||||
{
|
||||
isEditBox && isExecute
|
||||
? <TextArea className={"body-param__text"} value={value} onChange={ this.handleOnChange }/>
|
||||
: (value && <HighlightCode className="body-param__example"
|
||||
value={ value }/>)
|
||||
}
|
||||
<div className="body-param-options">
|
||||
{
|
||||
!isExecute ? null
|
||||
: <div className="body-param-edit">
|
||||
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
|
||||
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,18 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { OrderedMap } from "immutable"
|
||||
import { getSampleSchema } from "core/utils"
|
||||
|
||||
|
||||
const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) => {
|
||||
const RequestBody = ({
|
||||
requestBody,
|
||||
getComponent,
|
||||
specSelectors,
|
||||
contentType,
|
||||
isExecute,
|
||||
onChange
|
||||
}) => {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
||||
|
||||
const requestBodyDescription = (requestBody && requestBody.get("description")) || null
|
||||
const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap()
|
||||
@@ -16,10 +21,6 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
|
||||
|
||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||
|
||||
const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType, {
|
||||
includeWriteOnly: true
|
||||
})
|
||||
|
||||
return <div>
|
||||
{ requestBodyDescription &&
|
||||
<Markdown source={requestBodyDescription} />
|
||||
@@ -28,8 +29,16 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={1}
|
||||
isExecute={isExecute}
|
||||
schema={mediaTypeValue.get("schema")}
|
||||
example={<HighlightCode value={sampleSchema} />}
|
||||
example={<RequestBodyEditor
|
||||
requestBody={requestBody}
|
||||
onChange={onChange}
|
||||
mediaType={contentType}
|
||||
getComponent={getComponent}
|
||||
isExecute={isExecute}
|
||||
specSelectors={specSelectors}
|
||||
/>}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
@@ -38,7 +47,9 @@ RequestBody.propTypes = {
|
||||
requestBody: ImPropTypes.orderedMap.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
contentType: PropTypes.string.isRequired
|
||||
contentType: PropTypes.string.isRequired,
|
||||
isExecute: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default RequestBody
|
||||
|
||||
155
src/core/plugins/oas3/components/servers.jsx
Normal file
155
src/core/plugins/oas3/components/servers.jsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import React from "react"
|
||||
import { OrderedMap } from "immutable"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class Servers extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
servers: ImPropTypes.list.isRequired,
|
||||
currentServer: PropTypes.string.isRequired,
|
||||
setSelectedServer: PropTypes.func.isRequired,
|
||||
setServerVariableValue: PropTypes.func.isRequired,
|
||||
getServerVariable: PropTypes.func.isRequired,
|
||||
getEffectiveServerValue: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let { servers } = this.props
|
||||
|
||||
//fire 'change' event to set default 'value' of select
|
||||
this.setServer(servers.first().get("url"))
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
let {
|
||||
servers,
|
||||
setServerVariableValue,
|
||||
getServerVariable
|
||||
} = this.props
|
||||
|
||||
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()
|
||||
|
||||
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
|
||||
|
||||
currentServerVariableDefs.map((val, key) => {
|
||||
let currentValue = getServerVariable(nextProps.currentServer, key)
|
||||
// only set the default value if the user hasn't set one yet
|
||||
if(!currentValue) {
|
||||
setServerVariableValue({
|
||||
server: nextProps.currentServer,
|
||||
key,
|
||||
val: val.get("default") || ""
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onServerChange =( e ) => {
|
||||
this.setServer( e.target.value )
|
||||
|
||||
// set default variable values
|
||||
}
|
||||
|
||||
onServerVariableValueChange = ( e ) => {
|
||||
let {
|
||||
setServerVariableValue,
|
||||
currentServer
|
||||
} = this.props
|
||||
|
||||
let variableName = e.target.getAttribute("data-variable")
|
||||
let newVariableValue = e.target.value
|
||||
|
||||
if(typeof setServerVariableValue === "function") {
|
||||
setServerVariableValue({
|
||||
server: currentServer,
|
||||
key: variableName,
|
||||
val: newVariableValue
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setServer = ( value ) => {
|
||||
let { setSelectedServer } = this.props
|
||||
|
||||
setSelectedServer(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { servers,
|
||||
currentServer,
|
||||
getServerVariable,
|
||||
getEffectiveServerValue
|
||||
} = this.props
|
||||
|
||||
let currentServerDefinition = servers.find(v => v.get("url") === currentServer) || OrderedMap()
|
||||
|
||||
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
|
||||
|
||||
let shouldShowVariableUI = currentServerVariableDefs.size !== 0
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor="servers">
|
||||
<span className="servers-title">Servers</span>
|
||||
<select onChange={ this.onServerChange }>
|
||||
{ servers.valueSeq().map(
|
||||
( server ) =>
|
||||
<option
|
||||
value={ server.get("url") }
|
||||
key={ server.get("url") }>
|
||||
{ server.get("url") }
|
||||
</option>
|
||||
).toArray()}
|
||||
</select>
|
||||
</label>
|
||||
{ shouldShowVariableUI ?
|
||||
<div>
|
||||
<h4>Server variables</h4>
|
||||
<div className={"computed-url"}>
|
||||
Computed URL:
|
||||
<code>
|
||||
{getEffectiveServerValue(currentServer)}
|
||||
</code>
|
||||
</div>
|
||||
<table>
|
||||
<tbody>
|
||||
{
|
||||
currentServerVariableDefs.map((val, name) => {
|
||||
return <tr key={name}>
|
||||
<td>{name}</td>
|
||||
<td>
|
||||
{ val.get("enum") ?
|
||||
<select data-variable={name} onChange={this.onServerVariableValueChange}>
|
||||
{val.get("enum").map(enumValue => {
|
||||
return <option
|
||||
selected={enumValue === getServerVariable(currentServer, name)}
|
||||
key={enumValue}
|
||||
value={enumValue}>
|
||||
{enumValue}
|
||||
</option>
|
||||
})}
|
||||
</select> :
|
||||
<input
|
||||
type={"text"}
|
||||
value={getServerVariable(currentServer, name) || ""}
|
||||
onChange={this.onServerVariableValueChange}
|
||||
data-variable={name}
|
||||
></input>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
// import reducers from "./reducers"
|
||||
// import * as actions from "./actions"
|
||||
import * as wrapSelectors from "./wrap-selectors"
|
||||
import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
|
||||
import * as specSelectors from "./spec-extensions/selectors"
|
||||
import components from "./components"
|
||||
import wrapComponents from "./wrap-components"
|
||||
import * as oas3Actions from "./actions"
|
||||
import * as oas3Selectors from "./selectors"
|
||||
import oas3Reducers from "./reducers"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
@@ -10,7 +14,13 @@ export default function() {
|
||||
wrapComponents,
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors
|
||||
wrapSelectors: specWrapSelectors,
|
||||
selectors: specSelectors
|
||||
},
|
||||
oas3: {
|
||||
actions: oas3Actions,
|
||||
reducers: oas3Reducers,
|
||||
selectors: oas3Selectors,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
src/core/plugins/oas3/reducers.js
Normal file
23
src/core/plugins/oas3/reducers.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
UPDATE_SELECTED_SERVER,
|
||||
UPDATE_REQUEST_BODY_VALUE,
|
||||
UPDATE_REQUEST_CONTENT_TYPE,
|
||||
UPDATE_SERVER_VARIABLE_VALUE
|
||||
} from "./actions"
|
||||
|
||||
export default {
|
||||
[UPDATE_SELECTED_SERVER]: (state, { payload: selectedServerUrl } ) =>{
|
||||
return state.setIn( [ "selectedServer" ], selectedServerUrl)
|
||||
},
|
||||
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
return state.setIn( [ "requestData", path, method, "bodyValue" ], value)
|
||||
},
|
||||
[UPDATE_REQUEST_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
|
||||
},
|
||||
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
|
||||
return state.setIn( [ "serverVariableValues", server, key ], val)
|
||||
},
|
||||
}
|
||||
53
src/core/plugins/oas3/selectors.js
Normal file
53
src/core/plugins/oas3/selectors.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { OrderedMap } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper } from "./helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (...args) => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const selectedServer = onlyOAS3(state => {
|
||||
return state.getIn(["selectedServer"]) || ""
|
||||
}
|
||||
)
|
||||
|
||||
export const requestBodyValue = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "bodyValue"]) || null
|
||||
}
|
||||
)
|
||||
|
||||
export const requestContentType = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "requestContentType"]) || null
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariableValue = onlyOAS3((state, server, key) => {
|
||||
return state.getIn(["serverVariableValues", server, key]) || null
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariables = onlyOAS3((state, server) => {
|
||||
return state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
}
|
||||
)
|
||||
|
||||
export const serverEffectiveValue = onlyOAS3((state, server) => {
|
||||
let varValues = state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
let str = server
|
||||
|
||||
varValues.map((val, key) => {
|
||||
str = str.replace(new RegExp(`{${key}}`, "g"), val)
|
||||
})
|
||||
|
||||
return str
|
||||
}
|
||||
)
|
||||
50
src/core/plugins/oas3/spec-extensions/selectors.js
Normal file
50
src/core/plugins/oas3/spec-extensions/selectors.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return () => (system, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const state = state => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const specJson = createSelector(
|
||||
state,
|
||||
spec => spec.get("json", Map())
|
||||
)
|
||||
|
||||
const specResolved = createSelector(
|
||||
state,
|
||||
spec => spec.get("resolved", Map())
|
||||
)
|
||||
|
||||
const spec = state => {
|
||||
let res = specResolved(state)
|
||||
if(res.count() < 1)
|
||||
res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
// New selectors
|
||||
|
||||
export const servers = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["servers"]) || Map()
|
||||
))
|
||||
|
||||
export const isSwagger2 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isSwagger2Helper(spec)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "./helpers"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
@@ -56,6 +56,11 @@ export const schemes = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
export const servers = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["servers"]) || Map()
|
||||
))
|
||||
|
||||
export const isOAS3 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isOAS3Helper(spec)
|
||||
@@ -3,7 +3,6 @@ import parameters from "./parameters"
|
||||
import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
import Model from "./model"
|
||||
import TryItOutButton from "./try-it-out-button"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
@@ -11,5 +10,4 @@ export default {
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
onlineValidatorBadge: OnlineValidatorBadge,
|
||||
TryItOutButton
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ class Parameters extends Component {
|
||||
super(props)
|
||||
this.state = {
|
||||
callbackVisible: false,
|
||||
parametersVisible: true,
|
||||
requestBodyContentType: ""
|
||||
parametersVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +23,8 @@ class Parameters extends Component {
|
||||
operation: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
tryItOutEnabled: PropTypes.bool,
|
||||
allowTryItOut: PropTypes.bool,
|
||||
@@ -86,6 +87,8 @@ class Parameters extends Component {
|
||||
fn,
|
||||
getComponent,
|
||||
specSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
pathMethod,
|
||||
operation
|
||||
} = this.props
|
||||
@@ -159,16 +162,22 @@ class Parameters extends Component {
|
||||
<h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4>
|
||||
<label>
|
||||
<ContentType
|
||||
value={this.state.requestBodyContentType}
|
||||
value={oas3Selectors.requestContentType(...pathMethod)}
|
||||
contentTypes={ requestBody.get("content").keySeq() }
|
||||
onChange={(val) => this.setState({ requestBodyContentType: val })}
|
||||
onChange={(value) => {
|
||||
oas3Actions.setRequestContentType({ value, pathMethod })
|
||||
}}
|
||||
className="body-param-content-type" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="opblock-description-wrapper">
|
||||
<RequestBody
|
||||
requestBody={requestBody}
|
||||
contentType={this.state.requestBodyContentType}/>
|
||||
isExecute={isExecute}
|
||||
onChange={(value) => {
|
||||
oas3Actions.setRequestBodyValue({ value, pathMethod })
|
||||
}}
|
||||
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory(() => {
|
||||
return null
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import YAML from "js-yaml"
|
||||
import parseUrl from "url-parse"
|
||||
import serializeError from "serialize-error"
|
||||
import { isJSONObject } from "core/utils"
|
||||
|
||||
// Actions conform to FSA (flux-standard-actions)
|
||||
// {type: string,payload: Any|Error, meta: obj, error: bool}
|
||||
@@ -195,48 +196,67 @@ export const logRequest = (req) => {
|
||||
|
||||
// Actually fire the request via fn.execute
|
||||
// (For debugging) and ease of testing
|
||||
export const executeRequest = (req) => ({fn, specActions, specSelectors, getConfigs}) => {
|
||||
let { pathName, method, operation } = req
|
||||
let { requestInterceptor, responseInterceptor } = getConfigs()
|
||||
export const executeRequest = (req) =>
|
||||
({fn, specActions, specSelectors, getConfigs, oas3Selectors}) => {
|
||||
let { pathName, method, operation } = req
|
||||
let { requestInterceptor, responseInterceptor } = getConfigs()
|
||||
|
||||
let op = operation.toJS()
|
||||
let op = operation.toJS()
|
||||
|
||||
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
|
||||
req.contextUrl = parseUrl(specSelectors.url()).toString()
|
||||
// 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) {
|
||||
req.operationId = fn.opId(op, pathName, method)
|
||||
if(op && op.operationId) {
|
||||
req.operationId = op.operationId
|
||||
} else if(op && pathName && method) {
|
||||
req.operationId = fn.opId(op, pathName, method)
|
||||
}
|
||||
|
||||
if(specSelectors.isOAS3()) {
|
||||
// OAS3 request feature support
|
||||
req.server = oas3Selectors.selectedServer()
|
||||
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
|
||||
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
|
||||
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
|
||||
|
||||
if(isJSONObject(requestBody)) {
|
||||
req.requestBody = JSON.parse(requestBody)
|
||||
} else {
|
||||
req.requestBody = requestBody
|
||||
}
|
||||
}
|
||||
|
||||
let parsedRequest = Object.assign({}, req)
|
||||
parsedRequest = fn.buildRequest(parsedRequest)
|
||||
|
||||
specActions.setRequest(req.pathName, req.method, parsedRequest)
|
||||
|
||||
let requestInterceptorWrapper = function(r) {
|
||||
let mutatedRequest = requestInterceptor.apply(this, [r])
|
||||
let parsedMutatedRequest = Object.assign({}, mutatedRequest)
|
||||
specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
|
||||
return mutatedRequest
|
||||
}
|
||||
|
||||
req.requestInterceptor = requestInterceptorWrapper
|
||||
req.responseInterceptor = responseInterceptor
|
||||
|
||||
// track duration of request
|
||||
const startTime = Date.now()
|
||||
|
||||
return fn.execute(req)
|
||||
.then( res => {
|
||||
res.duration = Date.now() - startTime
|
||||
specActions.setResponse(req.pathName, req.method, res)
|
||||
} )
|
||||
.catch(
|
||||
err => specActions.setResponse(req.pathName, req.method, {
|
||||
error: true, err: serializeError(err)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let parsedRequest = Object.assign({}, req)
|
||||
parsedRequest = fn.buildRequest(parsedRequest)
|
||||
|
||||
specActions.setRequest(req.pathName, req.method, parsedRequest)
|
||||
|
||||
let requestInterceptorWrapper = function(r) {
|
||||
let mutatedRequest = requestInterceptor.apply(this, [r])
|
||||
let parsedMutatedRequest = Object.assign({}, mutatedRequest)
|
||||
specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
|
||||
return mutatedRequest
|
||||
}
|
||||
|
||||
req.requestInterceptor = requestInterceptorWrapper
|
||||
req.responseInterceptor = responseInterceptor
|
||||
|
||||
// track duration of request
|
||||
const startTime = Date.now()
|
||||
|
||||
return fn.execute(req)
|
||||
.then( res => {
|
||||
res.duration = Date.now() - startTime
|
||||
specActions.setResponse(req.pathName, req.method, res)
|
||||
} )
|
||||
.catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) )
|
||||
}
|
||||
|
||||
|
||||
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
|
||||
export const execute = ( { path, method, ...extras }={} ) => (system) => {
|
||||
|
||||
@@ -13,6 +13,25 @@ const DEFAULT_REPONSE_KEY = "default"
|
||||
|
||||
export const isImmutable = (maybe) => Im.Iterable.isIterable(maybe)
|
||||
|
||||
export function isJSONObject (str) {
|
||||
try {
|
||||
var o = JSON.parse(str)
|
||||
|
||||
// Handle non-exception-throwing cases:
|
||||
// Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
|
||||
// but... JSON.parse(null) returns null, and typeof null === "object",
|
||||
// so we must check for that, too. Thankfully, null is falsey, so this suffices:
|
||||
if (o && typeof o === "object") {
|
||||
return o
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function objectify (thing) {
|
||||
if(!isObject(thing))
|
||||
return {}
|
||||
|
||||
@@ -636,6 +636,77 @@
|
||||
}
|
||||
}
|
||||
|
||||
.server-container
|
||||
{
|
||||
margin: 0 0 20px 0;
|
||||
padding: 30px 0;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15);
|
||||
|
||||
.computed-url {
|
||||
margin: 2em 0;
|
||||
|
||||
code {
|
||||
color: grey;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
font-size: 16px;
|
||||
margin: 0 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.servers
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.servers-title {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
> label
|
||||
{
|
||||
font-size: 12px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: -20px 15px 0 0;
|
||||
|
||||
@include text_headline();
|
||||
|
||||
select
|
||||
{
|
||||
min-width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
tr {
|
||||
width: 30em;
|
||||
}
|
||||
td {
|
||||
display: inline-block;
|
||||
max-width: 15em;
|
||||
vertical-align: middle;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&:first-of-type {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.loading-container
|
||||
{
|
||||
|
||||
@@ -113,7 +113,8 @@ describe("spec plugin - actions", function(){
|
||||
spec: () => fromJS({}),
|
||||
parameterValues: () => fromJS({}),
|
||||
contentTypeValues: () => fromJS({}),
|
||||
url: () => fromJS({})
|
||||
url: () => fromJS({}),
|
||||
isOAS3: () => false
|
||||
},
|
||||
getConfigs: () => configs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user