diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index d3044d17eb..9781b97972 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -53,7 +53,7 @@ describe("Google Sheets Integration", () => { describe("create", () => { it("creates a new table", async () => { - await config.api.table.save({ + const table = await config.api.table.save({ name: "Test Table", type: "table", sourceId: datasource._id!, @@ -76,11 +76,138 @@ describe("Google Sheets Integration", () => { }, }) + expect(table.name).toEqual("Test Table") + expect(mock.cell("A1")).toEqual("name") expect(mock.cell("B1")).toEqual("description") expect(mock.cell("A2")).toEqual(null) expect(mock.cell("B2")).toEqual(null) }) + + it("can handle multiple tables", async () => { + const table1 = await config.api.table.save({ + name: "Test Table 1", + type: "table", + sourceId: datasource._id!, + sourceType: TableSourceType.EXTERNAL, + schema: { + one: { + name: "one", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + }, + }) + + const table2 = await config.api.table.save({ + name: "Test Table 2", + type: "table", + sourceId: datasource._id!, + sourceType: TableSourceType.EXTERNAL, + schema: { + two: { + name: "two", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + }, + }) + + expect(table1.name).toEqual("Test Table 1") + expect(table2.name).toEqual("Test Table 2") + + expect(mock.cell("Test Table 1!A1")).toEqual("one") + expect(mock.cell("Test Table 1!A2")).toEqual(null) + expect(mock.cell("Test Table 2!A1")).toEqual("two") + expect(mock.cell("Test Table 2!A2")).toEqual(null) + }) + }) + + describe("read", () => { + let table: Table + beforeEach(async () => { + table = await config.api.table.save({ + name: "Test Table", + type: "table", + sourceId: datasource._id!, + sourceType: TableSourceType.EXTERNAL, + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + description: { + name: "description", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + }, + }) + + await config.api.row.bulkImport(table._id!, { + rows: [ + { + name: "Test Contact 1", + description: "original description 1", + }, + { + name: "Test Contact 2", + description: "original description 2", + }, + ], + }) + }) + + it("can read table details", async () => { + const response = await config.api.table.get(table._id!) + expect(response.name).toEqual("Test Table") + expect(response.schema).toEqual({ + name: { + name: "name", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + description: { + name: "description", + type: FieldType.STRING, + constraints: { + type: "string", + }, + }, + }) + }) + + it("can read table rows", async () => { + const rows = await config.api.row.fetch(table._id!) + expect(rows.length).toEqual(2) + expect(rows[0].name).toEqual("Test Contact 1") + expect(rows[0].description).toEqual("original description 1") + expect(rows[0]._id).toEqual("%5B2%5D") + expect(rows[1].name).toEqual("Test Contact 2") + expect(rows[1].description).toEqual("original description 2") + expect(rows[1]._id).toEqual("%5B3%5D") + }) + + it("can get a specific row", async () => { + const row1 = await config.api.row.get(table._id!, "2") + expect(row1.name).toEqual("Test Contact 1") + expect(row1.description).toEqual("original description 1") + + const row2 = await config.api.row.get(table._id!, "3") + expect(row2.name).toEqual("Test Contact 2") + expect(row2.description).toEqual("original description 2") + }) }) describe("update", () => { diff --git a/packages/server/src/integrations/tests/utils/googlesheets.ts b/packages/server/src/integrations/tests/utils/googlesheets.ts index 0f92fea6cd..c58066bee5 100644 --- a/packages/server/src/integrations/tests/utils/googlesheets.ts +++ b/packages/server/src/integrations/tests/utils/googlesheets.ts @@ -534,9 +534,57 @@ export class GoogleSheetsMock { } values.push(this.cellValue(cell)) } + valueRange.values.push(values) } + return this.trimValueRange(valueRange) + } + + // When Google Sheets returns a value range, it will trim the data down to the + // smallest possible size. It does all of the following: + // + // 1. Converts cells in non-empty rows up to the first value to empty strings. + // 2. Removes all cells after the last non-empty cell in a row. + // 3. Removes all rows after the last non-empty row. + // 4. Rows that are before the first non-empty row that are empty are replaced with []. + // + // We replicate this behaviour here. + private trimValueRange(valueRange: ValueRange): ValueRange { + for (const row of valueRange.values) { + if (row.every(v => v == null)) { + row.splice(0, row.length) + continue + } + + for (let i = row.length - 1; i >= 0; i--) { + const cell = row[i] + if (cell == null) { + row.pop() + } else { + break + } + } + + for (let i = 0; i < row.length; i++) { + const cell = row[i] + if (cell == null) { + row[i] = "" + } else { + break + } + } + } + + for (let i = valueRange.values.length - 1; i >= 0; i--) { + const row = valueRange.values[i] + if (row.length === 0) { + valueRange.values.pop() + } else { + break + } + } + return valueRange }