@budibase/server 2.3.18-alpha.15 → 2.3.18-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.3.18-alpha.15",
4
+ "version": "2.3.18-alpha.17",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -43,11 +43,11 @@
43
43
  "license": "GPL-3.0",
44
44
  "dependencies": {
45
45
  "@apidevtools/swagger-parser": "10.0.3",
46
- "@budibase/backend-core": "2.3.18-alpha.15",
47
- "@budibase/client": "2.3.18-alpha.15",
48
- "@budibase/pro": "2.3.18-alpha.14",
49
- "@budibase/string-templates": "2.3.18-alpha.15",
50
- "@budibase/types": "2.3.18-alpha.15",
46
+ "@budibase/backend-core": "2.3.18-alpha.17",
47
+ "@budibase/client": "2.3.18-alpha.17",
48
+ "@budibase/pro": "2.3.18-alpha.16",
49
+ "@budibase/string-templates": "2.3.18-alpha.17",
50
+ "@budibase/types": "2.3.18-alpha.17",
51
51
  "@bull-board/api": "3.7.0",
52
52
  "@bull-board/koa": "3.9.4",
53
53
  "@elastic/elasticsearch": "7.10.0",
@@ -174,5 +174,5 @@
174
174
  "optionalDependencies": {
175
175
  "oracledb": "5.3.0"
176
176
  },
177
- "gitHead": "78287e11da6973374962afac59b35c598a54efc3"
177
+ "gitHead": "f703ebb73409290aa949c240c51b95c7ce3826c8"
178
178
  }
@@ -11,8 +11,8 @@ import { OAuth2Client } from "google-auth-library"
11
11
  import { buildExternalTableId } from "./utils"
12
12
  import { DataSourceOperation, FieldTypes } from "../constants"
13
13
  import { GoogleSpreadsheet } from "google-spreadsheet"
14
+ import fetch from "node-fetch"
14
15
  import { configs, HTTPError } from "@budibase/backend-core"
15
- const fetch = require("node-fetch")
16
16
 
