feature: allowEmptyValue controls (#4788)

* add baseline tests

* coerce empty strings to null when updating parameter values

* add ParameterIncludeEmpty

* add redux management for empty parameter value inclusion state

* use name+in keying for state management instead of hash keying

* update new redux method usages to name+in keying

* coerce empty Immutable iterables in onChangeWrapper

* OAS3 tests & support

* add included empty parameters to requests before dispatching to Swagger Client

* make empty inclusion interface prettier

* add tests for #4587

* linter fixes

* check for truthy value before reaching into property
This commit is contained in:
kyle
2018-08-04 01:26:07 -07:00
committed by GitHub
parent dd3afdc456
commit 87296702c6
18 changed files with 1363 additions and 4 deletions

View File

@@ -0,0 +1,27 @@
import React from "react"
import cx from "classnames"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export const ParameterIncludeEmpty = ({ param, isIncluded, onChange, isDisabled }) => {
const onCheckboxChange = e => {
onChange(e.target.checked)
}
if(!param.get("allowEmptyValue")) {
return null
}
return <div className={cx("parameter__empty_value_toggle", {
"disabled": isDisabled
})}>
<input type="checkbox" disabled={isDisabled} checked={!isDisabled && isIncluded} onChange={onCheckboxChange} />
Send empty value
</div>
}
ParameterIncludeEmpty.propTypes = {
param: ImPropTypes.map.isRequired,
isIncluded: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
}
export default ParameterIncludeEmpty

View File

@@ -15,6 +15,7 @@ export default class ParameterRow extends Component {
isExecute: PropTypes.bool,
onChangeConsumes: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired,
specPath: ImPropTypes.list.isRequired
@@ -61,7 +62,23 @@ export default class ParameterRow extends Component {
onChangeWrapper = (value, isXml = false) => {
let { onChange, rawParam } = this.props
return onChange(rawParam, value, isXml)
let valueForUpstream
// Coerce empty strings and empty Immutable objects to null
if(value === "" || (value && value.size === 0)) {
valueForUpstream = null
} else {
valueForUpstream = value
}
return onChange(rawParam, valueForUpstream, isXml)
}
onChangeIncludeEmpty = (newValue) => {
let { specActions, param, pathMethod } = this.props
const paramName = param.get("name")
const paramIn = param.get("in")
return specActions.updateEmptyParamInclusion(pathMethod, paramName, paramIn, newValue)
}
setDefaultValue = () => {
@@ -120,6 +137,7 @@ export default class ParameterRow extends Component {
const ModelExample = getComponent("modelExample")
const Markdown = getComponent("Markdown")
const ParameterExt = getComponent("ParameterExt")
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam)
let format = param.get("format")
@@ -225,6 +243,16 @@ export default class ParameterRow extends Component {
: null
}
{
!bodyParam && isExecute ?
<ParameterIncludeEmpty
onChange={this.onChangeIncludeEmpty}
isIncluded={specSelectors.parameterInclusionSettingFor(pathMethod, param.get("name"), param.get("in"))}
isDisabled={value && value.size !== 0}
param={param} />
: null
}
</td>
</tr>

View File

@@ -65,7 +65,8 @@ export default class Parameters extends Component {
fn,
getComponent,
getConfigs,
specSelectors,
specSelectors,
specActions,
pathMethod
} = this.props
@@ -107,6 +108,7 @@ export default class Parameters extends Component {
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
specSelectors={ specSelectors }
specActions={specActions}
pathMethod={ pathMethod }
isExecute={ isExecute }/>
)).toArray()

View File

@@ -90,6 +90,7 @@ class Parameters extends Component {
getComponent,
getConfigs,
specSelectors,
specActions,
oas3Actions,
oas3Selectors,
pathMethod,
@@ -151,6 +152,7 @@ class Parameters extends Component {
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
specSelectors={ specSelectors }
specActions={ specActions }
pathMethod={ pathMethod }
isExecute={ isExecute }/>
)).toArray()

View File

@@ -14,6 +14,7 @@ export const UPDATE_SPEC = "spec_update_spec"
export const UPDATE_URL = "spec_update_url"
export const UPDATE_JSON = "spec_update_json"
export const UPDATE_PARAM = "spec_update_param"
export const UPDATE_EMPTY_PARAM_INCLUSION = "spec_update_empty_param_inclusion"
export const VALIDATE_PARAMS = "spec_validate_param"
export const SET_RESPONSE = "spec_set_response"
export const SET_REQUEST = "spec_set_request"
@@ -270,6 +271,18 @@ export const validateParams = ( payload, isOAS3 ) =>{
}
}
export const updateEmptyParamInclusion = ( pathMethod, paramName, paramIn, includeEmptyValue ) =>{
return {
type: UPDATE_EMPTY_PARAM_INCLUSION,
payload:{
pathMethod,
paramName,
paramIn,
includeEmptyValue
}
}
}
export function clearValidateParams( payload ){
return {
type: CLEAR_VALIDATE_PARAMS,
@@ -327,7 +340,28 @@ export const executeRequest = (req) =>
let { pathName, method, operation } = req
let { requestInterceptor, responseInterceptor } = getConfigs()
let op = operation.toJS()
// ensure that explicitly-included params are in the request
if(op && op.parameters && op.parameters.length) {
op.parameters
.filter(param => param && param.allowEmptyValue === true)
.forEach(param => {
if (specSelectors.parameterInclusionSettingFor([pathName, method], param.name, param.in)) {
req.parameters = req.parameters || {}
const paramValue = req.parameters[param.name]
// if the value is falsy or an empty Immutable iterable...
if(!paramValue || (paramValue && paramValue.size === 0)) {
// set it to empty string, so Swagger Client will treat it as
// present but empty.
req.parameters[param.name] = ""
}
}
})
}
// if url is relative, parseUrl makes it absolute by inferring from `window.location`
req.contextUrl = parseUrl(specSelectors.url()).toString()

View File

@@ -12,6 +12,7 @@ import {
UPDATE_URL,
UPDATE_JSON,
UPDATE_PARAM,
UPDATE_EMPTY_PARAM_INCLUSION,
VALIDATE_PARAMS,
SET_RESPONSE,
SET_REQUEST,
@@ -70,6 +71,22 @@ export default {
)
},
[UPDATE_EMPTY_PARAM_INCLUSION]: ( state, {payload} ) => {
let { pathMethod, paramName, paramIn, includeEmptyValue } = payload
if(!paramName || !paramIn) {
console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey.")
return state
}
const paramKey = `${paramName}.${paramIn}`
return state.setIn(
["meta", "paths", ...pathMethod, "parameter_inclusions", paramKey],
includeEmptyValue
)
},
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
let isXml = /xml/i.test(meta.get("consumes_value"))

View File

@@ -311,6 +311,11 @@ export const parameterWithMetaByIdentity = (state, pathMethod, param) => {
return mergedParams.find(curr => curr.get("in") === param.get("in") && curr.get("name") === param.get("name"), OrderedMap())
}
export const parameterInclusionSettingFor = (state, pathMethod, paramName, paramIn) => {
const paramKey = `${paramName}.${paramIn}`
return state.getIn(["meta", "paths", ...pathMethod, "parameter_inclusions", paramKey], false)
}
export const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], OrderedMap())

View File

@@ -42,6 +42,7 @@ import Response from "core/components/response"
import ResponseBody from "core/components/response-body"
import Parameters from "core/components/parameters"
import ParameterExt from "core/components/parameter-extension"
import ParameterIncludeEmpty from "core/components/parameter-include-empty"
import ParameterRow from "core/components/parameter-row"
import Execute from "core/components/execute"
import Headers from "core/components/headers"
@@ -143,6 +144,7 @@ export default function() {
OperationExt,
OperationExtRow,
ParameterExt,
ParameterIncludeEmpty,
OperationTag,
OperationContainer,
DeepLink,

View File

@@ -139,6 +139,20 @@ table
@include text_code($table-parameter-deprecated-font-color);
}
.parameter__empty_value_toggle {
font-size: 13px;
padding-top: 5px;
padding-bottom: 12px;
input {
margin-right: 7px;
}
&.disabled {
opacity: 0.7;
}
}
.table-container
{

View File

@@ -1,7 +1,7 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { fromJS } from "immutable"
import { execute, executeRequest, changeParamByIdentity } from "corePlugins/spec/actions"
import { execute, executeRequest, changeParamByIdentity, updateEmptyParamInclusion } from "corePlugins/spec/actions"
describe("spec plugin - actions", function(){
@@ -207,4 +207,22 @@ describe("spec plugin - actions", function(){
})
})
})
describe("updateEmptyParamInclusion", function () {
it("should map its arguments to a payload", function () {
const pathMethod = ["/one", "get"]
const result = updateEmptyParamInclusion(pathMethod, "param", "query", true)
expect(result).toEqual({
type: "spec_update_empty_param_inclusion",
payload: {
pathMethod,
paramName: "param",
paramIn: "query",
includeEmptyValue: true
}
})
})
})
})

