budibase/packages/server/src/integrations/googlesheets.ts

194 lines
5.1 KiB
TypeScript
Raw Normal View History

2021-11-25 18:12:12 +01:00
import {
DatasourceFieldTypes,
Integration,
QueryTypes,
} from "../definitions/datasource"
import { IntegrationBase } from "./base/IntegrationBase"
import { GoogleSpreadsheet } from "google-spreadsheet"
module GoogleSheetsModule {
interface GoogleSheetsConfig {
spreadsheetId: string
clientEmail: string
privateKey: string
}
const SCHEMA: Integration = {
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",
datasource: {
spreadsheetId: {
type: DatasourceFieldTypes.STRING,
required: true,
},
clientEmail: {
type: DatasourceFieldTypes.STRING,
required: true,
},
privateKey: {
type: DatasourceFieldTypes.LONGFORM,
required: true,
},
},
query: {
create: {
type: QueryTypes.FIELDS,
fields: {
sheet: {
type: "string",
required: true,
},
row: {
type: QueryTypes.JSON,
required: true,
},
},
},
read: {
type: QueryTypes.FIELDS,
fields: {
sheet: {
type: "string",
required: true,
},
},
},
update: {
type: QueryTypes.FIELDS,
fields: {
sheet: {
type: "string",
required: true,
},
rowIndex: {
type: "number",
required: true,
},
row: {
type: QueryTypes.JSON,
required: true,
},
},
},
delete: {
type: QueryTypes.FIELDS,
fields: {
sheet: {
type: "string",
required: true,
},
rowIndex: {
type: "number",
required: true,
},
},
},
},
}
class GoogleSheetsIntegration implements IntegrationBase {
private readonly config: GoogleSheetsConfig
private client: any
constructor(config: GoogleSheetsConfig) {
this.config = config
this.client = new GoogleSpreadsheet(this.config.spreadsheetId)
}
async connect() {
try {
await this.client.useServiceAccountAuth({
// env var values are copied from service account credentials generated by google
// see "Authentication" section in docs for more info
client_email: this.config.clientEmail,
private_key: this.config.privateKey,
})
await this.client.loadInfo()
} catch (err) {
console.error("Error connecting to google sheets", err)
throw err
}
}
buildRowObject(headers: string[], values: string[]) {
const rowObject = {}
for (let i = 0; i < headers.length; i++) {
rowObject[headers[i]] = values[i]
}
return rowObject
}
async create(query: { sheet: string; row: string }) {
try {
await this.connect()
const sheet = await this.client.sheetsByTitle[query.sheet]
const rowToInsert = JSON.parse(query.row)
const row = await sheet.addRow(rowToInsert)
return [this.buildRowObject(sheet.headerValues, row._rawData)]
} catch (err) {
console.error("Error writing to google sheets", err)
throw err
}
}
async read(query: { sheet: string }) {
try {
await this.connect()
const sheet = await this.client.sheetsByTitle[query.sheet]
const rows = await sheet.getRows()
const headerValues = sheet.headerValues
const response = []
for (let row of rows) {
response.push(this.buildRowObject(headerValues, row._rawData))
}
return response
} catch (err) {
console.error("Error reading from google sheets", err)
throw err
}
}
async update(query: { sheet: string; rowIndex: number; row: string }) {
try {
await this.connect()
const sheet = await this.client.sheetsByTitle[query.sheet]
const rows = await sheet.getRows()
const row = rows[query.rowIndex]
if (row) {
const updateValues = JSON.parse(query.row)
for (let key in updateValues) {
row[key] = updateValues[key]
}
await row.save()
return [this.buildRowObject(sheet.headerValues, row._rawData)]
} else {
throw new Error("Row does not exist.")
}
} catch (err) {
console.error("Error reading from google sheets", err)
throw err
}
}
async delete(query: { sheet: string; rowIndex: number }) {
await this.connect()
const sheet = await this.client.sheetsByTitle[query.sheet]
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.")
}
}
}
module.exports = {
schema: SCHEMA,
integration: GoogleSheetsIntegration,
}
}