forked from tsai/budibase
8 changed files with 1277 additions and 19 deletions
@ -0,0 +1,184 @@ |
|||
<script> |
|||
export let width = "100" |
|||
export let height = "100" |
|||
</script> |
|||
|
|||
<svg |
|||
{width} |
|||
{height} |
|||
version="1.0" |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
viewBox="0 0 50 80" |
|||
preserveAspectRatio="xMidYMid meet" |
|||
> |
|||
<defs> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-1" |
|||
/> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-3" |
|||
/> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-5" |
|||
/> |
|||
<linearGradient |
|||
x1="50.0053945%" |
|||
y1="8.58610612%" |
|||
x2="50.0053945%" |
|||
y2="100.013939%" |
|||
id="linearGradient-7" |
|||
> |
|||
<stop stop-color="#263238" stop-opacity="0.2" offset="0%" /> |
|||
<stop stop-color="#263238" stop-opacity="0.02" offset="100%" /> |
|||
</linearGradient> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-8" |
|||
/> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-10" |
|||
/> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-12" |
|||
/> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="path-14" |
|||
/> |
|||
<radialGradient |
|||
cx="3.16804688%" |
|||
cy="2.71744318%" |
|||
fx="3.16804688%" |
|||
fy="2.71744318%" |
|||
r="161.248516%" |
|||
gradientTransform="translate(0.031680,0.027174),scale(1.000000,0.727273),translate(-0.031680,-0.027174)" |
|||
id="radialGradient-16" |
|||
> |
|||
<stop stop-color="#FFFFFF" stop-opacity="0.1" offset="0%" /> |
|||
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%" /> |
|||
</radialGradient> |
|||
</defs> |
|||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> |
|||
<g |
|||
id="Consumer-Apps-Sheets-Large-VD-R8-" |
|||
transform="translate(-451.000000, -451.000000)" |
|||
> |
|||
<g id="Hero" transform="translate(0.000000, 63.000000)"> |
|||
<g id="Personal" transform="translate(277.000000, 299.000000)"> |
|||
<g id="Sheets-icon" transform="translate(174.833333, 89.958333)"> |
|||
<g id="Group"> |
|||
<g id="Clipped"> |
|||
<mask id="mask-2" fill="white"> |
|||
<use xlink:href="#path-1" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L36.9791667,10.3541667 L29.5833333,0 Z" |
|||
id="Path" |
|||
fill="#0F9D58" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-2)" |
|||
/> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-4" fill="white"> |
|||
<use xlink:href="#path-3" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<path |
|||
d="M11.8333333,31.8020833 L11.8333333,53.25 L35.5,53.25 L35.5,31.8020833 L11.8333333,31.8020833 Z M22.1875,50.2916667 L14.7916667,50.2916667 L14.7916667,46.59375 L22.1875,46.59375 L22.1875,50.2916667 Z M22.1875,44.375 L14.7916667,44.375 L14.7916667,40.6770833 L22.1875,40.6770833 L22.1875,44.375 Z M22.1875,38.4583333 L14.7916667,38.4583333 L14.7916667,34.7604167 L22.1875,34.7604167 L22.1875,38.4583333 Z M32.5416667,50.2916667 L25.1458333,50.2916667 L25.1458333,46.59375 L32.5416667,46.59375 L32.5416667,50.2916667 Z M32.5416667,44.375 L25.1458333,44.375 L25.1458333,40.6770833 L32.5416667,40.6770833 L32.5416667,44.375 Z M32.5416667,38.4583333 L25.1458333,38.4583333 L25.1458333,34.7604167 L32.5416667,34.7604167 L32.5416667,38.4583333 Z" |
|||
id="Shape" |
|||
fill="#F1F1F1" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-4)" |
|||
/> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-6" fill="white"> |
|||
<use xlink:href="#path-5" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<polygon |
|||
id="Path" |
|||
fill="url(#linearGradient-7)" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-6)" |
|||
points="30.8813021 16.4520313 47.3333333 32.9003646 47.3333333 17.75" |
|||
/> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-9" fill="white"> |
|||
<use xlink:href="#path-8" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<g id="Group" mask="url(#mask-9)"> |
|||
<g transform="translate(26.625000, -2.958333)"> |
|||
<path |
|||
d="M2.95833333,2.95833333 L2.95833333,16.2708333 C2.95833333,18.7225521 4.94411458,20.7083333 7.39583333,20.7083333 L20.7083333,20.7083333 L2.95833333,2.95833333 Z" |
|||
id="Path" |
|||
fill="#87CEAC" |
|||
fill-rule="nonzero" |
|||
/> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-11" fill="white"> |
|||
<use xlink:href="#path-10" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<path |
|||
d="M4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,4.80729167 C0,2.36666667 1.996875,0.369791667 4.4375,0.369791667 L29.5833333,0.369791667 L29.5833333,0 L4.4375,0 Z" |
|||
id="Path" |
|||
fill-opacity="0.2" |
|||
fill="#FFFFFF" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-11)" |
|||
/> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-13" fill="white"> |
|||
<use xlink:href="#path-12" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<path |
|||
d="M42.8958333,64.7135417 L4.4375,64.7135417 C1.996875,64.7135417 0,62.7166667 0,60.2760417 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,60.2760417 C47.3333333,62.7166667 45.3364583,64.7135417 42.8958333,64.7135417 Z" |
|||
id="Path" |
|||
fill-opacity="0.2" |
|||
fill="#263238" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-13)" |
|||
/> |
|||
</g> |
|||
<g id="Clipped"> |
|||
<mask id="mask-15" fill="white"> |
|||
<use xlink:href="#path-14" /> |
|||
</mask> |
|||
<g id="SVGID_1_" /> |
|||
<path |
|||
d="M34.0208333,17.75 C31.5691146,17.75 29.5833333,15.7642188 29.5833333,13.3125 L29.5833333,13.6822917 C29.5833333,16.1340104 31.5691146,18.1197917 34.0208333,18.1197917 L47.3333333,18.1197917 L47.3333333,17.75 L34.0208333,17.75 Z" |
|||
id="Path" |
|||
fill-opacity="0.1" |
|||
fill="#263238" |
|||
fill-rule="nonzero" |
|||
mask="url(#mask-15)" |
|||
/> |
|||
</g> |
|||
</g> |
|||
<path |
|||
d="M29.5833333,0 L4.4375,0 C1.996875,0 0,1.996875 0,4.4375 L0,60.6458333 C0,63.0864583 1.996875,65.0833333 4.4375,65.0833333 L42.8958333,65.0833333 C45.3364583,65.0833333 47.3333333,63.0864583 47.3333333,60.6458333 L47.3333333,17.75 L29.5833333,0 Z" |
|||
id="Path" |
|||
fill="url(#radialGradient-16)" |
|||
fill-rule="nonzero" |
|||
/> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</svg> |
|||
@ -0,0 +1,193 @@ |
|||
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, |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue