diff --git a/package.json b/package.json
index 02383b29..a42621c9 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/core/components/layouts/base.jsx b/src/core/components/layouts/base.jsx
index 268ef46c..6cd909bb 100644
--- a/src/core/components/layouts/base.jsx
+++ b/src/core/components/layouts/base.jsx
@@ -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 {
) : null }
+ { servers && servers.size ? (
+
diff --git a/src/core/plugins/oas3/actions.js b/src/core/plugins/oas3/actions.js
new file mode 100644
index 00000000..4a5bf9ac
--- /dev/null
+++ b/src/core/plugins/oas3/actions.js
@@ -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 }
+ }
+}
diff --git a/src/core/plugins/oas3/components/index.js b/src/core/plugins/oas3/components/index.js
index 8186ce8e..642c6689 100644
--- a/src/core/plugins/oas3/components/index.js
+++ b/src/core/plugins/oas3/components/index.js
@@ -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
}
diff --git a/src/core/plugins/oas3/components/request-body-editor.jsx b/src/core/plugins/oas3/components/request-body-editor.jsx
new file mode 100644
index 00000000..62f91a52
--- /dev/null
+++ b/src/core/plugins/oas3/components/request-body-editor.jsx
@@ -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 (
+
+ {
+ isEditBox && isExecute
+ ?
+ : (value &&
)
+ }
+
+ {
+ !isExecute ? null
+ :
+
+
+ }
+
+
+
+ )
+
+ }
+}
diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx
index 2cbf491a..48976ef5 100644
--- a/src/core/plugins/oas3/components/request-body.jsx
+++ b/src/core/plugins/oas3/components/request-body.jsx
@@ -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
{ requestBodyDescription &&
@@ -28,8 +29,16 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
getComponent={ getComponent }
specSelectors={ specSelectors }
expandDepth={1}
+ isExecute={isExecute}
schema={mediaTypeValue.get("schema")}
- example={}
+ example={}
/>
}
@@ -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
diff --git a/src/core/plugins/oas3/components/servers.jsx b/src/core/plugins/oas3/components/servers.jsx
new file mode 100644
index 00000000..75b528f5
--- /dev/null
+++ b/src/core/plugins/oas3/components/servers.jsx
@@ -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 (
+
+
+ { shouldShowVariableUI ?
+
+
Server variables
+
+ Computed URL:
+
+ {getEffectiveServerValue(currentServer)}
+
+
+
+
: null
+ }
+
+ )
+ }
+}
diff --git a/src/core/plugins/oas3/index.js b/src/core/plugins/oas3/index.js
index 3413b65e..7af47fea 100644
--- a/src/core/plugins/oas3/index.js
+++ b/src/core/plugins/oas3/index.js
@@ -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,
}
}
}
diff --git a/src/core/plugins/oas3/reducers.js b/src/core/plugins/oas3/reducers.js
new file mode 100644
index 00000000..810c1ba0
--- /dev/null
+++ b/src/core/plugins/oas3/reducers.js
@@ -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)
+ },
+}
diff --git a/src/core/plugins/oas3/selectors.js b/src/core/plugins/oas3/selectors.js
new file mode 100644
index 00000000..a6c57761
--- /dev/null
+++ b/src/core/plugins/oas3/selectors.js
@@ -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
+ }
+)
diff --git a/src/core/plugins/oas3/spec-extensions/selectors.js b/src/core/plugins/oas3/spec-extensions/selectors.js
new file mode 100644
index 00000000..e8911423
--- /dev/null
+++ b/src/core/plugins/oas3/spec-extensions/selectors.js
@@ -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)
+}
diff --git a/src/core/plugins/oas3/wrap-selectors.js b/src/core/plugins/oas3/spec-extensions/wrap-selectors.js
similarity index 92%
rename from src/core/plugins/oas3/wrap-selectors.js
rename to src/core/plugins/oas3/spec-extensions/wrap-selectors.js
index 694e456b..6625b3e4 100644
--- a/src/core/plugins/oas3/wrap-selectors.js
+++ b/src/core/plugins/oas3/spec-extensions/wrap-selectors.js
@@ -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)
diff --git a/src/core/plugins/oas3/wrap-components/index.js b/src/core/plugins/oas3/wrap-components/index.js
index eb2bc7a1..8b105b38 100644
--- a/src/core/plugins/oas3/wrap-components/index.js
+++ b/src/core/plugins/oas3/wrap-components/index.js
@@ -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
}
diff --git a/src/core/plugins/oas3/wrap-components/parameters.jsx b/src/core/plugins/oas3/wrap-components/parameters.jsx
index e735085e..31495709 100644
--- a/src/core/plugins/oas3/wrap-components/parameters.jsx
+++ b/src/core/plugins/oas3/wrap-components/parameters.jsx
@@ -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 {
Request body