Skip to content

Commit

Permalink
Read headers in batch on getRows() (#693)
Browse files Browse the repository at this point in the history
* Get header on batch when reading without header loaded

* Add tests

* Added await to processHeader function as failure would cause an unhandleRejection error and crash the BB server

* Adding yarn.lock, publishing new named version.

---------

Co-authored-by: Dean <[email protected]>
Co-authored-by: Michael Drury <[email protected]>
  • Loading branch information
3 people authored Aug 31, 2024
1 parent 0478876 commit f73eb18
Show file tree
Hide file tree
Showing 4 changed files with 8,000 additions and 9 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "google-spreadsheet",
"version": "4.1.2",
"name": "@budibase/google-spreadsheet",
"version": "4.1.3",
"description": "Google Sheets API -- simple interface to read/write data and manage sheets",
"keywords": [
"google spreadsheets",
Expand Down
36 changes: 29 additions & 7 deletions src/lib/GoogleSpreadsheetWorksheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export class GoogleSpreadsheetWorksheet {
get hidden() { return this._getProp('hidden'); }
get tabColor() { return this._getProp('tabColor'); }
get rightToLeft() { return this._getProp('rightToLeft'); }
private get _headerRange() {
return `A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`;
}

set sheetId(newVal: WorksheetProperties['sheetId']) { this._setProp('sheetId', newVal); }
set title(newVal: WorksheetProperties['title']) { this._setProp('title', newVal); }
Expand Down Expand Up @@ -342,7 +345,11 @@ export class GoogleSpreadsheetWorksheet {

async loadHeaderRow(headerRowIndex?: number) {
if (headerRowIndex !== undefined) this._headerRowIndex = headerRowIndex;
const rows = await this.getCellsInRange(`A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`);
const rows = await this.getCellsInRange(this._headerRange);
await this._processHeaderRow(rows);
}

private async _processHeaderRow(rows: any[]) {
if (!rows) {
throw new Error('No values in the header row - fill the first row with header values before trying to interact with rows');
}
Expand Down Expand Up @@ -489,14 +496,21 @@ export class GoogleSpreadsheetWorksheet {
const offset = options?.offset || 0;
const limit = options?.limit || this.rowCount - 1;

await this._ensureHeaderRowLoaded();

const firstRow = 1 + this._headerRowIndex + offset;
const lastRow = firstRow + limit - 1; // inclusive so we subtract 1
const lastColumn = columnToLetter(this.headerValues.length);
const rawRows = await this.getCellsInRange(
`A${firstRow}:${lastColumn}${lastRow}`
);

let rawRows;
if (this._headerValues) {
const lastColumn = columnToLetter(this.headerValues.length);
rawRows = await this.getCellsInRange(
`A${firstRow}:${lastColumn}${lastRow}`
);
} else {
const result = await this.batchGetCellsInRange([this._headerRange,
`A${firstRow}:${this.lastColumnLetter}${lastRow}`]);
this._processHeaderRow(result[0]);
rawRows = result[1];
}

if (!rawRows) return [];

Expand Down Expand Up @@ -600,6 +614,14 @@ export class GoogleSpreadsheetWorksheet {
return response.data.values;
}

async batchGetCellsInRange(a1Ranges: A1Range[], options?: GetValuesRequestOptions) {
const ranges = a1Ranges.map((r) => `ranges=${this.encodedA1SheetName}!${r}`).join('&');
const response = await this._spreadsheet.sheetsApi.get(`/values:batchGet?${ranges}`, {
params: options,
});
return response.data.valueRanges.map((r: any) => r.values);
}

async updateNamedRange() {
// Request type = `updateNamedRange`
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateNamedRangeRequest
Expand Down
11 changes: 11 additions & 0 deletions src/test/rows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ describe('Row-based operations', () => {
expect(rows.length).toEqual(2);
expect(rows[0].get('numbers')).toEqual(INITIAL_DATA[2][0]);
});

it('it will fetch the same row content when the header is not populated', async () => {
sheet.resetLocalCache(true); // forget the header values
expect(() => sheet.headerValues).toThrowError('Header values are not yet loaded');
const rowsWithoutPrefetchHeaders = await sheet.getRows();

expect(sheet.headerValues).toBeDefined();
const rowsWithFetchedHeaders = await sheet.getRows();

expect(rowsWithoutPrefetchHeaders).toEqual(rowsWithFetchedHeaders);
});
});

describe('adding rows', () => {
Expand Down
Loading

0 comments on commit f73eb18

Please sign in to comment.