2nd test WIP.

This commit is contained in:
Sam Rose 2024-09-06 17:29:56 +01:00
parent 0359b20347
commit 1bc84c1633
No known key found for this signature in database
2 changed files with 174 additions and 67 deletions

View File

@ -6,9 +6,9 @@ import {
Datasource,
FieldType,
SourceName,
Table,
TableSourceType,
} from "@budibase/types"
import { access } from "node:fs"
import { GoogleSheetsMock } from "./utils/googlesheets"
describe("Google Sheets Integration", () => {
@ -84,4 +84,33 @@ describe("Google Sheets Integration", () => {
expect(cell.userEnteredValue.stringValue).toEqual(table.name)
})
})
describe("update", () => {
let table: Table
beforeEach(async () => {
table = await config.api.table.save({
name: "Test Table",
type: "table",
sourceId: datasource._id!,
sourceType: TableSourceType.EXTERNAL,
schema: {
name: {
name: "name",
type: FieldType.STRING,
},
description: {
name: "description",
type: FieldType.STRING,
},
},
})
})
it.only("should be able to add a new row", async () => {
await config.api.row.save(table._id!, {
name: "Test Contact",
description: "original description",
})
})
})
})

View File

@ -6,8 +6,8 @@ type Value = string | number | boolean
type Dimension = "ROWS" | "COLUMNS"
interface Range {
row: number | "ALL"
column: number | "ALL"
row: number
column: number
}
interface DimensionProperties {
@ -124,7 +124,7 @@ interface Spreadsheet {
export class GoogleSheetsMock {
private config: GoogleSheetsConfig
private sheet: Spreadsheet
private spreadsheet: Spreadsheet
static forDatasource(datasource: Datasource): GoogleSheetsMock {
return new GoogleSheetsMock(datasource.config as GoogleSheetsConfig)
@ -132,7 +132,7 @@ export class GoogleSheetsMock {
private constructor(config: GoogleSheetsConfig) {
this.config = config
this.sheet = {
this.spreadsheet = {
properties: {
title: "Test Spreadsheet",
},
@ -142,11 +142,14 @@ export class GoogleSheetsMock {
}
init() {
nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, {
nock("https://www.googleapis.com/")
.post("/oauth2/v4/token")
.reply(200, {
grant_type: "client_credentials",
client_id: "your-client-id",
client_secret: "your-client-secret",
})
.persist()
nock("https://oauth2.googleapis.com/")
.post("/token", {
client_id: "test",
@ -160,27 +163,97 @@ export class GoogleSheetsMock {
token_type: "Bearer",
scopes: "https://www.googleapis.com/auth/spreadsheets",
})
.persist()
nock("https://sheets.googleapis.com/", {
reqheaders: { authorization: "Bearer test" },
})
.get("/v4/spreadsheets/randomId/")
.reply(200, () => this.sheet)
.get(`/v4/spreadsheets/${this.config.spreadsheetId}/`)
.reply(200, () => this.handleGetSpreadsheet())
.persist()
nock("https://sheets.googleapis.com/", {
reqheaders: { authorization: "Bearer test" },
})
.post(`/v4/spreadsheets/${this.config.spreadsheetId}/:batchUpdate`)
.reply(200, (uri: string, request: nock.Body): nock.Body => {
const batchUpdateRequest = request as BatchUpdateRequest
.reply(200, (_uri, request) =>
this.handleBatchUpdate(request as BatchUpdateRequest)
)
.persist()
nock("https://sheets.googleapis.com/", {
reqheaders: { authorization: "Bearer test" },
})
.put(
new RegExp(`/v4/spreadsheets/${this.config.spreadsheetId}/values/.*`)
)
.reply(200, (_uri, request) =>
this.handleValueUpdate(request as ValueRange)
)
.persist()
nock("https://sheets.googleapis.com/", {
reqheaders: { authorization: "Bearer test" },
})
.get(
new RegExp(`/v4/spreadsheets/${this.config.spreadsheetId}/values/.*`)
)
.reply(200, uri => {
const range = uri.split("/").pop()
if (!range) {
throw new Error("No range provided")
}
return this.handleGetValues(decodeURIComponent(range))
})
.persist()
}
private handleGetValues(range: string): ValueRange {
const { sheet, topLeft, bottomRight } = this.parseA1Notation(range)
const valueRange: ValueRange = {
range,
majorDimension: "ROWS",
values: [],
}
for (let row = topLeft.row; row <= bottomRight.row; row++) {
const values: Value[] = []
for (let col = topLeft.column; col <= bottomRight.column; col++) {
const cell = this.getCellNumericIndexes(sheet, row, col)
if (!cell) {
throw new Error("Cell not found")
}
values.push(this.unwrapValue(cell.userEnteredValue))
}
valueRange.values.push(values)
}
return valueRange
}
private handleBatchUpdate(
batchUpdateRequest: BatchUpdateRequest
): BatchUpdateResponse {
const replies: Response[] = []
for (const request of batchUpdateRequest.requests) {
if (request.addSheet) {
const response = this.handleAddSheet(request.addSheet)
replies.push({ addSheet: response })
}
}
return {
spreadsheetId: this.spreadsheet.spreadsheetId,
replies,
updatedSpreadsheet: this.spreadsheet,
}
}
private handleAddSheet(request: AddSheetRequest): AddSheetResponse {
const properties: SheetProperties = {
title: request.addSheet.properties.title,
sheetId: this.sheet.sheets.length,
title: request.properties.title,
sheetId: this.spreadsheet.sheets.length,
gridProperties: {
rowCount: 100,
columnCount: 26,
@ -192,33 +265,16 @@ export class GoogleSheetsMock {
},
}
this.sheet.sheets.push({
this.spreadsheet.sheets.push({
properties,
data: [this.createEmptyGrid(100, 26)],
})
replies.push({ addSheet: { properties } })
}
return { properties }
}
const response: BatchUpdateResponse = {
spreadsheetId: this.sheet.spreadsheetId,
replies,
updatedSpreadsheet: this.sheet,
}
return response
})
.persist()
nock("https://sheets.googleapis.com/", {
reqheaders: { authorization: "Bearer test" },
})
.put(
new RegExp(`/v4/spreadsheets/${this.config.spreadsheetId}/values/.*`)
)
.reply(200, (uri, request) =>
this.handleValueUpdate(request as ValueRange)
)
private handleGetSpreadsheet(): Spreadsheet {
return this.spreadsheet
}
private handleValueUpdate(valueRange: ValueRange): UpdateValuesResponse {
@ -230,19 +286,6 @@ export class GoogleSheetsMock {
valueRange.range
)
if (topLeft.row === "ALL") {
topLeft.row = 0
}
if (bottomRight.row === "ALL") {
bottomRight.row = sheet.properties.gridProperties.rowCount - 1
}
if (topLeft.column === "ALL") {
topLeft.column = 0
}
if (bottomRight.column === "ALL") {
bottomRight.column = sheet.properties.gridProperties.columnCount - 1
}
for (let row = topLeft.row; row <= bottomRight.row; row++) {
for (
let column = topLeft.column;
@ -260,7 +303,7 @@ export class GoogleSheetsMock {
}
const response: UpdateValuesResponse = {
spreadsheetId: this.sheet.spreadsheetId,
spreadsheetId: this.spreadsheet.spreadsheetId,
updatedRange: valueRange.range,
updatedRows: valueRange.values.length,
updatedColumns: valueRange.values[0].length,
@ -270,6 +313,20 @@ export class GoogleSheetsMock {
return response
}
private unwrapValue(from: ExtendedValue): Value {
if (from.stringValue !== undefined) {
return from.stringValue
} else if (from.numberValue !== undefined) {
return from.numberValue
} else if (from.boolValue !== undefined) {
return from.boolValue
} else if (from.formulaValue !== undefined) {
return from.formulaValue
} else {
throw new Error("Unsupported value type")
}
}
private createValue(from: Value): ExtendedValue {
if (typeof from === "string") {
return {
@ -375,10 +432,26 @@ export class GoogleSheetsMock {
throw new Error(`Sheet ${sheetName} not found`)
}
const parsedTopLeft = this.parseCell(topLeft)
const parsedBottomRight = this.parseCell(bottomRight)
if (parsedTopLeft.row === "ALL") {
parsedTopLeft.row = 0
}
if (parsedBottomRight.row === "ALL") {
parsedBottomRight.row = sheet.properties.gridProperties.rowCount - 1
}
if (parsedTopLeft.column === "ALL") {
parsedTopLeft.column = 0
}
if (parsedBottomRight.column === "ALL") {
parsedBottomRight.column = sheet.properties.gridProperties.columnCount - 1
}
return {
sheet,
topLeft: this.parseCell(topLeft),
bottomRight: this.parseCell(bottomRight),
topLeft: parsedTopLeft as Range,
bottomRight: parsedBottomRight as Range,
}
}
@ -387,7 +460,10 @@ export class GoogleSheetsMock {
* @param cell a string of the form A1, B2, etc.
* @returns
*/
private parseCell(cell: string): Range {
private parseCell(cell: string): {
row: number | "ALL"
column: number | "ALL"
} {
const firstChar = cell.slice(0, 1)
if (this.isInteger(firstChar)) {
return { row: parseInt(cell) - 1, column: "ALL" }
@ -409,6 +485,8 @@ export class GoogleSheetsMock {
}
private getSheetByName(name: string): Sheet | undefined {
return this.sheet.sheets.find(sheet => sheet.properties.title === name)
return this.spreadsheet.sheets.find(
sheet => sheet.properties.title === name
)
}
}