17
17
  interface GoogleSheetsConfig {
18
18
  spreadsheetId: string
@@ -111,7 +111,7 @@ const SCHEMA: Integration = {
111
111
 
112
112
  class GoogleSheetsIntegration implements DatasourcePlus {
113
113
  private readonly config: GoogleSheetsConfig
114
- private client: any
114
+ private client: GoogleSpreadsheet
115
115
  public tables: Record<string, Table> = {}
116
116
  public schemaErrors: Record<string, string> = {}
117
117
 
@@ -203,7 +203,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
203
203
 
204
204
  async buildSchema(datasourceId: string) {
205
205
  await this.connect()
206
- const sheets = await this.client.sheetsByIndex
206
+ const sheets = this.client.sheetsByIndex
207
207
  const tables: Record<string, Table> = {}
208
208
  for (let sheet of sheets) {
209
209
  // must fetch rows to determine schema
@@ -286,7 +286,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
286
286
  async updateTable(table?: any) {
287
287
  try {
288
288
  await this.connect()
289
- const sheet = await this.client.sheetsByTitle[table.name]
289
+ const sheet = this.client.sheetsByTitle[table.name]
290
290
  await sheet.loadHeaderRow()
291
291
 
292
292
  if (table._rename) {
@@ -300,10 +300,17 @@ class GoogleSheetsIntegration implements DatasourcePlus {
300
300
  }
301
301
  await sheet.setHeaderRow(headers)
302
302
  } else {
303
- let newField = Object.keys(table.schema).find(
303
+ const updatedHeaderValues = [...sheet.headerValues]
304
+
305
+ const newField = Object.keys(table.schema).find(
304
306
  key => !sheet.headerValues.includes(key)
305
307
  )
306
- await sheet.setHeaderRow([...sheet.headerValues, newField])
308
+
309
+ if (newField) {
310
+ updatedHeaderValues.push(newField)
311
+ }
312
+
313
+ await sheet.setHeaderRow(updatedHeaderValues)
307
314
  }
308
315
  } catch (err) {
309
316
  console.error("Error updating table in google sheets", err)
@@ -314,7 +321,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
314
321
  async deleteTable(sheet: any) {
315
322
  try {
316
323
  await this.connect()
317
- const sheetToDelete = await this.client.sheetsByTitle[sheet]
324
+ const sheetToDelete = this.client.sheetsByTitle[sheet]
318
325
  return await sheetToDelete.delete()
319
326
  } catch (err) {
320
327
  console.error("Error deleting table in google sheets", err)
@@ -325,7 +332,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
325
332
  async create(query: { sheet: string; row: any }) {
326
333
  try {
327
334
  await this.connect()
328
- const sheet = await this.client.sheetsByTitle[query.sheet]
335
+ const sheet = this.client.sheetsByTitle[query.sheet]
329
336
  const rowToInsert =
330
337
  typeof query.row === "string" ? JSON.parse(query.row) : query.row
331
338
  const row = await sheet.addRow(rowToInsert)
@@ -341,7 +348,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
341
348
  async read(query: { sheet: string }) {
342
349
  try {
343
350
  await this.connect()
344
- const sheet = await this.client.sheetsByTitle[query.sheet]
351
+ const sheet = this.client.sheetsByTitle[query.sheet]
345
352
  const rows = await sheet.getRows()
346
353
  const headerValues = sheet.headerValues
347
354
  const response = []
@@ -360,7 +367,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
360
367
  async update(query: { sheet: string; rowIndex: number; row: any }) {
361
368
  try {
362
369
  await this.connect()
363
- const sheet = await this.client.sheetsByTitle[query.sheet]
370
+ const sheet = this.client.sheetsByTitle[query.sheet]
364
371
  const rows = await sheet.getRows()
365
372
  const row = rows[query.rowIndex]
366
373
  if (row) {
@@ -384,7 +391,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
384
391
 
385
392
  async delete(query: { sheet: string; rowIndex: number }) {
386
393
  await this.connect()
387
- const sheet = await this.client.sheetsByTitle[query.sheet]
394
+ const sheet = this.client.sheetsByTitle[query.sheet]
388
395
  const rows = await sheet.getRows()
389
396
  const row = rows[query.rowIndex]
390
397
  if (row) {
@@ -0,0 +1,122 @@
1
+ import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"
2
+
3
+ jest.mock("google-auth-library")
4
+ const { OAuth2Client } = require("google-auth-library")
5
+
6
+ const setCredentialsMock = jest.fn()
7
+ const getAccessTokenMock = jest.fn()
8
+
9
+ OAuth2Client.mockImplementation(() => {
10
+ return {
11
+ setCredentials: setCredentialsMock,
12
+ getAccessToken: getAccessTokenMock,
13
+ }
14
+ })
15
+
16
+ jest.mock("google-spreadsheet")
17
+ const { GoogleSpreadsheet } = require("google-spreadsheet")
18
+
19
+ const sheetsByTitle: { [title: string]: GoogleSpreadsheetWorksheet } = {}
20
+
21
+ GoogleSpreadsheet.mockImplementation(() => {
22
+ return {
23
+ useOAuth2Client: jest.fn(),
24
+ loadInfo: jest.fn(),
25
+ sheetsByTitle,
26
+ }
27
+ })
28
+
29
+ import { structures } from "@budibase/backend-core/tests"
30
+ import TestConfiguration from "../../tests/utilities/TestConfiguration"
31
+ import GoogleSheetsIntegration from "../googlesheets"
32
+ import { FieldType, Table, TableSchema } from "../../../../types/src/documents"
33
+
34
+ describe("Google Sheets Integration", () => {
35
+ let integration: any,
36
+ config = new TestConfiguration()
37
+
38
+ beforeEach(async () => {
39
+ integration = new GoogleSheetsIntegration.integration({
40
+ spreadsheetId: "randomId",
41
+ auth: {
42
+ appId: "appId",
43
+ accessToken: "accessToken",
44
+ refreshToken: "refreshToken",
45
+ },
46
+ })
47
+ await config.init()
48
+ })
49
+
50
+ function createBasicTable(name: string, columns: string[]): Table {
51
+ return {
52
+ name,
53
+ schema: {
54
+ ...columns.reduce((p, c) => {
55
+ p[c] = {
56
+ name: c,
57
+ type: FieldType.STRING,
58
+ constraints: {
59
+ type: "string",
60
+ },
61
+ }
62
+ return p
63
+ }, {} as TableSchema),
64
+ },
65
+ }
66
+ }
67
+
68
+ function createSheet({
69
+ headerValues,
70
+ }: {
71
+ headerValues: string[]
72
+ }): GoogleSpreadsheetWorksheet {
73
+ return {
74
+ // to ignore the unmapped fields
75
+ ...({} as any),
76
+ loadHeaderRow: jest.fn(),
77
+ headerValues,
78
+ setHeaderRow: jest.fn(),
79
+ }
80
+ }
81
+
82
+ describe("update table", () => {
83
+ test("adding a new field will be adding a new header row", async () => {
84
+ await config.doInContext(structures.uuid(), async () => {
85
+ const tableColumns = ["name", "description", "new field"]
86
+ const table = createBasicTable(structures.uuid(), tableColumns)
87
+
88
+ const sheet = createSheet({ headerValues: ["name", "description"] })
89
+ sheetsByTitle[table.name] = sheet
90
+ await integration.updateTable(table)
91
+
92
+ expect(sheet.loadHeaderRow).toBeCalledTimes(1)
93
+ expect(sheet.setHeaderRow).toBeCalledTimes(1)
94
+ expect(sheet.setHeaderRow).toBeCalledWith(tableColumns)
95
+ })
96
+ })
97
+
98
+ test("removing an existing field will not remove the data from the spreadsheet", async () => {
99
+ await config.doInContext(structures.uuid(), async () => {
100
+ const tableColumns = ["name"]
101
+ const table = createBasicTable(structures.uuid(), tableColumns)
102
+
103
+ const sheet = createSheet({
104
+ headerValues: ["name", "description", "location"],
105
+ })
106
+ sheetsByTitle[table.name] = sheet
107
+ await integration.updateTable(table)
108
+
109
+ expect(sheet.loadHeaderRow).toBeCalledTimes(1)
110
+ expect(sheet.setHeaderRow).toBeCalledTimes(1)
111
+ expect(sheet.setHeaderRow).toBeCalledWith([
112
+ "name",
113
+ "description",
114
+ "location",
115
+ ])
116
+
117
+ // No undefineds are sent
118
+ expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(3)
119
+ })
120
+ })
121
+ })
122
+ })