diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 934a838e6a..4801ac4c55 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -485,6 +485,25 @@ describe.each([ ) 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", () => { diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 876b52c0a6..d65980a7cb 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -17,6 +17,7 @@ import { TableSchema, User, Row, + RelationshipType, } from "@budibase/types" import _ from "lodash" import tk from "timekeeper" @@ -73,7 +74,7 @@ describe.each([ }) async function createTable(schema: TableSchema) { - table = await config.api.table.save( + return await config.api.table.save( tableForDatasource(datasource, { schema }) ) } @@ -186,7 +187,7 @@ describe.each([ describe("boolean", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ isTrue: { name: "isTrue", type: FieldType.BOOLEAN }, }) await createRows([{ isTrue: true }, { isTrue: false }]) @@ -316,7 +317,7 @@ describe.each([ }) ) - await createTable({ + table = await createTable({ name: { name: "name", type: FieldType.STRING }, appointment: { name: "appointment", type: FieldType.DATETIME }, single_user: { @@ -592,7 +593,7 @@ describe.each([ describe.each([FieldType.STRING, FieldType.LONGFORM])("%s", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ name: { name: "name", type: FieldType.STRING }, }) await createRows([{ name: "foo" }, { name: "bar" }]) @@ -790,7 +791,7 @@ describe.each([ describe("numbers", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ age: { name: "age", type: FieldType.NUMBER }, }) await createRows([{ age: 1 }, { age: 10 }]) @@ -899,7 +900,7 @@ describe.each([ const JAN_10TH = "2020-01-10T00:00:00.000Z" beforeAll(async () => { - await createTable({ + table = await createTable({ dob: { name: "dob", type: FieldType.DATETIME }, }) @@ -1011,7 +1012,7 @@ describe.each([ describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ numbers: { name: "numbers", type: FieldType.ARRAY, @@ -1091,7 +1092,7 @@ describe.each([ const BIG = "9223372036854775807" beforeAll(async () => { - await createTable({ + table = await createTable({ num: { name: "num", type: FieldType.BIGINT }, }) await createRows([{ num: SMALL }, { num: MEDIUM }, { num: BIG }]) @@ -1182,7 +1183,7 @@ describe.each([ isInternal && describe("auto", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ auto: { name: "auto", type: FieldType.AUTO, @@ -1366,7 +1367,7 @@ describe.each([ describe("field name 1:name", () => { beforeAll(async () => { - await createTable({ + table = await createTable({ "1:name": { name: "1:name", type: FieldType.STRING }, }) await createRows([{ "1:name": "bar" }, { "1:name": "foo" }]) @@ -1382,4 +1383,52 @@ describe.each([ expectQuery({ equal: { "1:1:name": "none" } }).toFindNothing()) }) }) + + // This will never work for Lucene. + // TODO(samwho): fix for SQS + !isInternal && + 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 }] }])) + }) }) diff --git a/packages/server/src/integrations/tests/sql.spec.ts b/packages/server/src/integrations/tests/sql.spec.ts index 4fc964b320..9b84409e92 100644 --- a/packages/server/src/integrations/tests/sql.spec.ts +++ b/packages/server/src/integrations/tests/sql.spec.ts @@ -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 { return { endpoint: { @@ -146,24 +136,6 @@ describe("SQL query builder", () => { 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", () => { const query = sql._query(generateRelationshipJson({ schema: "production" })) expect(query).toEqual({ @@ -234,29 +206,4 @@ describe("SQL query builder", () => { sql: `select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1) where rownum <= :2) "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 *`, - }) - }) }) diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 77a6431335..7213cc66f1 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -37,7 +37,7 @@ export function tableForDatasource( ): Table { return merge( { - name: generator.guid(), + name: generator.guid().substring(0, 10), type: "table", sourceType: datasource ? TableSourceType.EXTERNAL diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 73176af6d8..4d91759be5 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -150,6 +150,12 @@ export async function inputProcessing( clonedRow[key] = coerce(value, field.type) } + if (field.type === FieldType.DATETIME) { + if (typeof clonedRow[key] === "string") { + clonedRow[key] = clonedRow[key].trim() + } + } + // remove any attachment urls, they are generated on read if (field.type === FieldType.ATTACHMENTS) { const attachments = clonedRow[key]