View File

@@ -178,4 +178,26 @@ describe("spec plugin - reducer", function(){
expect(value).toEqual(`{ "a": 123 }`)
})
})
describe("SPEC_UPDATE_EMPTY_PARAM_INCLUSION", function() {
it("should store parameter values by name+in", () => {
const updateParam = reducer["spec_update_empty_param_inclusion"]
const path = "/pet/post"
const method = "POST"
const state = fromJS({})
const result = updateParam(state, {
payload: {
pathMethod: [path, method],
paramName: "param",
paramIn: "query",
includeEmptyValue: true
}
})
const response = result.getIn(["meta", "paths", path, method, "parameter_inclusions", "param.query"])
expect(response).toEqual(true)
})
})
})

View File

@@ -14,7 +14,8 @@ import Petstore from "./assets/petstore.json"
import {
operationWithMeta,
parameterWithMeta,
parameterWithMetaByIdentity
parameterWithMetaByIdentity,
parameterInclusionSettingFor
} from "../../../../src/core/plugins/spec/selectors"
describe("spec plugin - selectors", function(){
@@ -712,4 +713,42 @@ describe("spec plugin - selectors", function(){
})
})
})
describe("parameterInclusionSettingFor", function() {
it("should support getting name+in param inclusion settings", function () {
const param = fromJS({
name: "param",
in: "query",
allowEmptyValue: true
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
param
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
"parameter_inclusions": {
[`param.query`]: true
}
}
}
}
}
})
const result = parameterInclusionSettingFor(state, ["/", "get"], "param", "query")
expect(result).toEqual(true)
})
})
})

