Merge branch 'master' of github.com:Budibase/budibase into grid-ux-improvements
This commit is contained in:
commit
8378afb3c2
|
@ -177,7 +177,7 @@ export const stringifyDate = (
|
||||||
const year = value.year()
|
const year = value.year()
|
||||||
const month = `${value.month() + 1}`.padStart(2, "0")
|
const month = `${value.month() + 1}`.padStart(2, "0")
|
||||||
const day = `${value.date()}`.padStart(2, "0")
|
const day = `${value.date()}`.padStart(2, "0")
|
||||||
return `${year}-${month}-${day}T00:00:00.000`
|
return `${year}-${month}-${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise use a normal ISO string with time and timezone
|
// Otherwise use a normal ISO string with time and timezone
|
||||||
|
|
|
@ -237,7 +237,12 @@
|
||||||
|
|
||||||
const onChangeJSValue = e => {
|
const onChangeJSValue = e => {
|
||||||
jsValue = encodeJSBinding(e.detail)
|
jsValue = encodeJSBinding(e.detail)
|
||||||
updateValue(jsValue)
|
if (!e.detail?.trim()) {
|
||||||
|
// Don't bother saving empty values as JS
|
||||||
|
updateValue(null)
|
||||||
|
} else {
|
||||||
|
updateValue(jsValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
runtimeToReadableBinding,
|
runtimeToReadableBinding,
|
||||||
} from "dataBinding"
|
} from "dataBinding"
|
||||||
|
|
||||||
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
||||||
import { createEventDispatcher, setContext } from "svelte"
|
import { createEventDispatcher, setContext } from "svelte"
|
||||||
import { isJSBinding } from "@budibase/string-templates"
|
import { isJSBinding } from "@budibase/string-templates"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { screenStore, componentStore } from "stores/builder"
|
import { screenStore, componentStore, navigationStore } from "stores/builder"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import {
|
import {
|
||||||
ActionMenu,
|
ActionMenu,
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
import ScreenDetailsModal from "components/design/ScreenDetailsModal.svelte"
|
import ScreenDetailsModal from "components/design/ScreenDetailsModal.svelte"
|
||||||
import sanitizeUrl from "helpers/sanitizeUrl"
|
import sanitizeUrl from "helpers/sanitizeUrl"
|
||||||
import { makeComponentUnique } from "helpers/components"
|
import { makeComponentUnique } from "helpers/components"
|
||||||
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
export let screenId
|
export let screenId
|
||||||
|
|
||||||
|
@ -48,6 +49,13 @@
|
||||||
try {
|
try {
|
||||||
// Create the screen
|
// Create the screen
|
||||||
await screenStore.save(duplicateScreen)
|
await screenStore.save(duplicateScreen)
|
||||||
|
|
||||||
|
// Add new screen to navigation
|
||||||
|
await navigationStore.saveLink(
|
||||||
|
duplicateScreen.routing.route,
|
||||||
|
capitalise(duplicateScreen.routing.route.split("/")[1]),
|
||||||
|
duplicateScreen.routing.roleId
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error duplicating screen")
|
notifications.error("Error duplicating screen")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import active from "svelte-spa-router/active"
|
import active from "svelte-spa-router/active"
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
|
@ -13,8 +13,6 @@
|
||||||
export let navStateStore
|
export let navStateStore
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const sdk = getContext("sdk")
|
|
||||||
const { linkable } = sdk
|
|
||||||
|
|
||||||
let renderKey
|
let renderKey
|
||||||
|
|
||||||
|
@ -46,10 +44,9 @@
|
||||||
styled
|
styled
|
||||||
-->
|
-->
|
||||||
<a
|
<a
|
||||||
href={url}
|
href="#{url}"
|
||||||
on:click={onClickLink}
|
on:click={onClickLink}
|
||||||
use:active={url}
|
use:active={url}
|
||||||
use:linkable
|
|
||||||
class:active={false}
|
class:active={false}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
@ -73,10 +70,9 @@
|
||||||
{#each subLinks || [] as subLink}
|
{#each subLinks || [] as subLink}
|
||||||
{#if subLink.internalLink}
|
{#if subLink.internalLink}
|
||||||
<a
|
<a
|
||||||
href={subLink.url}
|
href="#{subLink.url}"
|
||||||
on:click={onClickLink}
|
on:click={onClickLink}
|
||||||
use:active={subLink.url}
|
use:active={subLink.url}
|
||||||
use:linkable
|
|
||||||
>
|
>
|
||||||
{subLink.text}
|
{subLink.text}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -238,7 +238,13 @@ const triggerAutomationHandler = async action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const navigationHandler = action => {
|
const navigationHandler = action => {
|
||||||
const { url, peek, externalNewTab } = action.parameters
|
let { url, peek, externalNewTab, type } = action.parameters
|
||||||
|
|
||||||
|
// Ensure in-app navigation starts with a slash
|
||||||
|
if (type === "screen" && url && !url.startsWith("/")) {
|
||||||
|
url = `/${url}`
|
||||||
|
}
|
||||||
|
|
||||||
routeStore.actions.navigate(url, peek, externalNewTab)
|
routeStore.actions.navigate(url, peek, externalNewTab)
|
||||||
closeSidePanelHandler()
|
closeSidePanelHandler()
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,6 +485,25 @@ describe.each([
|
||||||
)
|
)
|
||||||
expect(response.message).toBe("Cannot create new user entry.")
|
expect(response.message).toBe("Cannot create new user entry.")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should not mis-parse date string out of JSON", async () => {
|
||||||
|
const table = await config.api.table.save(
|
||||||
|
saveTableRequest({
|
||||||
|
schema: {
|
||||||
|
name: {
|
||||||
|
type: FieldType.STRING,
|
||||||
|
name: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const row = await config.api.row.save(table._id!, {
|
||||||
|
name: `{ "foo": "2023-01-26T11:48:57.000Z" }`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(row.name).toEqual(`{ "foo": "2023-01-26T11:48:57.000Z" }`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("get", () => {
|
describe("get", () => {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
TableSchema,
|
TableSchema,
|
||||||
User,
|
User,
|
||||||
Row,
|
Row,
|
||||||
|
RelationshipType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
|
@ -73,7 +74,7 @@ describe.each([
|
||||||
})
|
})
|
||||||
|
|
||||||
async function createTable(schema: TableSchema) {
|
async function createTable(schema: TableSchema) {
|
||||||
table = await config.api.table.save(
|
return await config.api.table.save(
|
||||||
tableForDatasource(datasource, { schema })
|
tableForDatasource(datasource, { schema })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -190,7 +191,7 @@ describe.each([
|
||||||
|
|
||||||
describe("boolean", () => {
|
describe("boolean", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
isTrue: { name: "isTrue", type: FieldType.BOOLEAN },
|
isTrue: { name: "isTrue", type: FieldType.BOOLEAN },
|
||||||
})
|
})
|
||||||
await createRows([{ isTrue: true }, { isTrue: false }])
|
await createRows([{ isTrue: true }, { isTrue: false }])
|
||||||
|
@ -320,7 +321,7 @@ describe.each([
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
await createTable({
|
table = await createTable({
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
appointment: { name: "appointment", type: FieldType.DATETIME },
|
appointment: { name: "appointment", type: FieldType.DATETIME },
|
||||||
single_user: {
|
single_user: {
|
||||||
|
@ -596,7 +597,7 @@ describe.each([
|
||||||
|
|
||||||
describe.each([FieldType.STRING, FieldType.LONGFORM])("%s", () => {
|
describe.each([FieldType.STRING, FieldType.LONGFORM])("%s", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
})
|
})
|
||||||
await createRows([{ name: "foo" }, { name: "bar" }])
|
await createRows([{ name: "foo" }, { name: "bar" }])
|
||||||
|
@ -716,6 +717,20 @@ describe.each([
|
||||||
expectQuery({
|
expectQuery({
|
||||||
range: { name: { low: "g", high: "h" } },
|
range: { name: { low: "g", high: "h" } },
|
||||||
}).toFindNothing())
|
}).toFindNothing())
|
||||||
|
|
||||||
|
!isLucene &&
|
||||||
|
it("ignores low if it's an empty object", () =>
|
||||||
|
expectQuery({
|
||||||
|
// @ts-ignore
|
||||||
|
range: { name: { low: {}, high: "z" } },
|
||||||
|
}).toContainExactly([{ name: "foo" }, { name: "bar" }]))
|
||||||
|
|
||||||
|
!isLucene &&
|
||||||
|
it("ignores high if it's an empty object", () =>
|
||||||
|
expectQuery({
|
||||||
|
// @ts-ignore
|
||||||
|
range: { name: { low: "a", high: {} } },
|
||||||
|
}).toContainExactly([{ name: "foo" }, { name: "bar" }]))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("empty", () => {
|
describe("empty", () => {
|
||||||
|
@ -780,7 +795,7 @@ describe.each([
|
||||||
|
|
||||||
describe("numbers", () => {
|
describe("numbers", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
age: { name: "age", type: FieldType.NUMBER },
|
age: { name: "age", type: FieldType.NUMBER },
|
||||||
})
|
})
|
||||||
await createRows([{ age: 1 }, { age: 10 }])
|
await createRows([{ age: 1 }, { age: 10 }])
|
||||||
|
@ -889,7 +904,7 @@ describe.each([
|
||||||
const JAN_10TH = "2020-01-10T00:00:00.000Z"
|
const JAN_10TH = "2020-01-10T00:00:00.000Z"
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
dob: { name: "dob", type: FieldType.DATETIME },
|
dob: { name: "dob", type: FieldType.DATETIME },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1012,7 +1027,7 @@ describe.each([
|
||||||
const NULL_TIME__ID = `null_time__id`
|
const NULL_TIME__ID = `null_time__id`
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
timeid: { name: "timeid", type: FieldType.STRING },
|
timeid: { name: "timeid", type: FieldType.STRING },
|
||||||
time: { name: "time", type: FieldType.DATETIME, timeOnly: true },
|
time: { name: "time", type: FieldType.DATETIME, timeOnly: true },
|
||||||
})
|
})
|
||||||
|
@ -1154,7 +1169,7 @@ describe.each([
|
||||||
|
|
||||||
describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => {
|
describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
numbers: {
|
numbers: {
|
||||||
name: "numbers",
|
name: "numbers",
|
||||||
type: FieldType.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
|
@ -1234,7 +1249,7 @@ describe.each([
|
||||||
const BIG = "9223372036854775807"
|
const BIG = "9223372036854775807"
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
num: { name: "num", type: FieldType.BIGINT },
|
num: { name: "num", type: FieldType.BIGINT },
|
||||||
})
|
})
|
||||||
await createRows([{ num: SMALL }, { num: MEDIUM }, { num: BIG }])
|
await createRows([{ num: SMALL }, { num: MEDIUM }, { num: BIG }])
|
||||||
|
@ -1325,7 +1340,7 @@ describe.each([
|
||||||
isInternal &&
|
isInternal &&
|
||||||
describe("auto", () => {
|
describe("auto", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
auto: {
|
auto: {
|
||||||
name: "auto",
|
name: "auto",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
|
@ -1452,6 +1467,25 @@ describe.each([
|
||||||
{ auto: 2 },
|
{ auto: 2 },
|
||||||
{ auto: 1 },
|
{ auto: 1 },
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
// This is important for pagination. The order of results must always
|
||||||
|
// be stable or pagination will break. We don't want the user to need
|
||||||
|
// to specify an order for pagination to work.
|
||||||
|
it("is stable without a sort specified", async () => {
|
||||||
|
let { rows } = await config.api.row.search(table._id!, {
|
||||||
|
tableId: table._id!,
|
||||||
|
query: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const response = await config.api.row.search(table._id!, {
|
||||||
|
tableId: table._id!,
|
||||||
|
limit: 1,
|
||||||
|
query: {},
|
||||||
|
})
|
||||||
|
expect(response.rows).toEqual(rows)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO(samwho): fix for SQS
|
// TODO(samwho): fix for SQS
|
||||||
|
@ -1490,7 +1524,7 @@ describe.each([
|
||||||
|
|
||||||
describe("field name 1:name", () => {
|
describe("field name 1:name", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await createTable({
|
table = await createTable({
|
||||||
"1:name": { name: "1:name", type: FieldType.STRING },
|
"1:name": { name: "1:name", type: FieldType.STRING },
|
||||||
})
|
})
|
||||||
await createRows([{ "1:name": "bar" }, { "1:name": "foo" }])
|
await createRows([{ "1:name": "bar" }, { "1:name": "foo" }])
|
||||||
|
@ -1506,4 +1540,51 @@ describe.each([
|
||||||
expectQuery({ equal: { "1:1:name": "none" } }).toFindNothing())
|
expectQuery({ equal: { "1:1:name": "none" } }).toFindNothing())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This will never work for Lucene.
|
||||||
|
!isLucene &&
|
||||||
|
describe("relations", () => {
|
||||||
|
let otherTable: Table
|
||||||
|
let rows: Row[]
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
otherTable = await createTable({
|
||||||
|
one: { name: "one", type: FieldType.STRING },
|
||||||
|
})
|
||||||
|
table = await createTable({
|
||||||
|
two: { name: "two", type: FieldType.STRING },
|
||||||
|
other: {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
name: "other",
|
||||||
|
fieldName: "other",
|
||||||
|
tableId: otherTable._id!,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
rows = await Promise.all([
|
||||||
|
config.api.row.save(otherTable._id!, { one: "foo" }),
|
||||||
|
config.api.row.save(otherTable._id!, { one: "bar" }),
|
||||||
|
])
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
config.api.row.save(table._id!, {
|
||||||
|
two: "foo",
|
||||||
|
other: [rows[0]._id],
|
||||||
|
}),
|
||||||
|
config.api.row.save(table._id!, {
|
||||||
|
two: "bar",
|
||||||
|
other: [rows[1]._id],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("can search through relations", () =>
|
||||||
|
expectQuery({
|
||||||
|
equal: { [`${otherTable.name}.one`]: "foo" },
|
||||||
|
}).toContainExactly([{ two: "foo", other: [{ _id: rows[0]._id }] }]))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -56,16 +56,6 @@ function generateReadJson({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateCreateJson(table = TABLE_NAME, body = {}): QueryJson {
|
|
||||||
return {
|
|
||||||
endpoint: endpoint(table, "CREATE"),
|
|
||||||
meta: {
|
|
||||||
table: TABLE,
|
|
||||||
},
|
|
||||||
body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRelationshipJson(config: { schema?: string } = {}): QueryJson {
|
function generateRelationshipJson(config: { schema?: string } = {}): QueryJson {
|
||||||
return {
|
return {
|
||||||
endpoint: {
|
endpoint: {
|
||||||
|
@ -146,24 +136,6 @@ describe("SQL query builder", () => {
|
||||||
sql = new Sql(client, limit)
|
sql = new Sql(client, limit)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should allow filtering on a related field", () => {
|
|
||||||
const query = sql._query(
|
|
||||||
generateReadJson({
|
|
||||||
filters: {
|
|
||||||
equal: {
|
|
||||||
age: 10,
|
|
||||||
"task.name": "task 1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
// order of bindings changes because relationship filters occur outside inner query
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: [10, limit, "task 1"],
|
|
||||||
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."age" = $1 limit $2) as "${TABLE_NAME}" where "task"."name" = $3`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should add the schema to the LEFT JOIN", () => {
|
it("should add the schema to the LEFT JOIN", () => {
|
||||||
const query = sql._query(generateRelationshipJson({ schema: "production" }))
|
const query = sql._query(generateRelationshipJson({ schema: "production" }))
|
||||||
expect(query).toEqual({
|
expect(query).toEqual({
|
||||||
|
@ -190,44 +162,6 @@ describe("SQL query builder", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should ignore high range value if it is an empty object", () => {
|
|
||||||
const query = sql._query(
|
|
||||||
generateReadJson({
|
|
||||||
filters: {
|
|
||||||
range: {
|
|
||||||
dob: {
|
|
||||||
low: "2000-01-01 00:00:00",
|
|
||||||
high: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: ["2000-01-01 00:00:00", 500],
|
|
||||||
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."dob" >= $1 limit $2) as "${TABLE_NAME}"`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should ignore low range value if it is an empty object", () => {
|
|
||||||
const query = sql._query(
|
|
||||||
generateReadJson({
|
|
||||||
filters: {
|
|
||||||
range: {
|
|
||||||
dob: {
|
|
||||||
low: {},
|
|
||||||
high: "2010-01-01 00:00:00",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: ["2010-01-01 00:00:00", 500],
|
|
||||||
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."dob" <= $1 limit $2) as "${TABLE_NAME}"`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should lowercase the values for Oracle LIKE statements", () => {
|
it("should lowercase the values for Oracle LIKE statements", () => {
|
||||||
let query = new Sql(SqlClient.ORACLE, limit)._query(
|
let query = new Sql(SqlClient.ORACLE, limit)._query(
|
||||||
generateReadJson({
|
generateReadJson({
|
||||||
|
@ -272,44 +206,4 @@ describe("SQL query builder", () => {
|
||||||
sql: `select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1) where rownum <= :2) "test"`,
|
sql: `select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1) where rownum <= :2) "test"`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should sort SQL Server tables by the primary key if no sort data is provided", () => {
|
|
||||||
let query = new Sql(SqlClient.MS_SQL, limit)._query(
|
|
||||||
generateReadJson({
|
|
||||||
sort: {},
|
|
||||||
paginate: {
|
|
||||||
limit: 10,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: [10],
|
|
||||||
sql: `select * from (select top (@p0) * from [test] order by [test].[id] asc) as [test]`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should not parse JSON string as Date", () => {
|
|
||||||
let query = new Sql(SqlClient.POSTGRES, limit)._query(
|
|
||||||
generateCreateJson(TABLE_NAME, {
|
|
||||||
name: '{ "created_at":"2023-09-09T03:21:06.024Z" }',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: ['{ "created_at":"2023-09-09T03:21:06.024Z" }'],
|
|
||||||
sql: `insert into "test" ("name") values ($1) returning *`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should parse and trim valid string as Date", () => {
|
|
||||||
const dateObj = new Date("2023-09-09T03:21:06.024Z")
|
|
||||||
let query = new Sql(SqlClient.POSTGRES, limit)._query(
|
|
||||||
generateCreateJson(TABLE_NAME, {
|
|
||||||
name: " 2023-09-09T03:21:06.024Z ",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(query).toEqual({
|
|
||||||
bindings: [dateObj],
|
|
||||||
sql: `insert into "test" ("name") values ($1) returning *`,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,8 +55,8 @@ function buildInternalFieldList(
|
||||||
return fieldList
|
return fieldList
|
||||||
}
|
}
|
||||||
|
|
||||||
function tableInFilter(name: string) {
|
function tableNameInFieldRegex(tableName: string) {
|
||||||
return `:${name}.`
|
return new RegExp(`^${tableName}.|:${tableName}.`, "g")
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupFilters(filters: SearchFilters, tables: Table[]) {
|
function cleanupFilters(filters: SearchFilters, tables: Table[]) {
|
||||||
|
@ -72,15 +72,13 @@ function cleanupFilters(filters: SearchFilters, tables: Table[]) {
|
||||||
// relationship, switch to table ID
|
// relationship, switch to table ID
|
||||||
const tableRelated = tables.find(
|
const tableRelated = tables.find(
|
||||||
table =>
|
table =>
|
||||||
table.originalName && key.includes(tableInFilter(table.originalName))
|
table.originalName &&
|
||||||
|
key.match(tableNameInFieldRegex(table.originalName))
|
||||||
)
|
)
|
||||||
if (tableRelated && tableRelated.originalName) {
|
if (tableRelated && tableRelated.originalName) {
|
||||||
filter[
|
// only replace the first, not replaceAll
|
||||||
key.replace(
|
filter[key.replace(tableRelated.originalName, tableRelated._id!)] =
|
||||||
tableInFilter(tableRelated.originalName),
|
filter[key]
|
||||||
tableInFilter(tableRelated._id!)
|
|
||||||
)
|
|
||||||
] = filter[key]
|
|
||||||
delete filter[key]
|
delete filter[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export function tableForDatasource(
|
||||||
): Table {
|
): Table {
|
||||||
return merge(
|
return merge(
|
||||||
{
|
{
|
||||||
name: generator.guid(),
|
name: generator.guid().substring(0, 10),
|
||||||
type: "table",
|
type: "table",
|
||||||
sourceType: datasource
|
sourceType: datasource
|
||||||
? TableSourceType.EXTERNAL
|
? TableSourceType.EXTERNAL
|
||||||
|
|
|
@ -105,6 +105,9 @@ export function processDates<T extends Row | Row[]>(
|
||||||
if (schema.type !== FieldType.DATETIME) {
|
if (schema.type !== FieldType.DATETIME) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (schema.dateOnly) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
if (!schema.timeOnly && !schema.ignoreTimezones) {
|
||||||
datesWithTZ.push(column)
|
datesWithTZ.push(column)
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,11 @@ export function parse(rows: Rows, schema: TableSchema): Rows {
|
||||||
if (columnType === FieldType.NUMBER) {
|
if (columnType === FieldType.NUMBER) {
|
||||||
// If provided must be a valid number
|
// If provided must be a valid number
|
||||||
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
parsedRow[columnName] = columnData ? Number(columnData) : columnData
|
||||||
} else if (columnType === FieldType.DATETIME && !columnSchema.timeOnly) {
|
} else if (
|
||||||
|
columnType === FieldType.DATETIME &&
|
||||||
|
!columnSchema.timeOnly &&
|
||||||
|
!columnSchema.dateOnly
|
||||||
|
) {
|
||||||
// If provided must be a valid date
|
// If provided must be a valid date
|
||||||
parsedRow[columnName] = columnData
|
parsedRow[columnName] = columnData
|
||||||
? new Date(columnData).toISOString()
|
? new Date(columnData).toISOString()
|
||||||
|
|
Loading…
Reference in New Issue