@budibase/server 2.4.38 → 2.4.40

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.
@@ -1,25 +1,22 @@
1
1
  import {
2
2
  DatasourceFieldType,
3
3
  DatasourcePlus,
4
- FieldType,
5
4
  Integration,
6
- Operation,
7
5
  PaginationJson,
8
6
  QueryJson,
9
7
  QueryType,
10
- Row,
11
8
  SearchFilters,
12
9
  SortJson,
13
10
  Table,
14
- TableRequest,
11
+ TableSchema,
15
12
  } from "@budibase/types"
16
13
  import { OAuth2Client } from "google-auth-library"
17
- import { buildExternalTableId, finaliseExternalTables } from "./utils"
14
+ import { buildExternalTableId } from "./utils"
15
+ import { DataSourceOperation, FieldTypes } from "../constants"
18
16
  import { GoogleSpreadsheet } from "google-spreadsheet"
19
17
  import fetch from "node-fetch"
20
18
  import { configs, HTTPError } from "@budibase/backend-core"
21
19
  import { dataFilters } from "@budibase/shared-core"
22
- import { GOOGLE_SHEETS_PRIMARY_KEY } from "../constants"
23
20
 
24
21
  interface GoogleSheetsConfig {
25
22
  spreadsheetId: string
@@ -42,17 +39,6 @@ interface AuthTokenResponse {
42
39
  access_token: string
43
40
  }
44
41
 
45
- const ALLOWED_TYPES = [
46
- FieldType.STRING,
47
- FieldType.FORMULA,
48
- FieldType.NUMBER,
49
- FieldType.LONGFORM,
50
- FieldType.DATETIME,
51
- FieldType.OPTIONS,
52
- FieldType.BOOLEAN,
53
- FieldType.BARCODEQR,
54
- ]
55
-
56
42
  const SCHEMA: Integration = {
57
43
  plus: true,
58
44
  auth: {
@@ -213,90 +199,73 @@ class GoogleSheetsIntegration implements DatasourcePlus {
213
199
 
214
200
  this.client.useOAuth2Client(oauthClient)
215
201
  await this.client.loadInfo()
216
- } catch (err: any) {
217
- // this happens for xlsx imports
218
- if (err.message?.includes("operation is not supported")) {
219
- err.message =
220
- "This operation is not supported - XLSX sheets must be converted."
221
- }
202
+ } catch (err) {
222
203
  console.error("Error connecting to google sheets", err)
223
204
  throw err
224
205
  }
225
206
  }
226
207
 
227
- getTableSchema(title: string, headerValues: string[], id?: string) {
228
- // base table
229
- const table: Table = {
230
- name: title,
231
- primary: [GOOGLE_SHEETS_PRIMARY_KEY],
232
- schema: {},
233
- }
234
- if (id) {
235
- table._id = id
236
- }
237
- // build schema from headers
238
- for (let header of headerValues) {
239
- table.schema[header] = {
240
- name: header,
241
- type: FieldType.STRING,
242
- }
243
- }
244
- return table
245
- }
246
-
247
- async buildSchema(datasourceId: string, entities: Record<string, Table>) {
208
+ async buildSchema(datasourceId: string) {
248
209
  await this.connect()
249
210
  const sheets = this.client.sheetsByIndex
250
211
  const tables: Record<string, Table> = {}
251
212
  for (let sheet of sheets) {
252
213
  // must fetch rows to determine schema
253
214
  await sheet.getRows()
215
+ // build schema
216
+ const schema: TableSchema = {}
254
217
 
255
- const id = buildExternalTableId(datasourceId, sheet.title)
256
- tables[sheet.title] = this.getTableSchema(
257
- sheet.title,
258
- sheet.headerValues,
259
- id
260
- )
218
+ // build schema from headers
219
+ for (let header of sheet.headerValues) {
220
+ schema[header] = {
221
+ name: header,
222
+ type: FieldTypes.STRING,
223
+ }
224
+ }
225
+
226
+ // create tables
227
+ tables[sheet.title] = {
228
+ _id: buildExternalTableId(datasourceId, sheet.title),
229
+ name: sheet.title,
230
+ primary: ["rowNumber"],
231
+ schema,
232
+ }
261
233
  }
262
- const final = finaliseExternalTables(tables, entities)
263
- this.tables = final.tables
264
- this.schemaErrors = final.errors
234
+
235
+ this.tables = tables
265
236
  }
266
237
 
267
238
  async query(json: QueryJson) {
268
239
  const sheet = json.endpoint.entityId
269
- switch (json.endpoint.operation) {
270
- case Operation.CREATE:
271
- return this.create({ sheet, row: json.body as Row })
272
- case Operation.BULK_CREATE:
273
- return this.createBulk({ sheet, rows: json.body as Row[] })
274
- case Operation.READ:
275
- return this.read({ ...json, sheet })
276
- case Operation.UPDATE:
277
- return this.update({
240
+
241
+ const handlers = {
242
+ [DataSourceOperation.CREATE]: () =>
243
+ this.create({ sheet, row: json.body }),
244
+ [DataSourceOperation.READ]: () => this.read({ ...json, sheet }),
245
+ [DataSourceOperation.UPDATE]: () =>
246
+ this.update({
278
247
  // exclude the header row and zero index
279
248
  rowIndex: json.extra?.idFilter?.equal?.rowNumber - 2,
280
249
  sheet,
281
250
  row: json.body,
282
- })
283
- case Operation.DELETE:
284
- return this.delete({
251
+ }),
252
+ [DataSourceOperation.DELETE]: () =>
253
+ this.delete({
285
254
  // exclude the header row and zero index
286
255
  rowIndex: json.extra?.idFilter?.equal?.rowNumber - 2,
287
256
  sheet,
288
- })
289
- case Operation.CREATE_TABLE:
290
- return this.createTable(json?.table?.name)
291
- case Operation.UPDATE_TABLE:
292
- return this.updateTable(json.table!)
293
- case Operation.DELETE_TABLE:
294
- return this.deleteTable(json?.table?.name)
295
- default:
296
- throw new Error(
297
- `GSheets integration does not support "${json.endpoint.operation}".`
298
- )
257
+ }),
258
+ [DataSourceOperation.CREATE_TABLE]: () =>
259
+ this.createTable(json?.table?.name),
260
+ [DataSourceOperation.UPDATE_TABLE]: () => this.updateTable(json.table),
261
+ [DataSourceOperation.DELETE_TABLE]: () =>
262
+ this.deleteTable(json?.table?.name),
299
263
  }
264
+
265
+ // @ts-ignore
266
+ const internalQueryMethod = handlers[json.endpoint.operation]
267
+
268
+ return await internalQueryMethod()
300
269
  }
301
270
 
302
271
  buildRowObject(headers: string[], values: string[], rowNumber: number) {
@@ -309,70 +278,47 @@ class GoogleSheetsIntegration implements DatasourcePlus {
309
278
  }
310
279
 
311
280
  async createTable(name?: string) {
312
- if (!name) {
313
- throw new Error("Must provide name for new sheet.")
314
- }
315
281
  try {
316
282
  await this.connect()
317
- return await this.client.addSheet({ title: name, headerValues: [name] })
283
+ return await this.client.addSheet({ title: name, headerValues: ["test"] })
318
284
  } catch (err) {
319
285
  console.error("Error creating new table in google sheets", err)
320
286
  throw err
321
287
  }
322
288
  }
323
289
 
324
- async updateTable(table: TableRequest) {
325
- await this.connect()
326
- const sheet = this.client.sheetsByTitle[table.name]
327
- await sheet.loadHeaderRow()
328
-
329
- if (table._rename) {
330
- const headers = []
331
- for (let header of sheet.headerValues) {
332
- if (header === table._rename.old) {
333
- headers.push(table._rename.updated)
334
- } else {
335
- headers.push(header)
290
+ async updateTable(table?: any) {
291
+ try {
292
+ await this.connect()
293
+ const sheet = this.client.sheetsByTitle[table.name]
294
+ await sheet.loadHeaderRow()
295
+
296
+ if (table._rename) {
297
+ const headers = []
298
+ for (let header of sheet.headerValues) {
299
+ if (header === table._rename.old) {
300
+ headers.push(table._rename.updated)
301
+ } else {
302
+ headers.push(header)
303
+ }
336
304
  }
337
- }
338
- try {
339
305
  await sheet.setHeaderRow(headers)
340
- } catch (err) {
341
- console.error("Error updating column name in google sheets", err)
342
- throw err
343
- }
344
- } else {
345
- const updatedHeaderValues = [...sheet.headerValues]
346
-
347
- // add new column - doesn't currently exist
348
- for (let [key, column] of Object.entries(table.schema)) {
349
- if (!ALLOWED_TYPES.includes(column.type)) {
350
- throw new Error(
351
- `Column type: ${column.type} not allowed for GSheets integration.`
352
- )
353
- }
354
- if (
355
- !sheet.headerValues.includes(key) &&
356
- column.type !== FieldType.FORMULA
357
- ) {
358
- updatedHeaderValues.push(key)
359
- }
360
- }
306
+ } else {
307
+ const updatedHeaderValues = [...sheet.headerValues]
361
308
 
362
- // clear out deleted columns
363
- for (let key of sheet.headerValues) {
364
- if (!Object.keys(table.schema).includes(key)) {
365
- const idx = updatedHeaderValues.indexOf(key)
366
- updatedHeaderValues.splice(idx, 1)
309
+ const newField = Object.keys(table.schema).find(
310
+ key => !sheet.headerValues.includes(key)
311
+ )
312
+
313
+ if (newField) {
314
+ updatedHeaderValues.push(newField)
367
315
  }
368
- }
369
316
 
370
- try {
371
317
  await sheet.setHeaderRow(updatedHeaderValues)
372
- } catch (err) {
373
- console.error("Error updating table in google sheets", err)
374
- throw err
375
318
  }
319
+ } catch (err) {
320
+ console.error("Error updating table in google sheets", err)
321
+ throw err
376
322
  }
377
323
  }
378
324
 
@@ -403,24 +349,6 @@ class GoogleSheetsIntegration implements DatasourcePlus {
403
349
  }
404
350
  }
405
351
 
406
- async createBulk(query: { sheet: string; rows: any[] }) {
407
- try {
408
- await this.connect()
409
- const sheet = this.client.sheetsByTitle[query.sheet]
410
- let rowsToInsert = []
411
- for (let row of query.rows) {
412
- rowsToInsert.push(typeof row === "string" ? JSON.parse(row) : row)
413
- }
414
- const rows = await sheet.addRows(rowsToInsert)
415
- return rows.map(row =>
416
- this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber)
417
- )
418
- } catch (err) {
419
- console.error("Error bulk writing to google sheets", err)
420
- throw err
421
- }
422
- }
423
-
424
352
  async read(query: {
425
353
  sheet: string
426
354
  filters?: SearchFilters
@@ -4,7 +4,6 @@ import { FieldTypes, BuildSchemaErrors, InvalidColumns } from "../constants"
4
4
 
5
5
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
6
6
  const ROW_ID_REGEX = /^\[.*]$/g
7
- const ENCODED_SPACE = encodeURIComponent(" ")
8
7
 
9
8
  const SQL_NUMBER_TYPE_MAP = {
10
9
  integer: FieldTypes.NUMBER,
@@ -80,10 +79,6 @@ export function isExternalTable(tableId: string) {
80
79
  }
81
80
 
82
81
  export function buildExternalTableId(datasourceId: string, tableName: string) {
83
- // encode spaces
84
- if (tableName.includes(" ")) {
85
- tableName = encodeURIComponent(tableName)
86
- }
87
82
  return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}`
88
83
  }
89
84
 
@@ -95,10 +90,6 @@ export function breakExternalTableId(tableId: string | undefined) {
95
90
  let datasourceId = parts.shift()
96
91
  // if they need joined
97
92
  let tableName = parts.join(DOUBLE_SEPARATOR)
98
- // if contains encoded spaces, decode it
99
- if (tableName.includes(ENCODED_SPACE)) {
100
- tableName = decodeURIComponent(tableName)
101
- }
102
93
  return { datasourceId, tableName }
103
94
  }
104
95
 
@@ -209,9 +200,9 @@ export function isIsoDateString(str: string) {
209
200
  * @param column The column to check, to see if it is a valid relationship.
210
201
  * @param tableIds The IDs of the tables which currently exist.
211
202
  */
212
- export function shouldCopyRelationship(
203
+ function shouldCopyRelationship(
213
204
  column: { type: string; tableId?: string },
214
- tableIds: string[]
205
+ tableIds: [string]
215
206
  ) {
216
207
  return (
217
208
  column.type === FieldTypes.LINK &&
@@ -228,7 +219,7 @@ export function shouldCopyRelationship(
228
219
  * @param column The column to check for options or boolean type.
229
220
  * @param fetchedColumn The fetched column to check for the type in the external database.
230
221
  */
231
- export function shouldCopySpecialColumn(
222
+ function shouldCopySpecialColumn(
232
223
  column: { type: string },
233
224
  fetchedColumn: { type: string } | undefined
234
225
  ) {
@@ -266,12 +257,9 @@ function copyExistingPropsOver(
266
257
  tableIds: [string]
267
258
  ) {
268
259
  if (entities && entities[tableName]) {
269
- if (entities[tableName]?.primaryDisplay) {
260
+ if (entities[tableName].primaryDisplay) {
270
261
  table.primaryDisplay = entities[tableName].primaryDisplay
271
262
  }
272
- if (entities[tableName]?.created) {
273
- table.created = entities[tableName]?.created
274
- }
275
263
  const existingTableSchema = entities[tableName].schema
276
264
  for (let key in existingTableSchema) {
277
265
  if (!existingTableSchema.hasOwnProperty(key)) {