View File

@@ -0,0 +1,36 @@
describe("bug #4587: clearing header param values", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input:not([disabled])", 5000)
.pause(2000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4587.yaml")
.click("button.download-url-button")
.pause(1000)
done()
})
afterEach(function (client, done) {
done()
})
it("sets a required initial value based the first enum value", function (client) {
client.waitForElementVisible(".opblock-tag-section", 10000)
.click("#operations-sql-execSql")
.waitForElementVisible(".opblock.is-open", 5000)
.click("button.btn.try-out__btn")
.setValue(`tr[data-param-name="x-irest-conn"] input`, "hi")
.click("button.btn.execute")
.waitForElementVisible(".request-url", 2000)
.setValue(`tr[data-param-name="x-irest-conn"] input`, `\u0008\u0008\u0008`) // backspaces
.pause(900)
.click("button.btn.execute")
.expect.element("textarea.curl").text
.to.not.contain(`x-irest-conn`)
})
})

View File

@@ -0,0 +1,433 @@
describe("feature: OpenAPI 3 allowEmptyValue", function () {
beforeEach(function (client, done) {
client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input:not([disabled])", 5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "/test-specs/features/allow-empty-value.openapi.yaml")
.click("button.download-url-button")
.waitForElementVisible(".opblock", 10000)
done()
})
afterEach(function (client, done) {
done()
})
describe("regular parameters", function () {
it("should set and unset an integer value", function (client) {
const inputSelector = `tr[data-param-name="int"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a string value", function (client) {
const inputSelector = `tr[data-param-name="str"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a number value", function (client) {
const inputSelector = `tr[data-param-name="num"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a boolean value", function (client) {
const inputSelector = `tr[data-param-name="bool"] select`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} [value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} [value=""]`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset an array value", function (client) {
const inputSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} .json-schema-form-item-add`)
.setValue(`${inputSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} .json-schema-form-item-remove`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
})
describe("allowEmptyValue parameters", function () {
describe("normal behavior", function () {
it("should set and unset an integer value", function (client) {
const inputSelector = `tr[data-param-name="int"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a string value", function (client) {
const inputSelector = `tr[data-param-name="str"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a number value", function (client) {
const inputSelector = `tr[data-param-name="num"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a boolean value", function (client) {
const inputSelector = `tr[data-param-name="bool"] select`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} [value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} [value=""]`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset an array value", function (client) {
const inputSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} .json-schema-form-item-add`)
.setValue(`${inputSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} .json-schema-form-item-remove`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
})
describe("send empty inital value behavior", function () {
it("should send an empty integer value", function (client) {
const paramSelector = `tr[data-param-name="int"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?int="`)
})
it("should send an empty string value", function (client) {
const paramSelector = `tr[data-param-name="str"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?str="`)
})
it("should send an empty number value", function (client) {
const paramSelector = `tr[data-param-name="num"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?num="`)
})
it("should send an empty boolean value", function (client) {
const paramSelector = `tr[data-param-name="bool"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?bool="`)
})
it("should send an empty array value", function (client) {
const paramSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?arr="`)
})
})
describe("modify and send empty behavior", function () {
it("should set, unset and send an empty integer value", function (client) {
const paramSelector = `tr[data-param-name="int"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?int="`)
})
it("should set, unset and send an empty string value", function (client) {
const paramSelector = `tr[data-param-name="str"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?str="`)
})
it("should set, unset and send an empty number value", function (client) {
const paramSelector = `tr[data-param-name="num"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?num="`)
})
it("should set, unset and send an empty boolean value", function (client) {
const paramSelector = `tr[data-param-name="bool"]`
const inputSelector = `${paramSelector} select`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} option[value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.click(`${inputSelector} option[value=""]`)
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?bool="`)
})
it("should set, unset and send an empty array value", function (client) {
const paramSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${paramSelector} .json-schema-form-item-add`)
.setValue(`${paramSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${paramSelector} .json-schema-form-item-remove`)
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?arr="`)
})
})
})
})

