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:
Michael Drury 2022-05-11 10:34:36 +01:00 committed by GitHub
commit e7bb4a1fb7
7 changed files with 76 additions and 9 deletions

View File

@ -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>

View File

@ -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

View File

@ -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}%`,
])
} }
}) })
} }

View File

@ -13,6 +13,16 @@ const HTML_SWAPS = {
">": "&gt;", ">": "&gt;",
} }
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

View File

@ -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})`
} }

View File

@ -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 }}")

View File

@ -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", () => {