2021-11-25 18:12:12 +01:00
|
|
|
import {
|
2022-08-11 14:50:05 +02:00
|
|
|
DatasourceFieldType,
|
2022-09-07 13:40:45 +02:00
|
|
|
DatasourcePlus,
|
2021-11-25 18:12:12 +01:00
|
|
|
Integration,
|
2023-03-13 18:21:04 +01:00
|
|
|
Operation,
|
2023-03-09 09:50:26 +01:00
|
|
|
PaginationJson,
|
2022-09-07 13:40:45 +02:00
|
|
|
QueryJson,
|
2022-08-11 14:50:05 +02:00
|
|
|
QueryType,
|
2023-03-09 09:50:26 +01:00
|
|
|
SearchFilters,
|
|
|
|
SortJson,
|
2022-08-11 12:48:58 +02:00
|
|
|
Table,
|
2023-03-13 18:21:04 +01:00
|
|
|
Row,
|
2022-08-11 12:48:58 +02:00
|
|
|
} from "@budibase/types"
|
2022-01-06 09:08:54 +01:00
|
|
|
import { OAuth2Client } from "google-auth-library"
|
2021-11-25 20:12:32 +01:00
|
|
|
import { buildExternalTableId } from "./utils"
|
2023-03-13 18:21:04 +01:00
|
|
|
import { FieldTypes } from "../constants"
|
2022-01-15 19:28:04 +01:00
|
|
|
import { GoogleSpreadsheet } from "google-spreadsheet"
|
2023-02-27 17:25:26 +01:00
|
|
|
import fetch from "node-fetch"
|
2023-02-23 14:41:35 +01:00
|
|
|
import { configs, HTTPError } from "@budibase/backend-core"
|
2023-03-09 09:50:26 +01:00
|
|
|
import { dataFilters } from "@budibase/shared-core"
|
2022-08-12 18:03:06 +02:00
|
|
|
|
|
|
|
interface GoogleSheetsConfig {
|
|
|
|
spreadsheetId: string
|
|
|
|
auth: OAuthClientConfig
|
|
|
|
}
|
2022-01-06 09:08:54 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
interface OAuthClientConfig {
|
|
|
|
appId: string
|
|
|
|
accessToken: string
|
|
|
|
refreshToken: string
|
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
interface AuthTokenRequest {
|
|
|
|
client_id: string
|
|
|
|
client_secret: string
|
|
|
|
refresh_token: string
|
|
|
|
}
|
2022-04-25 00:32:47 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
interface AuthTokenResponse {
|
|
|
|
access_token: string
|
|
|
|
}
|
2022-04-25 00:32:47 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
const SCHEMA: Integration = {
|
|
|
|
plus: true,
|
|
|
|
auth: {
|
|
|
|
type: "google",
|
|
|
|
},
|
|
|
|
relationships: false,
|
|
|
|
docs: "https://developers.google.com/sheets/api/quickstart/nodejs",
|
|
|
|
description:
|
|
|
|
"Create and collaborate on online spreadsheets in real-time and from any device. ",
|
|
|
|
friendlyName: "Google Sheets",
|
|
|
|
type: "Spreadsheet",
|
|
|
|
datasource: {
|
|
|
|
spreadsheetId: {
|
|
|
|
display: "Google Sheet URL",
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
2022-01-06 09:08:54 +01:00
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
query: {
|
|
|
|
create: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
sheet: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
row: {
|
|
|
|
type: QueryType.JSON,
|
|
|
|
required: true,
|
|
|
|
},
|
2021-11-25 18:12:12 +01:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
read: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
sheet: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
2021-11-25 18:12:12 +01:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
update: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
sheet: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
2021-11-25 18:12:12 +01:00
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
rowIndex: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
row: {
|
|
|
|
type: QueryType.JSON,
|
|
|
|
required: true,
|
2021-11-25 18:12:12 +01:00
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
delete: {
|
|
|
|
type: QueryType.FIELDS,
|
|
|
|
fields: {
|
|
|
|
sheet: {
|
|
|
|
type: DatasourceFieldType.STRING,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
rowIndex: {
|
|
|
|
type: DatasourceFieldType.NUMBER,
|
|
|
|
required: true,
|
2021-11-25 18:12:12 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2022-08-12 18:03:06 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
|
private readonly config: GoogleSheetsConfig
|
2023-02-27 17:25:26 +01:00
|
|
|
private client: GoogleSpreadsheet
|
2022-08-12 18:03:06 +02:00
|
|
|
public tables: Record<string, Table> = {}
|
|
|
|
public schemaErrors: Record<string, string> = {}
|
|
|
|
|
|
|
|
constructor(config: GoogleSheetsConfig) {
|
|
|
|
this.config = config
|
|
|
|
const spreadsheetId = this.cleanSpreadsheetUrl(this.config.spreadsheetId)
|
|
|
|
this.client = new GoogleSpreadsheet(spreadsheetId)
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
getBindingIdentifier() {
|
|
|
|
return ""
|
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
getStringConcat(parts: string[]) {
|
|
|
|
return ""
|
|
|
|
}
|
2022-01-15 19:28:04 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
/**
|
|
|
|
* Pull the spreadsheet ID out from a valid google sheets URL
|
|
|
|
* @param spreadsheetId - the URL or standard spreadsheetId of the google sheet
|
|
|
|
* @returns spreadsheet Id of the google sheet
|
|
|
|
*/
|
|
|
|
cleanSpreadsheetUrl(spreadsheetId: string) {
|
|
|
|
if (!spreadsheetId) {
|
|
|
|
throw new Error(
|
|
|
|
"You must set a spreadsheet ID in your configuration to fetch tables."
|
|
|
|
)
|
2022-03-02 23:45:10 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
const parts = spreadsheetId.split("/")
|
|
|
|
return parts.length > 5 ? parts[5] : spreadsheetId
|
|
|
|
}
|
2022-03-02 23:45:10 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async fetchAccessToken(
|
|
|
|
payload: AuthTokenRequest
|
|
|
|
): Promise<AuthTokenResponse> {
|
|
|
|
const response = await fetch("https://www.googleapis.com/oauth2/v4/token", {
|
|
|
|
method: "POST",
|
|
|
|
body: JSON.stringify({
|
|
|
|
...payload,
|
|
|
|
grant_type: "refresh_token",
|
|
|
|
}),
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
})
|
2022-03-11 01:19:26 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
const json = await response.json()
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
if (response.status !== 200) {
|
|
|
|
throw new Error(
|
|
|
|
`Error authenticating with google sheets. ${json.error_description}`
|
2022-04-25 00:32:47 +02:00
|
|
|
)
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-04-25 00:32:47 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
return json
|
|
|
|
}
|
2022-06-05 17:43:04 +02:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async connect() {
|
|
|
|
try {
|
|
|
|
// Initialise oAuth client
|
2023-02-28 11:36:00 +01:00
|
|
|
let googleConfig = await configs.getGoogleDatasourceConfig()
|
2022-08-12 18:03:06 +02:00
|
|
|
if (!googleConfig) {
|
2023-02-23 14:41:35 +01:00
|
|
|
throw new HTTPError("Google config not found", 400)
|
2022-04-25 00:32:47 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
const oauthClient = new OAuth2Client({
|
|
|
|
clientId: googleConfig.clientID,
|
|
|
|
clientSecret: googleConfig.clientSecret,
|
|
|
|
})
|
|
|
|
|
|
|
|
const tokenResponse = await this.fetchAccessToken({
|
|
|
|
client_id: googleConfig.clientID,
|
|
|
|
client_secret: googleConfig.clientSecret,
|
|
|
|
refresh_token: this.config.auth.refreshToken,
|
|
|
|
})
|
|
|
|
|
|
|
|
oauthClient.setCredentials({
|
|
|
|
refresh_token: this.config.auth.refreshToken,
|
|
|
|
access_token: tokenResponse.access_token,
|
|
|
|
})
|
|
|
|
|
|
|
|
this.client.useOAuth2Client(oauthClient)
|
|
|
|
await this.client.loadInfo()
|
2023-03-10 19:46:48 +01:00
|
|
|
} catch (err: any) {
|
|
|
|
// this happens for xlsx imports
|
|
|
|
if (err.message?.includes("operation is not supported")) {
|
|
|
|
err.message =
|
|
|
|
"This operation is not supported - XLSX sheets must be converted."
|
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
console.error("Error connecting to google sheets", err)
|
|
|
|
throw err
|
2022-04-25 00:32:47 +02:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-04-25 00:32:47 +02:00
|
|
|
|
2023-03-13 17:21:22 +01:00
|
|
|
getTableSchema(title: string, headerValues: string[], id?: string) {
|
|
|
|
// base table
|
|
|
|
const table: Table = {
|
|
|
|
name: title,
|
|
|
|
primary: ["rowNumber"],
|
|
|
|
schema: {},
|
|
|
|
}
|
|
|
|
if (id) {
|
|
|
|
table._id = id
|
|
|
|
}
|
|
|
|
// build schema from headers
|
|
|
|
for (let header of headerValues) {
|
|
|
|
table.schema[header] = {
|
|
|
|
name: header,
|
|
|
|
type: FieldTypes.STRING,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return table
|
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async buildSchema(datasourceId: string) {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheets = this.client.sheetsByIndex
|
2022-08-12 18:03:06 +02:00
|
|
|
const tables: Record<string, Table> = {}
|
|
|
|
for (let sheet of sheets) {
|
|
|
|
// must fetch rows to determine schema
|
|
|
|
await sheet.getRows()
|
2022-03-28 17:44:33 +02:00
|
|
|
|
2023-03-13 17:21:22 +01:00
|
|
|
const id = buildExternalTableId(datasourceId, sheet.title)
|
|
|
|
tables[sheet.title] = this.getTableSchema(
|
|
|
|
sheet.title,
|
|
|
|
sheet.headerValues,
|
|
|
|
id
|
|
|
|
)
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
this.tables = tables
|
|
|
|
}
|
2021-11-25 20:12:32 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async query(json: QueryJson) {
|
|
|
|
const sheet = json.endpoint.entityId
|
2023-03-13 18:21:04 +01:00
|
|
|
switch (json.endpoint.operation) {
|
|
|
|
case Operation.CREATE:
|
|
|
|
return this.create({ sheet, row: json.body as Row })
|
|
|
|
case Operation.BULK_CREATE:
|
|
|
|
return this.createBulk({ sheet, rows: json.body as Row[] })
|
|
|
|
case Operation.READ:
|
|
|
|
return this.read({ ...json, sheet })
|
|
|
|
case Operation.UPDATE:
|
|
|
|
return this.update({
|
2022-08-12 18:03:06 +02:00
|
|
|
// exclude the header row and zero index
|
|
|
|
rowIndex: json.extra?.idFilter?.equal?.rowNumber - 2,
|
|
|
|
sheet,
|
|
|
|
row: json.body,
|
2023-03-13 18:21:04 +01:00
|
|
|
})
|
|
|
|
case Operation.DELETE:
|
|
|
|
return this.delete({
|
2022-08-12 18:03:06 +02:00
|
|
|
// exclude the header row and zero index
|
|
|
|
rowIndex: json.extra?.idFilter?.equal?.rowNumber - 2,
|
|
|
|
sheet,
|
2023-03-13 18:21:04 +01:00
|
|
|
})
|
|
|
|
case Operation.CREATE_TABLE:
|
|
|
|
return this.createTable(json?.table?.name)
|
|
|
|
case Operation.UPDATE_TABLE:
|
|
|
|
return this.updateTable(json.table)
|
|
|
|
case Operation.DELETE_TABLE:
|
|
|
|
return this.deleteTable(json?.table?.name)
|
|
|
|
default:
|
|
|
|
throw new Error(
|
|
|
|
`GSheets integration does not support "${json.endpoint.operation}".`
|
|
|
|
)
|
2021-11-25 20:12:32 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-01-18 17:15:29 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
buildRowObject(headers: string[], values: string[], rowNumber: number) {
|
|
|
|
const rowObject: { rowNumber: number; [key: string]: any } = { rowNumber }
|
|
|
|
for (let i = 0; i < headers.length; i++) {
|
|
|
|
rowObject._id = rowNumber
|
|
|
|
rowObject[headers[i]] = values[i]
|
2021-11-25 20:12:32 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
return rowObject
|
|
|
|
}
|
2021-11-25 20:12:32 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async createTable(name?: string) {
|
|
|
|
try {
|
|
|
|
await this.connect()
|
fixes for google sheets, admin checklist, and deleting an app from API (#8846)
* fixes for google sheets, admin checklist, and deleting an app from API
* code review
* splitting unpublish endpoint, moving deploy endpoint to applications controller. Still to do public API work and move deployment controller into application controller
* updating REST method for unpublish in API test
* unpublish and publish endpoint on public API, delete endpoint unpublishes and deletes app
* removing skip_setup from prodAppDb call
* removing commented code
* unit tests and open API spec updates
* unpublish, publish unit tests - delete still in progress
* remove line updating app name in API test
* unit tests
* v2.1.46
* Update pro version to 2.1.46
* v2.2.0
* Update pro version to 2.2.0
* Fix for budibase plugin skeleton, which utilises the old import style.
* Fix side nav styles
* v2.2.1
* Update pro version to 2.2.1
* using dist folder to allow importing constants for openAPI specs
* v2.2.2
* Update pro version to 2.2.2
* Fix for user enrichment call (updating to @budibase/nano fork) (#9038)
* Fix for #9029 - this should fix the issue users have been experiencing with user enrichment calls in apps, essentially it utilises a fork of the nano library we use to interact with CouchDB, which has been updated to use a POST request rather than a GET request as it supports a larger set of data being sent as query parameters.
* Incrementing Nano version to attempt to fix yarn registry issues.
* v2.2.3
* Update pro version to 2.2.3
* Fix SQL table `_id` filtering (#9030)
* Re-add support for filtering on _id using external SQL tables and fix filter key prefixes not working with _id field
* Remove like operator from internal tables and only allow basic operators on SQL table _id column
* Update data section filtering to respect new rules
* Update automation section filtering to respect new rules
* Update dynamic filter component to respect new rules
* v2.2.4
* Update pro version to 2.2.4
* lock changes (#9047)
* v2.2.5
* Update pro version to 2.2.5
* Make looping arrow point in right direction (#9053)
* v2.2.6
* Update pro version to 2.2.6
* Types/attaching license to account (#9065)
* adding license type to account
* removing planDuration
* v2.2.7
* Update pro version to 2.2.7
* Environment variable type coercion fix (#9074)
* Environment variable type coercion fix
* Update .gitignore
* v2.2.8
* Update pro version to 2.2.8
* tests passing
* all tests passing, updates to public API response
* update unpublish call to return 204, openAPI spec and unit
* fixing API tests
Co-authored-by: Budibase Release Bot <>
Co-authored-by: mike12345567 <me@michaeldrury.co.uk>
Co-authored-by: Andrew Kingston <andrew@kingston.dev>
Co-authored-by: melohagan <101575380+melohagan@users.noreply.github.com>
Co-authored-by: Rory Powell <rory.codes@gmail.com>
2022-12-19 14:18:00 +01:00
|
|
|
return await this.client.addSheet({ title: name, headerValues: ["test"] })
|
2022-08-12 18:03:06 +02:00
|
|
|
} catch (err) {
|
|
|
|
console.error("Error creating new table in google sheets", err)
|
|
|
|
throw err
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async updateTable(table?: any) {
|
|
|
|
try {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheet = this.client.sheetsByTitle[table.name]
|
2022-08-12 18:03:06 +02:00
|
|
|
await sheet.loadHeaderRow()
|
2022-01-18 17:15:29 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
if (table._rename) {
|
|
|
|
const headers = []
|
|
|
|
for (let header of sheet.headerValues) {
|
|
|
|
if (header === table._rename.old) {
|
|
|
|
headers.push(table._rename.updated)
|
|
|
|
} else {
|
|
|
|
headers.push(header)
|
2022-01-18 17:15:29 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
await sheet.setHeaderRow(headers)
|
|
|
|
} else {
|
2023-02-27 13:33:19 +01:00
|
|
|
const updatedHeaderValues = [...sheet.headerValues]
|
|
|
|
|
2023-03-13 17:21:22 +01:00
|
|
|
// add new column - doesn't currently exist
|
|
|
|
for (let key of Object.keys(table.schema)) {
|
|
|
|
if (!sheet.headerValues.includes(key)) {
|
|
|
|
updatedHeaderValues.push(key)
|
|
|
|
}
|
|
|
|
}
|
2023-02-27 13:33:19 +01:00
|
|
|
|
2023-03-13 17:21:22 +01:00
|
|
|
// clear out deleted columns
|
|
|
|
for (let key of sheet.headerValues) {
|
|
|
|
if (!Object.keys(table.schema).includes(key)) {
|
|
|
|
const idx = updatedHeaderValues.indexOf(key)
|
|
|
|
updatedHeaderValues.splice(idx, 1)
|
|
|
|
}
|
2023-02-27 13:33:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
await sheet.setHeaderRow(updatedHeaderValues)
|
2022-01-18 17:15:29 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
} catch (err) {
|
|
|
|
console.error("Error updating table in google sheets", err)
|
|
|
|
throw err
|
2022-01-18 17:15:29 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2022-01-18 17:15:29 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async deleteTable(sheet: any) {
|
|
|
|
try {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheetToDelete = this.client.sheetsByTitle[sheet]
|
2022-08-12 18:03:06 +02:00
|
|
|
return await sheetToDelete.delete()
|
|
|
|
} catch (err) {
|
|
|
|
console.error("Error deleting table in google sheets", err)
|
|
|
|
throw err
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async create(query: { sheet: string; row: any }) {
|
|
|
|
try {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
2022-08-12 18:03:06 +02:00
|
|
|
const rowToInsert =
|
|
|
|
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
|
|
|
const row = await sheet.addRow(rowToInsert)
|
|
|
|
return [
|
|
|
|
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber),
|
|
|
|
]
|
|
|
|
} catch (err) {
|
|
|
|
console.error("Error writing to google sheets", err)
|
|
|
|
throw err
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2023-03-13 18:21:04 +01:00
|
|
|
async createBulk(query: { sheet: string; rows: any[] }) {
|
|
|
|
try {
|
|
|
|
await this.connect()
|
|
|
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
|
|
|
let rowsToInsert = []
|
|
|
|
for (let row of query.rows) {
|
|
|
|
rowsToInsert.push(typeof row === "string" ? JSON.parse(row) : row)
|
|
|
|
}
|
|
|
|
const rows = await sheet.addRows(rowsToInsert)
|
|
|
|
return rows.map(row =>
|
|
|
|
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber)
|
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
console.error("Error bulk writing to google sheets", err)
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 09:50:26 +01:00
|
|
|
async read(query: {
|
|
|
|
sheet: string
|
|
|
|
filters?: SearchFilters
|
|
|
|
sort?: SortJson
|
|
|
|
paginate?: PaginationJson
|
|
|
|
}) {
|
2022-08-12 18:03:06 +02:00
|
|
|
try {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
2022-08-12 18:03:06 +02:00
|
|
|
const rows = await sheet.getRows()
|
2023-03-09 09:50:26 +01:00
|
|
|
const filtered = dataFilters.runLuceneQuery(rows, query.filters)
|
2022-08-12 18:03:06 +02:00
|
|
|
const headerValues = sheet.headerValues
|
2023-03-09 09:50:26 +01:00
|
|
|
let response = []
|
|
|
|
for (let row of filtered) {
|
2022-08-12 18:03:06 +02:00
|
|
|
response.push(
|
|
|
|
this.buildRowObject(headerValues, row._rawData, row._rowNumber)
|
|
|
|
)
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2023-03-09 09:50:26 +01:00
|
|
|
|
|
|
|
if (query.sort) {
|
|
|
|
if (Object.keys(query.sort).length !== 1) {
|
|
|
|
console.warn("Googlesheets does not support multiple sorting", {
|
|
|
|
sortInfo: query.sort,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
const [sortField, sortInfo] = Object.entries(query.sort)[0]
|
|
|
|
response = dataFilters.luceneSort(
|
|
|
|
response,
|
|
|
|
sortField,
|
|
|
|
sortInfo.direction,
|
|
|
|
sortInfo.type
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
return response
|
|
|
|
} catch (err) {
|
|
|
|
console.error("Error reading from google sheets", err)
|
|
|
|
throw err
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async update(query: { sheet: string; rowIndex: number; row: any }) {
|
|
|
|
try {
|
2021-11-25 18:12:12 +01:00
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
2021-11-25 18:12:12 +01:00
|
|
|
const rows = await sheet.getRows()
|
|
|
|
const row = rows[query.rowIndex]
|
|
|
|
if (row) {
|
2022-09-07 13:40:45 +02:00
|
|
|
const updateValues =
|
|
|
|
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
2022-08-12 18:03:06 +02:00
|
|
|
for (let key in updateValues) {
|
|
|
|
row[key] = updateValues[key]
|
|
|
|
}
|
|
|
|
await row.save()
|
|
|
|
return [
|
|
|
|
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber),
|
|
|
|
]
|
2021-11-25 18:12:12 +01:00
|
|
|
} else {
|
|
|
|
throw new Error("Row does not exist.")
|
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
} catch (err) {
|
|
|
|
console.error("Error reading from google sheets", err)
|
|
|
|
throw err
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-12 18:03:06 +02:00
|
|
|
async delete(query: { sheet: string; rowIndex: number }) {
|
|
|
|
await this.connect()
|
2023-02-27 17:25:26 +01:00
|
|
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
2022-08-12 18:03:06 +02:00
|
|
|
const rows = await sheet.getRows()
|
|
|
|
const row = rows[query.rowIndex]
|
|
|
|
if (row) {
|
|
|
|
await row.delete()
|
|
|
|
return [{ deleted: query.rowIndex }]
|
|
|
|
} else {
|
|
|
|
throw new Error("Row does not exist.")
|
|
|
|
}
|
2021-11-25 18:12:12 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-12 18:03:06 +02:00
|
|
|
|
|
|
|
export default {
|
|
|
|
schema: SCHEMA,
|
|
|
|
integration: GoogleSheetsIntegration,
|
|
|
|
}
|