View File

@@ -0,0 +1,433 @@
describe("feature: Swagger 2 allowEmptyValue", function () {
beforeEach(function (client, done) {
client
.url("localhost:3230")
.page.main()
client.waitForElementVisible(".download-url-input:not([disabled])", 5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", "/test-specs/features/allow-empty-value.swagger.yaml")
.click("button.download-url-button")
.waitForElementVisible(".opblock", 10000)
done()
})
afterEach(function (client, done) {
done()
})
describe("regular parameters", function () {
it("should set and unset an integer value", function (client) {
const inputSelector = `tr[data-param-name="int"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a string value", function (client) {
const inputSelector = `tr[data-param-name="str"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a number value", function (client) {
const inputSelector = `tr[data-param-name="num"] input`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset a boolean value", function (client) {
const inputSelector = `tr[data-param-name="bool"] select`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} [value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} [value=""]`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
it("should set and unset an array value", function (client) {
const inputSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_regularParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} .json-schema-form-item-add`)
.setValue(`${inputSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} .json-schema-form-item-remove`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/regularParams"`)
})
})
describe("allowEmptyValue parameters", function () {
describe("normal behavior", function () {
it("should set and unset an integer value", function (client) {
const inputSelector = `tr[data-param-name="int"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a string value", function (client) {
const inputSelector = `tr[data-param-name="str"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a number value", function (client) {
const inputSelector = `tr[data-param-name="num"] input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset a boolean value", function (client) {
const inputSelector = `tr[data-param-name="bool"] select`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} [value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} [value=""]`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
it("should set and unset an array value", function (client) {
const inputSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} .json-schema-form-item-add`)
.setValue(`${inputSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${inputSelector} .json-schema-form-item-remove`)
.pause(200)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams"`)
})
})
describe("send empty inital value behavior", function () {
it("should send an empty integer value", function (client) {
const paramSelector = `tr[data-param-name="int"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?int="`)
})
it("should send an empty string value", function (client) {
const paramSelector = `tr[data-param-name="str"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?str="`)
})
it("should send an empty number value", function (client) {
const paramSelector = `tr[data-param-name="num"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?num="`)
})
it("should send an empty boolean value", function (client) {
const paramSelector = `tr[data-param-name="bool"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?bool="`)
})
it("should send an empty array value", function (client) {
const paramSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // tick "send empty value" box and execute
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?arr="`)
})
})
describe("modify and send empty behavior", function () {
it("should set, unset and send an empty integer value", function (client) {
const paramSelector = `tr[data-param-name="int"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?int="`)
})
it("should set, unset and send an empty string value", function (client) {
const paramSelector = `tr[data-param-name="str"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?str="`)
})
it("should set, unset and send an empty number value", function (client) {
const paramSelector = `tr[data-param-name="num"]`
const inputSelector = `${paramSelector} input`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.setValue(inputSelector, "123")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.setValue(inputSelector, "\u0008\u0008\u0008") // backspaces
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?num="`)
})
it("should set, unset and send an empty boolean value", function (client) {
const paramSelector = `tr[data-param-name="bool"]`
const inputSelector = `${paramSelector} select`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${inputSelector} option[value="true"]`)
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, click "send empty", execute again, assert
.click(`${inputSelector} option[value=""]`)
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?bool="`)
})
it("should set, unset and send an empty array value", function (client) {
const paramSelector = `tr[data-param-name="arr"]`
client // open try-it-out
.click("#operations-default-get_emptyValueParams")
.waitForElementVisible("button.btn.try-out__btn", 5000)
.click("button.btn.try-out__btn")
.pause(200)
client // set parameter, to ensure an initial value is set
.click(`${paramSelector} .json-schema-form-item-add`)
.setValue(`${paramSelector} input`, "asdf")
.click("button.btn.execute.opblock-control__btn")
.pause(200)
client // remove initial value, execute again
.click(`${paramSelector} .json-schema-form-item-remove`)
.pause(400)
.click(`${paramSelector} .parameter__empty_value_toggle input`)
.click("button.btn.execute.opblock-control__btn")
.expect.element("textarea.curl").text
.to.contain(`GET "http://localhost:3230/emptyValueParams?arr="`)
})
})
})
})

