Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 66 additions & 13 deletions src/core/components/execute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,81 @@ export default class Execute extends Component {
operation: PropTypes.object.isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
oas3Selectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
onExecute: PropTypes.func
}

onClick=()=>{
let { specSelectors, specActions, operation, path, method } = this.props
handleValidateParameters = () => {
let { specSelectors, specActions, path, method } = this.props
specActions.validateParams([path, method])
return specSelectors.validateBeforeExecute([path, method])
}

handleValidateRequestBody = () => {
let { path, method, specSelectors, oas3Selectors, oas3Actions } = this.props
let validationErrors = {
missingBodyValue: false,
missingRequiredKeys: []
}
// context: reset errors, then (re)validate
oas3Actions.clearRequestBodyValidateError({ path, method })
let oas3RequiredRequestBodyContentType = specSelectors.getOAS3RequiredRequestBodyContentType([path, method])
let oas3RequestBodyValue = oas3Selectors.requestBodyValue(path, method)
let oas3ValidateBeforeExecuteSuccess = oas3Selectors.validateBeforeExecute([path, method])

if (!oas3ValidateBeforeExecuteSuccess) {
validationErrors.missingBodyValue = true
oas3Actions.setRequestBodyValidateError({ path, method, validationErrors })
return false
}
if (!oas3RequiredRequestBodyContentType) {
return true
}
let missingRequiredKeys = oas3Selectors.validateShallowRequired({ oas3RequiredRequestBodyContentType, oas3RequestBodyValue })
if (!missingRequiredKeys || missingRequiredKeys.length < 1) {
return true
}
missingRequiredKeys.forEach((missingKey) => {
validationErrors.missingRequiredKeys.push(missingKey)
})
oas3Actions.setRequestBodyValidateError({ path, method, validationErrors })
return false
}

specActions.validateParams( [path, method] )
handleValidationResultPass = () => {
let { specActions, operation, path, method } = this.props
if (this.props.onExecute) {
// loading spinner
this.props.onExecute()
}
specActions.execute({ operation, path, method })
}

if ( specSelectors.validateBeforeExecute([path, method]) ) {
if(this.props.onExecute) {
this.props.onExecute()
}
specActions.execute( { operation, path, method } )
handleValidationResultFail = () => {
let { specActions, path, method } = this.props
// deferred by 40ms, to give element class change time to settle.
specActions.clearValidateParams([path, method])
setTimeout(() => {
specActions.validateParams([path, method])
}, 40)
}

handleValidationResult = (isPass) => {
if (isPass) {
this.handleValidationResultPass()
} else {
// deferred by 40ms, to give element class change time to settle.
specActions.clearValidateParams( [path, method] )
setTimeout(() => {
specActions.validateParams([path, method])
}, 40)
this.handleValidationResultFail()
}
}

onClick = () => {
let paramsResult = this.handleValidateParameters()
let requestBodyResult = this.handleValidateRequestBody()
let isPass = paramsResult && requestBodyResult
this.handleValidationResult(isPass)
}

onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)

render(){
Expand Down
2 changes: 2 additions & 0 deletions src/core/components/operation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ export default class Operation extends PureComponent {
operation={ operation }
specActions={ specActions }
specSelectors={ specSelectors }
oas3Selectors={ oas3Selectors }
oas3Actions={ oas3Actions }
path={ path }
method={ method }
onExecute={ onExecute } />
Expand Down
2 changes: 2 additions & 0 deletions src/core/components/parameters/parameters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export default class Parameters extends Component {
contentTypes={ requestBody.get("content", List()).keySeq() }
onChange={(value) => {
oas3Actions.setRequestContentType({ value, pathMethod })
oas3Actions.initRequestBodyValidateError({ pathMethod })
}}
className="body-param-content-type" />
</label>
Expand All @@ -197,6 +198,7 @@ export default class Parameters extends Component {
requestBody={requestBody}
requestBodyValue={oas3Selectors.requestBodyValue(...pathMethod)}
requestBodyInclusionSetting={oas3Selectors.requestBodyInclusionSetting(...pathMethod)}
requestBodyErrors={oas3Selectors.requestBodyErrors(...pathMethod)}
isExecute={isExecute}
activeExamplesKey={oas3Selectors.activeExamplesMember(
...pathMethod,
Expand Down
23 changes: 23 additions & 0 deletions src/core/plugins/oas3/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const UPDATE_ACTIVE_EXAMPLES_MEMBER = "oas3_set_active_examples_member"
export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
export const UPDATE_RESPONSE_CONTENT_TYPE = "oas3_set_response_content_type"
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
export const SET_REQUEST_BODY_VALIDATE_ERROR = "oas3_set_request_body_validate_error"
export const CLEAR_REQUEST_BODY_VALIDATE_ERROR = "oas3_clear_request_body_validate_error"

export function setSelectedServer (selectedServerUrl, namespace) {
return {
Expand Down Expand Up @@ -57,3 +59,24 @@ export function setServerVariableValue ({ server, namespace, key, val }) {
payload: { server, namespace, key, val }
}
}

export const setRequestBodyValidateError = ({ path, method, validationErrors }) => {
return {
type: SET_REQUEST_BODY_VALIDATE_ERROR,
payload: { path, method, validationErrors }
}
}

export const clearRequestBodyValidateError = ({ path, method }) => {
return {
type: CLEAR_REQUEST_BODY_VALIDATE_ERROR,
payload: { path, method }
}
}

export const initRequestBodyValidateError = ({ pathMethod } ) => {
return {
type: CLEAR_REQUEST_BODY_VALIDATE_ERROR,
payload: { path: pathMethod[0], method: pathMethod[1] }
}
}
9 changes: 7 additions & 2 deletions src/core/plugins/oas3/components/request-body-editor.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import cx from "classnames"
import { stringify } from "core/utils"

const NOOP = Function.prototype
Expand All @@ -11,6 +12,7 @@ export default class RequestBodyEditor extends PureComponent {
getComponent: PropTypes.func.isRequired,
value: PropTypes.string,
defaultValue: PropTypes.string,
errors: PropTypes.array,
};

static defaultProps = {
Expand Down Expand Up @@ -74,19 +76,22 @@ export default class RequestBodyEditor extends PureComponent {

render() {
let {
getComponent
getComponent,
errors
} = this.props

let {
value
} = this.state

let isInvalid = errors.size > 0 ? true : false
const TextArea = getComponent("TextArea")

return (
<div className="body-param">
<TextArea
className={"body-param__text"}
className={cx("body-param__text", { invalid: isInvalid } )}
title={errors.size ? errors.join(", ") : ""}
value={value}
onChange={ this.onDomChange }
/>
Expand Down
9 changes: 8 additions & 1 deletion src/core/plugins/oas3/components/request-body.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const RequestBody = ({
requestBody,
requestBodyValue,
requestBodyInclusionSetting,
requestBodyErrors,
getComponent,
getConfigs,
specSelectors,
Expand Down Expand Up @@ -88,6 +89,7 @@ const RequestBody = ({
const handleExamplesSelect = (key /*, { isSyntheticChange } */) => {
updateActiveExamplesKey(key)
}
requestBodyErrors = List.isList(requestBodyErrors) ? requestBodyErrors : List()

if(!mediaTypeValue.size) {
return null
Expand Down Expand Up @@ -138,7 +140,8 @@ const RequestBody = ({
const type = prop.get("type")
const format = prop.get("format")
const description = prop.get("description")
const currentValue = requestBodyValue.get(key)
const currentValue = requestBodyValue.getIn([key, "value"])
const currentErrors = requestBodyValue.getIn([key, "errors"]) || requestBodyErrors

let initialValue = prop.get("default") || prop.get("example") || ""

Expand Down Expand Up @@ -179,6 +182,8 @@ const RequestBody = ({
description={key}
getComponent={getComponent}
value={currentValue === undefined ? initialValue : currentValue}
required = { required }
errors = { currentErrors }
onChange={(value) => {
onChange(value, [key])
}}
Expand Down Expand Up @@ -223,6 +228,7 @@ const RequestBody = ({
<div>
<RequestBodyEditor
value={requestBodyValue}
errors={requestBodyErrors}
defaultValue={getDefaultRequestBodyValue(
requestBody,
contentType,
Expand Down Expand Up @@ -270,6 +276,7 @@ RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired,
requestBodyValue: ImPropTypes.orderedMap.isRequired,
requestBodyInclusionSetting: ImPropTypes.Map.isRequired,
requestBodyErrors: ImPropTypes.list.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
fn: PropTypes.object.isRequired,
Expand Down
62 changes: 60 additions & 2 deletions src/core/plugins/oas3/reducers.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { fromJS, Map } from "immutable"

import {
UPDATE_SELECTED_SERVER,
UPDATE_REQUEST_BODY_VALUE,
UPDATE_REQUEST_BODY_INCLUSION,
UPDATE_ACTIVE_EXAMPLES_MEMBER,
UPDATE_REQUEST_CONTENT_TYPE,
UPDATE_SERVER_VARIABLE_VALUE,
UPDATE_RESPONSE_CONTENT_TYPE
UPDATE_RESPONSE_CONTENT_TYPE,
SET_REQUEST_BODY_VALIDATE_ERROR,
CLEAR_REQUEST_BODY_VALIDATE_ERROR,
} from "./actions"

export default {
Expand All @@ -15,7 +19,27 @@ export default {
},
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "bodyValue" ], value)
if (!Map.isMap(value)) {
// context: application/json is always a String (instead of Map)
return state.setIn( [ "requestData", path, method, "bodyValue" ], value)
}
let currentVal = state.getIn(["requestData", path, method, "bodyValue"]) || Map()
if (!Map.isMap(currentVal)) {
// context: user switch from application/json to application/x-www-form-urlencoded
currentVal = Map()
}
let newVal
const [...valueKeys] = value.keys()
valueKeys.forEach((valueKey) => {
let valueKeyVal = value.getIn([valueKey])
if (!currentVal.has(valueKey)) {
newVal = currentVal.setIn([valueKey, "value"], valueKeyVal)
} else if (!Map.isMap(valueKeyVal)) {
// context: user input will be received as String
newVal = currentVal.setIn([valueKey, "value"], valueKeyVal)
}
})
return state.setIn(["requestData", path, method, "bodyValue"], newVal)
},
[UPDATE_REQUEST_BODY_INCLUSION]: (state, { payload: { value, pathMethod, name } } ) =>{
let [path, method] = pathMethod
Expand All @@ -36,4 +60,38 @@ export default {
const path = namespace ? [ namespace, "serverVariableValues", server, key ] : [ "serverVariableValues", server, key ]
return state.setIn(path, val)
},
[SET_REQUEST_BODY_VALIDATE_ERROR]: (state, { payload: { path, method, validationErrors } } ) => {
let errors = []
errors.push("Required field is not provided")
if (validationErrors.missingBodyValue) {
// context: is application/json or application/xml, where typeof (missing) bodyValue = String
return state.setIn(["requestData", path, method, "errors"], fromJS(errors))
}
if (validationErrors.missingRequiredKeys && validationErrors.missingRequiredKeys.length > 0) {
// context: is application/x-www-form-urlencoded, with list of missing keys
const { missingRequiredKeys } = validationErrors
return state.updateIn(["requestData", path, method, "bodyValue"], fromJS({}), missingKeyValues => {
return missingRequiredKeys.reduce((bodyValue, currentMissingKey) => {
return bodyValue.setIn([currentMissingKey, "errors"], fromJS(errors))
}, missingKeyValues)
})
}
console.warn("unexpected result: SET_REQUEST_BODY_VALIDATE_ERROR")
return state
},
[CLEAR_REQUEST_BODY_VALIDATE_ERROR]: (state, { payload: { path, method } }) => {
const requestBodyValue = state.getIn(["requestData", path, method, "bodyValue"])
if (!Map.isMap(requestBodyValue)) {
return state.setIn(["requestData", path, method, "errors"], fromJS([]))
}
const [...valueKeys] = requestBodyValue.keys()
if (!valueKeys) {
return state
}
return state.updateIn(["requestData", path, method, "bodyValue"], fromJS({}), bodyValues => {
return valueKeys.reduce((bodyValue, curr) => {
return bodyValue.setIn([curr, "errors"], fromJS([]))
}, bodyValues)
})
},
}
Loading