From 43de204ca28d15e66ef4671c11f8d18bf53004d2 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 09:58:31 +0100 Subject: [PATCH 1/9] Default values failing test. --- .../server/src/api/routes/tests/row.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 27a0d0983e..b96fdd836c 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -207,6 +207,24 @@ describe.each([ await assertRowUsage(isInternal ? rowUsage + 1 : rowUsage) }) + it("creates a new row with a default value successfully", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + description: { + name: "description", + type: FieldType.STRING, + default: "default description", + }, + }, + }) + ) + + const row = await config.api.row.save(table._id!, {}) + expect(row.name).toEqual("Test Contact") + expect(row.description).toEqual("default description") + }) + it("fails to create a row for a table that does not exist", async () => { const rowUsage = await getRowUsage() await config.api.row.save("1234567", {}, { status: 404 }) From 12911db06e3153a1ee8278622c6397bf73b76538 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 10:43:58 +0100 Subject: [PATCH 2/9] Process primitive default values. --- packages/server/src/api/routes/tests/row.spec.ts | 3 +-- .../server/src/utilities/rowProcessor/index.ts | 15 ++++++++++++--- packages/types/src/documents/app/table/schema.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index b96fdd836c..482f80a587 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -207,7 +207,7 @@ describe.each([ await assertRowUsage(isInternal ? rowUsage + 1 : rowUsage) }) - it("creates a new row with a default value successfully", async () => { + it.only("creates a new row with a default value successfully", async () => { const table = await config.api.table.save( saveTableRequest({ schema: { @@ -221,7 +221,6 @@ describe.each([ ) const row = await config.api.row.save(table._id!, {}) - expect(row.name).toEqual("Test Contact") expect(row.description).toEqual("default description") }) diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 73176af6d8..402295ba96 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -88,7 +88,14 @@ export async function processAutoColumn( break } } - return { table, row } +} + +async function processDeafultValues(table: Table, row: Row) { + for (let [key, schema] of Object.entries(table.schema)) { + if ("default" in schema && row[key] == null) { + row[key] = schema.default + } + } } /** @@ -182,8 +189,10 @@ export async function inputProcessing( clonedRow._rev = row._rev } - // handle auto columns - this returns an object like {table, row} - return processAutoColumn(userId, table, clonedRow, opts) + await processAutoColumn(userId, table, clonedRow, opts) + await processDeafultValues(table, clonedRow) + + return { table, row: clonedRow } } /** diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 38424b26b6..3a2ddf097f 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -81,11 +81,13 @@ export interface NumberFieldMetadata extends Omit { toTable: string toKey: string } + default?: string } export interface JsonFieldMetadata extends Omit { type: FieldType.JSON subtype?: JsonFieldSubType.ARRAY + default?: string } export interface DateFieldMetadata extends Omit { @@ -94,17 +96,25 @@ export interface DateFieldMetadata extends Omit { timeOnly?: boolean dateOnly?: boolean subtype?: AutoFieldSubType.CREATED_AT | AutoFieldSubType.UPDATED_AT + default?: string } export interface LongFormFieldMetadata extends BaseFieldSchema { type: FieldType.LONGFORM useRichText?: boolean | null + default?: string +} + +export interface StringFieldMetadata extends BaseFieldSchema { + type: FieldType.STRING + default?: string } export interface FormulaFieldMetadata extends BaseFieldSchema { type: FieldType.FORMULA formula: string formulaType?: FormulaType + default?: string } export interface BBReferenceFieldMetadata @@ -171,6 +181,7 @@ interface OtherFieldMetadata extends BaseFieldSchema { | FieldType.BB_REFERENCE | FieldType.BB_REFERENCE_SINGLE | FieldType.ATTACHMENTS + | FieldType.STRING > } @@ -182,6 +193,7 @@ export type FieldSchema = | FormulaFieldMetadata | NumberFieldMetadata | LongFormFieldMetadata + | StringFieldMetadata | BBReferenceFieldMetadata | JsonFieldMetadata | AttachmentFieldMetadata From 297e9003ca0470d63790a533044f80ec57cf27a6 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 11:10:30 +0100 Subject: [PATCH 3/9] Support bindings in default values. --- .../server/src/api/routes/tests/row.spec.ts | 55 +++++++++++++------ .../src/utilities/rowProcessor/index.ts | 6 +- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 482f80a587..27c363e223 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -207,23 +207,6 @@ describe.each([ await assertRowUsage(isInternal ? rowUsage + 1 : rowUsage) }) - it.only("creates a new row with a default value successfully", async () => { - const table = await config.api.table.save( - saveTableRequest({ - schema: { - description: { - name: "description", - type: FieldType.STRING, - default: "default description", - }, - }, - }) - ) - - const row = await config.api.row.save(table._id!, {}) - expect(row.description).toEqual("default description") - }) - it("fails to create a row for a table that does not exist", async () => { const rowUsage = await getRowUsage() await config.api.row.save("1234567", {}, { status: 404 }) @@ -567,6 +550,44 @@ describe.each([ expect(row.name).toEqual(`{ "foo": "2023-01-26T11:48:57.000Z" }`) }) + + describe.only("default values", () => { + it("creates a new row with a default value successfully", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + description: { + name: "description", + type: FieldType.STRING, + default: "default description", + }, + }, + }) + ) + + const row = await config.api.row.save(table._id!, {}) + expect(row.description).toEqual("default description") + }) + + it("can use bindings in default values", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + description: { + name: "description", + type: FieldType.STRING, + default: `{{ date now "YYYY-MM-DDTHH:mm:ss" }}`, + }, + }, + }) + ) + + await tk.withFreeze(new Date("2023-01-26T11:48:57.000Z"), async () => { + const row = await config.api.row.save(table._id!, {}) + expect(row.description).toEqual("2023-01-26T11:48:57") + }) + }) + }) }) describe("get", () => { diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 402295ba96..0765873389 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -19,6 +19,7 @@ import { } from "./bbReferenceProcessor" import { isExternalTableID } from "../../integrations/utils" import { helpers } from "@budibase/shared-core" +import { processString } from "@budibase/string-templates" export * from "./utils" export * from "./attachments" @@ -92,8 +93,9 @@ export async function processAutoColumn( async function processDeafultValues(table: Table, row: Row) { for (let [key, schema] of Object.entries(table.schema)) { - if ("default" in schema && row[key] == null) { - row[key] = schema.default + if ("default" in schema && schema.default != null && row[key] == null) { + const processed = await processString(schema.default, {}) + row[key] = coerce(processed, schema.type) } } } From ee0c4187c8aff973105156b3e89a5b166b1eeaef Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 12:09:01 +0100 Subject: [PATCH 4/9] Better error handling of invalid default values. --- .../server/src/api/routes/tests/row.spec.ts | 169 +++++++++++++++--- .../src/utilities/rowProcessor/index.ts | 12 +- .../server/src/utilities/rowProcessor/map.ts | 8 +- 3 files changed, 160 insertions(+), 29 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 27c363e223..d96059697a 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -552,39 +552,156 @@ describe.each([ }) describe.only("default values", () => { - it("creates a new row with a default value successfully", async () => { - const table = await config.api.table.save( - saveTableRequest({ - schema: { - description: { - name: "description", - type: FieldType.STRING, - default: "default description", + describe("string column", () => { + beforeAll(async () => { + table = await config.api.table.save( + saveTableRequest({ + schema: { + description: { + name: "description", + type: FieldType.STRING, + default: "default description", + }, }, - }, - }) - ) + }) + ) + }) - const row = await config.api.row.save(table._id!, {}) - expect(row.description).toEqual("default description") + it("creates a new row with a default value successfully", async () => { + const row = await config.api.row.save(table._id!, {}) + expect(row.description).toEqual("default description") + }) + + it("does not use default value if value specified", async () => { + const row = await config.api.row.save(table._id!, { + description: "specified description", + }) + expect(row.description).toEqual("specified description") + }) + + it("uses the default value if value is null", async () => { + const row = await config.api.row.save(table._id!, { + description: null, + }) + expect(row.description).toEqual("default description") + }) + + it("uses the default value if value is undefined", async () => { + const row = await config.api.row.save(table._id!, { + description: undefined, + }) + expect(row.description).toEqual("default description") + }) }) - it("can use bindings in default values", async () => { - const table = await config.api.table.save( - saveTableRequest({ - schema: { - description: { - name: "description", - type: FieldType.STRING, - default: `{{ date now "YYYY-MM-DDTHH:mm:ss" }}`, + describe("number column", () => { + beforeAll(async () => { + table = await config.api.table.save( + saveTableRequest({ + schema: { + age: { + name: "age", + type: FieldType.NUMBER, + default: "25", + }, }, - }, - }) - ) + }) + ) + }) - await tk.withFreeze(new Date("2023-01-26T11:48:57.000Z"), async () => { + it("creates a new row with a default value successfully", async () => { const row = await config.api.row.save(table._id!, {}) - expect(row.description).toEqual("2023-01-26T11:48:57") + expect(row.age).toEqual(25) + }) + + it("does not use default value if value specified", async () => { + const row = await config.api.row.save(table._id!, { + age: 30, + }) + expect(row.age).toEqual(30) + }) + }) + + describe("bindings", () => { + describe("string column", () => { + beforeAll(async () => { + table = await config.api.table.save( + saveTableRequest({ + schema: { + description: { + name: "description", + type: FieldType.STRING, + default: `{{ date now "YYYY-MM-DDTHH:mm:ss" }}`, + }, + }, + }) + ) + }) + + it("can use bindings in default values", async () => { + const row = await config.api.row.save(table._id!, {}) + expect(row.description).toMatch( + /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/ + ) + }) + + it("does not use default value if value specified", async () => { + const row = await config.api.row.save(table._id!, { + description: "specified description", + }) + expect(row.description).toEqual("specified description") + }) + }) + + describe("number column", () => { + beforeAll(async () => { + table = await config.api.table.save( + saveTableRequest({ + schema: { + age: { + name: "age", + type: FieldType.NUMBER, + default: `{{ sum 10 10 5 }}`, + }, + }, + }) + ) + }) + + it("can use bindings in default values", async () => { + const row = await config.api.row.save(table._id!, {}) + expect(row.age).toEqual(25) + }) + + describe("invalid default value", () => { + beforeAll(async () => { + table = await config.api.table.save( + saveTableRequest({ + schema: { + age: { + name: "age", + type: FieldType.NUMBER, + default: `{{ capitalize "invalid" }}`, + }, + }, + }) + ) + }) + + it("throws an error when invalid default value", async () => { + await config.api.row.save( + table._id!, + {}, + { + status: 400, + body: { + message: + "Invalid default value for field 'age' - Invalid number value \"Invalid\"", + }, + } + ) + }) + }) }) }) }) diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 0765873389..308a34fa9c 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -1,6 +1,6 @@ import * as linkRows from "../../db/linkedRows" import { fixAutoColumnSubType, processFormulas } from "./utils" -import { objectStore, utils } from "@budibase/backend-core" +import { HTTPError, objectStore, utils } from "@budibase/backend-core" import { InternalTables } from "../../db/utils" import { TYPE_TRANSFORM_MAP } from "./map" import { @@ -95,7 +95,15 @@ async function processDeafultValues(table: Table, row: Row) { for (let [key, schema] of Object.entries(table.schema)) { if ("default" in schema && schema.default != null && row[key] == null) { const processed = await processString(schema.default, {}) - row[key] = coerce(processed, schema.type) + + try { + row[key] = coerce(processed, schema.type) + } catch (err: any) { + throw new HTTPError( + `Invalid default value for field '${key}' - ${err.message}`, + 400 + ) + } } } } diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index ccaf07ee96..18c53744f9 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -91,7 +91,13 @@ export const TYPE_TRANSFORM_MAP: any = { [null]: null, //@ts-ignore [undefined]: undefined, - parse: (n: any) => parseFloat(n), + parse: (n: any) => { + const parsed = parseFloat(n) + if (isNaN(parsed)) { + throw new Error(`Invalid number value "${n}"`) + } + return parsed + }, }, [FieldType.BIGINT]: { "": null, From 18acaccfcb57a3e08bccb44d944f9797cd63d3f2 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 16:26:15 +0100 Subject: [PATCH 5/9] Current User binding and tests. --- .../server/src/api/routes/tests/row.spec.ts | 76 +++++++++++++++++++ .../src/utilities/rowProcessor/index.ts | 25 +++++- .../server/src/utilities/rowProcessor/map.ts | 7 +- 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index d96059697a..c4399560af 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -622,6 +622,48 @@ describe.each([ }) }) + describe("date column", () => { + it("creates a row with a default value successfully", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + date: { + name: "date", + type: FieldType.DATETIME, + default: "2023-01-26T11:48:57.000Z", + }, + }, + }) + ) + const row = await config.api.row.save(table._id!, {}) + expect(row.date).toEqual("2023-01-26T11:48:57.000Z") + }) + + it("gives an error if the default value is invalid", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + date: { + name: "date", + type: FieldType.DATETIME, + default: "invalid", + }, + }, + }) + ) + await config.api.row.save( + table._id!, + {}, + { + status: 400, + body: { + message: `Invalid default value for field 'date' - Invalid date value: "invalid"`, + }, + } + ) + }) + }) + describe("bindings", () => { describe("string column", () => { beforeAll(async () => { @@ -651,6 +693,40 @@ describe.each([ }) expect(row.description).toEqual("specified description") }) + + it("can bind the current user", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + user: { + name: "user", + type: FieldType.STRING, + default: `{{ [Current User]._id }}`, + }, + }, + }) + ) + const row = await config.api.row.save(table._id!, {}) + expect(row.user).toEqual(config.getUser()._id) + }) + + it("cannot access current user password", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + user: { + name: "user", + type: FieldType.STRING, + default: `{{ user.password }}`, + }, + }, + }) + ) + const row = await config.api.row.save(table._id!, {}) + // For some reason it's null for internal tables, and undefined for + // external. + expect(row.user == null).toBe(true) + }) }) describe("number column", () => { diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 308a34fa9c..54d86d55ff 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -1,14 +1,22 @@ import * as linkRows from "../../db/linkedRows" import { fixAutoColumnSubType, processFormulas } from "./utils" -import { HTTPError, objectStore, utils } from "@budibase/backend-core" +import { + cache, + context, + HTTPError, + objectStore, + utils, +} from "@budibase/backend-core" import { InternalTables } from "../../db/utils" import { TYPE_TRANSFORM_MAP } from "./map" import { AutoFieldSubType, FieldType, + IdentityType, Row, RowAttachment, Table, + User, } from "@budibase/types" import { cloneDeep } from "lodash/fp" import { @@ -92,9 +100,22 @@ export async function processAutoColumn( } async function processDeafultValues(table: Table, row: Row) { + const ctx: { ["Current User"]?: User; user?: User } = {} + + const identity = context.getIdentity() + if (identity) { + if (identity._id && identity.type === IdentityType.USER) { + const user = await cache.user.getUser(identity._id) + delete user.password + + ctx["Current User"] = user + ctx.user = user + } + } + for (let [key, schema] of Object.entries(table.schema)) { if ("default" in schema && schema.default != null && row[key] == null) { - const processed = await processString(schema.default, {}) + const processed = await processString(schema.default, ctx) try { row[key] = coerce(processed, schema.type) diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index 18c53744f9..8d1c4a9ebd 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -115,8 +115,13 @@ export const TYPE_TRANSFORM_MAP: any = { parse: (date: any) => { if (date instanceof Date) { return date.toISOString() + } else { + const parsed = new Date(date) + if (isNaN(parsed.getTime())) { + throw new Error(`Invalid date value: "${date}"`) + } + return date } - return date }, }, [FieldType.ATTACHMENTS]: { From e39a5b0d7ecc45b6cfc3bad449f6fcaac55d6d65 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 16:44:43 +0100 Subject: [PATCH 6/9] Add test for creating a row through views. --- packages/server/src/api/routes/tests/row.spec.ts | 4 +++- packages/server/src/api/routes/tests/viewV2.spec.ts | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c4399560af..8cabdf5e0f 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -551,7 +551,9 @@ describe.each([ expect(row.name).toEqual(`{ "foo": "2023-01-26T11:48:57.000Z" }`) }) - describe.only("default values", () => { + describe("default values", () => { + let table: Table + describe("string column", () => { beforeAll(async () => { table = await config.api.table.save( diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index e9853e5dff..3edbc24365 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1022,6 +1022,11 @@ describe.each([ schema: { one: { type: FieldType.STRING, name: "one" }, two: { type: FieldType.STRING, name: "two" }, + default: { + type: FieldType.STRING, + name: "default", + default: "default", + }, }, }) ) @@ -1042,11 +1047,13 @@ describe.each([ _viewId: view.id, one: "foo", two: "bar", + default: "ohnoes", }) const row = await config.api.row.get(table._id!, newRow._id!) expect(row.one).toBeUndefined() expect(row.two).toEqual("bar") + expect(row.default).toEqual("default") }) it("can't persist readonly columns", async () => { From 9542c497deab71753b4540cea010bfc3148109d1 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 16:46:20 +0100 Subject: [PATCH 7/9] Create a test for required column with default value. --- packages/server/src/api/routes/tests/row.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 8cabdf5e0f..5ce16a54d5 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -563,6 +563,9 @@ describe.each([ name: "description", type: FieldType.STRING, default: "default description", + constraints: { + presence: true, + }, }, }, }) From 372153bc978e2923511e23c6e9546c12f970645b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Jul 2024 17:27:03 +0100 Subject: [PATCH 8/9] Accommodate time values in datetime coercion. --- packages/server/src/utilities/rowProcessor/map.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index 8d1c4a9ebd..d86e0fe4d2 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -1,5 +1,7 @@ import { FieldType } from "@budibase/types" +const TIME_REGEX = /^(?:\d{2}:)?(?:\d{2}:)(?:\d{2})$/ + const parseArrayString = (value: any) => { if (typeof value === "string") { if (value === "") { @@ -115,6 +117,8 @@ export const TYPE_TRANSFORM_MAP: any = { parse: (date: any) => { if (date instanceof Date) { return date.toISOString() + } else if (typeof date === "string" && TIME_REGEX.test(date)) { + return date } else { const parsed = new Date(date) if (isNaN(parsed.getTime())) { From c64d76eb845e43650195fa4067ec1f217e7b3d41 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 19 Jul 2024 15:07:58 +0100 Subject: [PATCH 9/9] Respond to PR comments. --- packages/backend-core/src/sql/utils.ts | 5 +++++ .../server/src/utilities/rowProcessor/index.ts | 16 +++++++--------- .../server/src/utilities/rowProcessor/map.ts | 5 ++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/backend-core/src/sql/utils.ts b/packages/backend-core/src/sql/utils.ts index e73c6ac445..67b5d2081b 100644 --- a/packages/backend-core/src/sql/utils.ts +++ b/packages/backend-core/src/sql/utils.ts @@ -8,6 +8,7 @@ const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}` const ROW_ID_REGEX = /^\[.*]$/g const ENCODED_SPACE = encodeURIComponent(" ") const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/ +const TIME_REGEX = /^(?:\d{2}:)?(?:\d{2}:)(?:\d{2})$/ export function isExternalTableID(tableId: string) { return tableId.startsWith(DocumentType.DATASOURCE + SEPARATOR) @@ -147,6 +148,10 @@ export function isValidFilter(value: any) { return value != null && value !== "" } +export function isValidTime(value: string) { + return TIME_REGEX.test(value) +} + export function sqlLog(client: string, query: string, values?: any[]) { if (!environment.SQL_LOGGING_ENABLE) { return diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 54d86d55ff..71de056814 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -99,18 +99,16 @@ export async function processAutoColumn( } } -async function processDeafultValues(table: Table, row: Row) { +async function processDefaultValues(table: Table, row: Row) { const ctx: { ["Current User"]?: User; user?: User } = {} const identity = context.getIdentity() - if (identity) { - if (identity._id && identity.type === IdentityType.USER) { - const user = await cache.user.getUser(identity._id) - delete user.password + if (identity?._id && identity.type === IdentityType.USER) { + const user = await cache.user.getUser(identity._id) + delete user.password - ctx["Current User"] = user - ctx.user = user - } + ctx["Current User"] = user + ctx.user = user } for (let [key, schema] of Object.entries(table.schema)) { @@ -221,7 +219,7 @@ export async function inputProcessing( } await processAutoColumn(userId, table, clonedRow, opts) - await processDeafultValues(table, clonedRow) + await processDefaultValues(table, clonedRow) return { table, row: clonedRow } } diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index d86e0fe4d2..db52c4718c 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -1,7 +1,6 @@ +import { sql } from "@budibase/backend-core" import { FieldType } from "@budibase/types" -const TIME_REGEX = /^(?:\d{2}:)?(?:\d{2}:)(?:\d{2})$/ - const parseArrayString = (value: any) => { if (typeof value === "string") { if (value === "") { @@ -117,7 +116,7 @@ export const TYPE_TRANSFORM_MAP: any = { parse: (date: any) => { if (date instanceof Date) { return date.toISOString() - } else if (typeof date === "string" && TIME_REGEX.test(date)) { + } else if (typeof date === "string" && sql.utils.isValidTime(date)) { return date } else { const parsed = new Date(date)