View File

@@ -0,0 +1,126 @@
---
swagger: '2.0'
info:
version: 0.0.1
title: DDErl REST interface
description: RESTful access to IMEM DB and DDErl
schemes:
- http
- https
securityDefinitions:
basicAuth:
type: basic
description: HTTP Basic Authentication Username:Password
basePath: "/dderlrest/0.0.1"
paths:
"/sql/":
get:
tags:
- sql
security:
- basicAuth: []
summary: execute sql
operationId: execSql
description: Prepare and execute SQL statements
parameters:
- name: x-irest-conn
in: header
required: false
description: ErlImem connection identifier
type: string
produces:
- application/json
responses:
'200':
description: OK
schema:
type: object
headers:
x-irest-conn:
description: ErlImem connection identifier
type: string
'403':
description: Malformed/Invalid
schema:
"$ref": "#/definitions/ErrorResponse"
definitions:
ErrorResponse:
readOnly: true
type: object
required:
- errorCode
- errorMessage
- errorDetails
properties:
errorCode:
description: Error Code
type: number
example: 1400
errorMessage:
description: Error Message
type: string
example: malformed
errorDetails:
description: Error Details
type: string
example: mandatory properties missing or bad type
ViewParams:
readOnly: true
type: array
items:
type: object
required:
- typ
- value
- name
properties:
name:
description: Name
type: string
example: ":atom_user"
value:
description: Value
type: string
example: system
typ:
description: Datatype
type: string
enum:
- atom
- binary
- raw
- blob
- rowid
- binstr
- clob
- nclob
- varchar2
- nvarchar2
- char
- nchar
- boolean
- datetime
- decimal
- float
- fun
- integer
- ipaddr
- list
- map
- number
- pid
- ref
- string
- term
- binterm
- timestamp
- tuple
- userid
example: atom
dir:
description: Direction
type: string
enum:
- in
- out
default: in

View File

@@ -0,0 +1,64 @@
openapi: "3.0.0"
paths:
/regularParams:
get:
parameters:
- name: int
in: query
schema:
type: integer
- name: str
in: query
schema:
type: string
- name: num
in: query
schema:
type: number
- name: bool
in: query
schema:
type: boolean
- name: arr
in: query
schema:
type: array
items:
type: string
responses:
200:
description: ok
/emptyValueParams:
get:
parameters:
- name: int
in: query
schema:
type: integer
allowEmptyValue: true
- name: str
in: query
schema:
type: string
allowEmptyValue: true
- name: num
in: query
schema:
type: number
allowEmptyValue: true
- name: bool
in: query
schema:
type: boolean
allowEmptyValue: true
- name: arr
in: query
schema:
type: array
items:
type: string
allowEmptyValue: true
responses:
200:
description: ok

View File

@@ -0,0 +1,57 @@
swagger: "2.0"
consumes:
- application/json
- multipart/form-data
paths:
/regularParams:
get:
parameters:
- name: int
in: query
type: integer
- name: str
in: query
type: string
- name: num
in: query
type: number
- name: bool
in: query
type: boolean
- name: arr
in: query
type: array
items:
type: string
responses:
200:
description: ok
/emptyValueParams:
get:
parameters:
- name: int
in: query
type: integer
allowEmptyValue: true
- name: str
in: query
type: string
allowEmptyValue: true
- name: num
in: query
type: number
allowEmptyValue: true
- name: bool
in: query
type: boolean
allowEmptyValue: true
- name: arr
in: query
type: array
items:
type: string
allowEmptyValue: true
responses:
200:
description: ok