Merge pull request #5707 from Budibase/fix/mike-fixes-04-05
SQL columns with spaces LIKE fix and dynamic REST variable UI change
This commit is contained in:
commit
e7bb4a1fb7
|
@ -1,5 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, ModalContent, Modal, Body } from "@budibase/bbui"
|
import { Input, ModalContent, Modal, Body } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let dynamicVariables
|
export let dynamicVariables
|
||||||
export let datasource
|
export let datasource
|
||||||
|
@ -35,6 +38,7 @@
|
||||||
name = null
|
name = null
|
||||||
binding = null
|
binding = null
|
||||||
dynamicVariables[copiedName] = copiedBinding
|
dynamicVariables[copiedName] = copiedBinding
|
||||||
|
dispatch("change", dynamicVariables)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
import AccessLevelSelect from "components/integration/AccessLevelSelect.svelte"
|
import AccessLevelSelect from "components/integration/AccessLevelSelect.svelte"
|
||||||
import DynamicVariableModal from "../../_components/DynamicVariableModal.svelte"
|
import DynamicVariableModal from "../../_components/DynamicVariableModal.svelte"
|
||||||
import Placeholder from "assets/bb-spaceship.svg"
|
import Placeholder from "assets/bb-spaceship.svg"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep, isEqual } from "lodash/fp"
|
||||||
import { RawRestBodyTypes } from "constants/backend"
|
import { RawRestBodyTypes } from "constants/backend"
|
||||||
|
|
||||||
let query, datasource
|
let query, datasource
|
||||||
|
@ -47,6 +47,7 @@
|
||||||
let response, schema, enabledHeaders
|
let response, schema, enabledHeaders
|
||||||
let authConfigId
|
let authConfigId
|
||||||
let dynamicVariables, addVariableModal, varBinding
|
let dynamicVariables, addVariableModal, varBinding
|
||||||
|
let baseQuery, baseDatasource, baseVariables
|
||||||
|
|
||||||
$: datasourceType = datasource?.source
|
$: datasourceType = datasource?.source
|
||||||
$: integrationInfo = $integrations[datasourceType]
|
$: integrationInfo = $integrations[datasourceType]
|
||||||
|
@ -62,6 +63,15 @@
|
||||||
$: hasSchema =
|
$: hasSchema =
|
||||||
Object.keys(schema || {}).length !== 0 ||
|
Object.keys(schema || {}).length !== 0 ||
|
||||||
Object.keys(query?.schema || {}).length !== 0
|
Object.keys(query?.schema || {}).length !== 0
|
||||||
|
$: baseQuery = !baseQuery ? cloneDeep(query) : baseQuery
|
||||||
|
$: baseDatasource = !baseDatasource ? cloneDeep(datasource) : baseDatasource
|
||||||
|
$: baseVariables = !baseVariables
|
||||||
|
? cloneDeep(dynamicVariables)
|
||||||
|
: baseVariables
|
||||||
|
$: hasChanged =
|
||||||
|
!isEqual(baseQuery, query) ||
|
||||||
|
!isEqual(baseDatasource, datasource) ||
|
||||||
|
!isEqual(baseVariables, dynamicVariables)
|
||||||
|
|
||||||
function getSelectedQuery() {
|
function getSelectedQuery() {
|
||||||
return cloneDeep(
|
return cloneDeep(
|
||||||
|
@ -120,6 +130,9 @@
|
||||||
datasource.config.dynamicVariables = rebuildVariables(saveId)
|
datasource.config.dynamicVariables = rebuildVariables(saveId)
|
||||||
datasource = await datasources.save(datasource)
|
datasource = await datasources.save(datasource)
|
||||||
}
|
}
|
||||||
|
baseQuery = query
|
||||||
|
baseDatasource = datasource
|
||||||
|
baseVariables = dynamicVariables
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error saving query`)
|
notifications.error(`Error saving query`)
|
||||||
}
|
}
|
||||||
|
@ -299,6 +312,7 @@
|
||||||
{dynamicVariables}
|
{dynamicVariables}
|
||||||
bind:binding={varBinding}
|
bind:binding={varBinding}
|
||||||
bind:this={addVariableModal}
|
bind:this={addVariableModal}
|
||||||
|
on:change={saveQuery}
|
||||||
/>
|
/>
|
||||||
{#if query && queryConfig}
|
{#if query && queryConfig}
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
|
@ -332,7 +346,7 @@
|
||||||
</div>
|
</div>
|
||||||
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
|
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={!query.name}
|
disabled={!query.name || !hasChanged}
|
||||||
cta
|
cta
|
||||||
on:click={saveQuery}
|
on:click={saveQuery}
|
||||||
tooltip={!hasSchema
|
tooltip={!hasSchema
|
||||||
|
|
|
@ -21,6 +21,31 @@ type KnexQuery = Knex.QueryBuilder | Knex
|
||||||
const MIN_ISO_DATE = "0000-00-00T00:00:00.000Z"
|
const MIN_ISO_DATE = "0000-00-00T00:00:00.000Z"
|
||||||
const MAX_ISO_DATE = "9999-00-00T00:00:00.000Z"
|
const MAX_ISO_DATE = "9999-00-00T00:00:00.000Z"
|
||||||
|
|
||||||
|
function likeKey(client: string, key: string): string {
|
||||||
|
if (!key.includes(" ")) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
let start: string, end: string
|
||||||
|
switch (client) {
|
||||||
|
case SqlClients.MY_SQL:
|
||||||
|
start = end = "`"
|
||||||
|
break
|
||||||
|
case SqlClients.ORACLE:
|
||||||
|
case SqlClients.POSTGRES:
|
||||||
|
start = end = '"'
|
||||||
|
break
|
||||||
|
case SqlClients.MS_SQL:
|
||||||
|
start = "["
|
||||||
|
end = "]"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw "Unknown client"
|
||||||
|
}
|
||||||
|
const parts = key.split(".")
|
||||||
|
key = parts.map(part => `${start}${part}${end}`).join(".")
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
function parse(input: any) {
|
function parse(input: any) {
|
||||||
if (Array.isArray(input)) {
|
if (Array.isArray(input)) {
|
||||||
return JSON.stringify(input)
|
return JSON.stringify(input)
|
||||||
|
@ -125,7 +150,9 @@ class InternalBuilder {
|
||||||
} else {
|
} else {
|
||||||
const rawFnc = `${fnc}Raw`
|
const rawFnc = `${fnc}Raw`
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
query = query[rawFnc](`LOWER(${key}) LIKE ?`, [`%${value}%`])
|
query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [
|
||||||
|
`%${value}%`,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,16 @@ const HTML_SWAPS = {
|
||||||
">": ">",
|
">": ">",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isObject(value) {
|
||||||
|
if (value == null || typeof value !== "object") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
value.toString() === "[object Object]" ||
|
||||||
|
(value.length > 0 && typeof value[0] === "object")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const HELPERS = [
|
const HELPERS = [
|
||||||
// external helpers
|
// external helpers
|
||||||
new Helper(HelperFunctionNames.OBJECT, value => {
|
new Helper(HelperFunctionNames.OBJECT, value => {
|
||||||
|
@ -22,11 +32,7 @@ const HELPERS = [
|
||||||
new Helper(HelperFunctionNames.JS, processJS, false),
|
new Helper(HelperFunctionNames.JS, processJS, false),
|
||||||
// this help is applied to all statements
|
// this help is applied to all statements
|
||||||
new Helper(HelperFunctionNames.ALL, (value, { __opts }) => {
|
new Helper(HelperFunctionNames.ALL, (value, { __opts }) => {
|
||||||
if (
|
if (isObject(value)) {
|
||||||
value != null &&
|
|
||||||
typeof value === "object" &&
|
|
||||||
value.toString() === "[object Object]"
|
|
||||||
) {
|
|
||||||
return new SafeString(JSON.stringify(value))
|
return new SafeString(JSON.stringify(value))
|
||||||
}
|
}
|
||||||
// null/undefined values produce bad results
|
// null/undefined values produce bad results
|
||||||
|
|
|
@ -64,9 +64,10 @@ module.exports.processors = [
|
||||||
return statement
|
return statement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const testHelper = possibleHelper.trim().toLowerCase()
|
||||||
if (
|
if (
|
||||||
!noHelpers &&
|
!noHelpers &&
|
||||||
HelperNames().some(option => option.includes(possibleHelper))
|
HelperNames().some(option => testHelper === option.toLowerCase())
|
||||||
) {
|
) {
|
||||||
insideStatement = `(${insideStatement})`
|
insideStatement = `(${insideStatement})`
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,16 @@ describe("Test that the object processing works correctly", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("check returning objects", () => {
|
||||||
|
it("should handle an array of objects", async () => {
|
||||||
|
const json = [{a: 1},{a: 2}]
|
||||||
|
const output = await processString("{{ testing }}", {
|
||||||
|
testing: json
|
||||||
|
})
|
||||||
|
expect(output).toEqual(JSON.stringify(json))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("check the utility functions", () => {
|
describe("check the utility functions", () => {
|
||||||
it("should return false for an invalid template string", () => {
|
it("should return false for an invalid template string", () => {
|
||||||
const valid = isValid("{{ table1.thing prop }}")
|
const valid = isValid("{{ table1.thing prop }}")
|
||||||
|
|
|
@ -30,6 +30,11 @@ describe("Handling context properties with spaces in their name", () => {
|
||||||
})
|
})
|
||||||
expect(output).toBe("testcase 1")
|
expect(output).toBe("testcase 1")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should allow the use of a", async () => {
|
||||||
|
const output = await processString("{{ a }}", { a: 1 })
|
||||||
|
expect(output).toEqual("1")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("attempt some complex problems", () => {
|
describe("attempt some complex problems", () => {
|
||||||
|
|
Loading…
Reference in New Issue