google-spreadsheet 3.3.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -34
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +987 -0
- package/dist/index.mjs +1 -0
- package/package.json +74 -37
- package/src/index.ts +5 -0
- package/src/lib/GoogleSpreadsheet.ts +636 -0
- package/src/lib/GoogleSpreadsheetCell.ts +307 -0
- package/src/lib/GoogleSpreadsheetCellErrorValue.ts +25 -0
- package/src/lib/GoogleSpreadsheetRow.ts +117 -0
- package/{lib/GoogleSpreadsheetWorksheet.js → src/lib/GoogleSpreadsheetWorksheet.ts} +271 -178
- package/src/lib/lodash.ts +32 -0
- package/src/lib/types/auth-types.ts +18 -0
- package/src/lib/types/drive-types.ts +40 -0
- package/src/lib/types/sheets-types.ts +528 -0
- package/src/lib/types/util-types.ts +8 -0
- package/src/lib/utils.ts +57 -0
- package/.eslintrc.js +0 -61
- package/.nvmrc +0 -1
- package/Changelog.md +0 -10
- package/TODO +0 -73
- package/UNLICENSE +0 -24
- package/index.js +0 -13
- package/lib/GoogleSpreadsheet.js +0 -426
- package/lib/GoogleSpreadsheetCell.js +0 -239
- package/lib/GoogleSpreadsheetRow.js +0 -72
- package/lib/errors.js +0 -10
- package/lib/utils.js +0 -32
|
@@ -1,63 +1,95 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
|
+
import * as _ from './lodash';
|
|
3
|
+
|
|
4
|
+
import { GoogleSpreadsheetRow } from './GoogleSpreadsheetRow';
|
|
5
|
+
import { GoogleSpreadsheetCell } from './GoogleSpreadsheetCell';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
getFieldMask, columnToLetter, letterToColumn, checkForDuplicateHeaders,
|
|
9
|
+
} from './utils';
|
|
10
|
+
import { GoogleSpreadsheet } from './GoogleSpreadsheet';
|
|
11
|
+
import {
|
|
12
|
+
A1Range, SpreadsheetId, DimensionRangeIndexes, WorksheetDimension, WorksheetId, WorksheetProperties, A1Address,
|
|
13
|
+
RowIndex, ColumnIndex, DataFilterWithoutWorksheetId, DataFilter, GetValuesRequestOptions, WorksheetGridProperties,
|
|
14
|
+
WorksheetDimensionProperties, CellDataRange, AddRowOptions, GridRangeWithOptionalWorksheetId,
|
|
15
|
+
} from './types/sheets-types';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// types of cell data accepted when using row based api
|
|
19
|
+
type RowCellData = string | number | boolean | Date;
|
|
20
|
+
// raw row data can be passed in as an array or an object using header values as keys
|
|
21
|
+
type RawRowData = RowCellData[] | Record<string, RowCellData>;
|
|
22
|
+
|
|
23
|
+
export class GoogleSpreadsheetWorksheet {
|
|
24
|
+
// assume "header row" (for row-based calls) is in first row, can be adjusted later
|
|
25
|
+
private _headerRowIndex = 1;
|
|
26
|
+
|
|
27
|
+
private _rawProperties: WorksheetProperties | null = null;
|
|
28
|
+
private _cells: GoogleSpreadsheetCell[][] = [];
|
|
29
|
+
private _rowMetadata: any[] = [];
|
|
30
|
+
private _columnMetadata: any[] = [];
|
|
31
|
+
|
|
32
|
+
private _headerValues: string[] | undefined;
|
|
33
|
+
get headerValues() {
|
|
34
|
+
if (!this._headerValues) {
|
|
35
|
+
throw new Error('Header values are not yet loaded');
|
|
15
36
|
}
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
class GoogleSpreadsheetWorksheet {
|
|
20
|
-
constructor(parentSpreadsheet, { properties, data }) {
|
|
21
|
-
this._spreadsheet = parentSpreadsheet; // the parent GoogleSpreadsheet instance
|
|
37
|
+
return this._headerValues!;
|
|
38
|
+
}
|
|
22
39
|
|
|
23
|
-
|
|
40
|
+
constructor(
|
|
41
|
+
/** parent GoogleSpreadsheet instance */
|
|
42
|
+
readonly _spreadsheet: GoogleSpreadsheet,
|
|
43
|
+
rawProperties: WorksheetProperties,
|
|
44
|
+
rawCellData?: CellDataRange[]
|
|
45
|
+
) {
|
|
46
|
+
this._headerRowIndex = 1;
|
|
24
47
|
|
|
25
48
|
// basic properties
|
|
26
|
-
this._rawProperties =
|
|
49
|
+
this._rawProperties = rawProperties;
|
|
27
50
|
|
|
28
51
|
this._cells = []; // we will use a 2d sparse array to store cells;
|
|
29
52
|
|
|
30
53
|
this._rowMetadata = []; // 1d sparse array
|
|
31
54
|
this._columnMetadata = [];
|
|
32
55
|
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
return this;
|
|
56
|
+
if (rawCellData) this._fillCellData(rawCellData);
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
// INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////
|
|
39
|
-
|
|
60
|
+
|
|
61
|
+
updateRawData(properties: WorksheetProperties, rawCellData: CellDataRange[]) {
|
|
62
|
+
this._rawProperties = properties;
|
|
63
|
+
this._fillCellData(rawCellData);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async _makeSingleUpdateRequest(requestType: string, requestParams: any) {
|
|
40
67
|
// pass the call up to the parent
|
|
41
68
|
return this._spreadsheet._makeSingleUpdateRequest(requestType, {
|
|
42
|
-
// sheetId: this.sheetId,
|
|
43
69
|
...requestParams,
|
|
44
70
|
});
|
|
45
71
|
}
|
|
46
72
|
|
|
47
|
-
_ensureInfoLoaded() {
|
|
73
|
+
private _ensureInfoLoaded() {
|
|
48
74
|
if (!this._rawProperties) {
|
|
49
75
|
throw new Error('You must call `doc.loadInfo()` again before accessing this property');
|
|
50
76
|
}
|
|
51
77
|
}
|
|
52
78
|
|
|
53
|
-
|
|
79
|
+
/** clear local cache of sheet data/properties */
|
|
80
|
+
resetLocalCache(
|
|
81
|
+
/** set to true to clear data only, leaving sheet metadata/propeties intact */
|
|
82
|
+
dataOnly?: boolean
|
|
83
|
+
) {
|
|
54
84
|
if (!dataOnly) this._rawProperties = null;
|
|
55
|
-
this.
|
|
85
|
+
this._headerValues = undefined;
|
|
56
86
|
this._headerRowIndex = 1;
|
|
57
87
|
this._cells = [];
|
|
58
88
|
}
|
|
59
89
|
|
|
60
|
-
_fillCellData(
|
|
90
|
+
private _fillCellData(
|
|
91
|
+
dataRanges: CellDataRange[]
|
|
92
|
+
) {
|
|
61
93
|
_.each(dataRanges, (range) => {
|
|
62
94
|
const startRow = range.startRow || 0;
|
|
63
95
|
const startColumn = range.startColumn || 0;
|
|
@@ -101,13 +133,27 @@ class GoogleSpreadsheetWorksheet {
|
|
|
101
133
|
});
|
|
102
134
|
}
|
|
103
135
|
|
|
136
|
+
// TODO: make this handle A1 ranges as well?
|
|
137
|
+
private _addSheetIdToRange(range: GridRangeWithOptionalWorksheetId) {
|
|
138
|
+
if (range.sheetId && range.sheetId !== this.sheetId) {
|
|
139
|
+
throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
...range,
|
|
143
|
+
sheetId: this.sheetId,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
104
147
|
|
|
105
148
|
// PROPERTY GETTERS //////////////////////////////////////////////////////////////////////////////
|
|
106
|
-
|
|
149
|
+
|
|
150
|
+
private _getProp<T extends keyof WorksheetProperties>(param: T): WorksheetProperties[T] {
|
|
107
151
|
this._ensureInfoLoaded();
|
|
108
|
-
|
|
152
|
+
// see note about asserting info loaded on GoogleSpreasheet
|
|
153
|
+
return this._rawProperties![param];
|
|
109
154
|
}
|
|
110
|
-
|
|
155
|
+
// eslint-disable-line no-unused-vars
|
|
156
|
+
private _setProp<T extends keyof WorksheetProperties>(_param: T, _newVal: WorksheetProperties[T]) {
|
|
111
157
|
throw new Error('Do not update directly - use `updateProperties()`');
|
|
112
158
|
}
|
|
113
159
|
|
|
@@ -120,14 +166,14 @@ class GoogleSpreadsheetWorksheet {
|
|
|
120
166
|
get tabColor() { return this._getProp('tabColor'); }
|
|
121
167
|
get rightToLeft() { return this._getProp('rightToLeft'); }
|
|
122
168
|
|
|
123
|
-
set sheetId(newVal) {
|
|
124
|
-
set title(newVal) {
|
|
125
|
-
set index(newVal) {
|
|
126
|
-
set sheetType(newVal) {
|
|
127
|
-
set gridProperties(newVal) {
|
|
128
|
-
set hidden(newVal) {
|
|
129
|
-
set tabColor(newVal) {
|
|
130
|
-
set rightToLeft(newVal) {
|
|
169
|
+
set sheetId(newVal: WorksheetProperties['sheetId']) { this._setProp('sheetId', newVal); }
|
|
170
|
+
set title(newVal: WorksheetProperties['title']) { this._setProp('title', newVal); }
|
|
171
|
+
set index(newVal: WorksheetProperties['index']) { this._setProp('index', newVal); }
|
|
172
|
+
set sheetType(newVal: WorksheetProperties['sheetType']) { this._setProp('sheetType', newVal); }
|
|
173
|
+
set gridProperties(newVal: WorksheetProperties['gridProperties']) { this._setProp('gridProperties', newVal); }
|
|
174
|
+
set hidden(newVal: WorksheetProperties['hidden']) { this._setProp('hidden', newVal); }
|
|
175
|
+
set tabColor(newVal: WorksheetProperties['tabColor']) { this._setProp('tabColor', newVal); }
|
|
176
|
+
set rightToLeft(newVal: WorksheetProperties['rightToLeft']) { this._setProp('rightToLeft', newVal); }
|
|
131
177
|
|
|
132
178
|
get rowCount() {
|
|
133
179
|
this._ensureInfoLoaded();
|
|
@@ -137,13 +183,13 @@ class GoogleSpreadsheetWorksheet {
|
|
|
137
183
|
this._ensureInfoLoaded();
|
|
138
184
|
return this.gridProperties.columnCount;
|
|
139
185
|
}
|
|
140
|
-
get colCount() { throw new Error('`colCount` is deprecated - please use `columnCount` instead.'); }
|
|
141
|
-
set rowCount(newVal) { throw new Error('Do not update directly. Use resize()'); }
|
|
142
|
-
set columnCount(newVal) { throw new Error('Do not update directly. Use resize()'); }
|
|
143
186
|
|
|
144
187
|
get a1SheetName() { return `'${this.title.replace(/'/g, "''")}'`; }
|
|
145
188
|
get encodedA1SheetName() { return encodeURIComponent(this.a1SheetName); }
|
|
146
|
-
get lastColumnLetter() {
|
|
189
|
+
get lastColumnLetter() {
|
|
190
|
+
// TODO: double check behaviour if data not loaded
|
|
191
|
+
return this.columnCount ? columnToLetter(this.columnCount) : '';
|
|
192
|
+
}
|
|
147
193
|
|
|
148
194
|
|
|
149
195
|
// CELLS-BASED INTERACTIONS //////////////////////////////////////////////////////////////////////
|
|
@@ -158,14 +204,15 @@ class GoogleSpreadsheetWorksheet {
|
|
|
158
204
|
};
|
|
159
205
|
}
|
|
160
206
|
|
|
161
|
-
getCellByA1(a1Address) {
|
|
207
|
+
getCellByA1(a1Address: A1Address) {
|
|
162
208
|
const split = a1Address.match(/([A-Z]+)([0-9]+)/);
|
|
209
|
+
if (!split) throw new Error(`Cell address "${a1Address}" not valid`);
|
|
163
210
|
const columnIndex = letterToColumn(split[1]);
|
|
164
211
|
const rowIndex = parseInt(split[2]);
|
|
165
212
|
return this.getCell(rowIndex - 1, columnIndex - 1);
|
|
166
213
|
}
|
|
167
214
|
|
|
168
|
-
getCell(rowIndex, columnIndex) {
|
|
215
|
+
getCell(rowIndex: RowIndex, columnIndex: ColumnIndex) {
|
|
169
216
|
if (rowIndex < 0 || columnIndex < 0) throw new Error('Min coordinate is 0, 0');
|
|
170
217
|
if (rowIndex >= this.rowCount || columnIndex >= this.columnCount) {
|
|
171
218
|
throw new Error(`Out of bounds, sheet is ${this.rowCount} by ${this.columnCount}`);
|
|
@@ -177,12 +224,12 @@ class GoogleSpreadsheetWorksheet {
|
|
|
177
224
|
return this._cells[rowIndex][columnIndex];
|
|
178
225
|
}
|
|
179
226
|
|
|
180
|
-
async loadCells(sheetFilters) {
|
|
227
|
+
async loadCells(sheetFilters?: DataFilterWithoutWorksheetId | DataFilterWithoutWorksheetId[]) {
|
|
181
228
|
// load the whole sheet
|
|
182
229
|
if (!sheetFilters) return this._spreadsheet.loadCells(this.a1SheetName);
|
|
183
230
|
|
|
184
|
-
|
|
185
|
-
|
|
231
|
+
const filtersArray = _.isArray(sheetFilters) ? sheetFilters : [sheetFilters];
|
|
232
|
+
const filtersArrayWithSheetId: DataFilter[] = _.map(filtersArray, (filter) => {
|
|
186
233
|
// add sheet name to A1 ranges
|
|
187
234
|
if (_.isString(filter)) {
|
|
188
235
|
if (filter.startsWith(this.a1SheetName)) return filter;
|
|
@@ -190,19 +237,18 @@ class GoogleSpreadsheetWorksheet {
|
|
|
190
237
|
}
|
|
191
238
|
if (_.isObject(filter)) {
|
|
192
239
|
// TODO: detect and support DeveloperMetadata filters
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
240
|
+
|
|
241
|
+
// check if the user passed in a sheet id
|
|
242
|
+
const filterAny = filter as any;
|
|
243
|
+
if (filterAny.sheetId && filterAny.sheetId !== this.sheetId) {
|
|
197
244
|
throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
|
|
198
|
-
} else {
|
|
199
|
-
return filter;
|
|
200
245
|
}
|
|
201
|
-
|
|
202
|
-
|
|
246
|
+
|
|
247
|
+
return { sheetId: this.sheetId, ...filter };
|
|
203
248
|
}
|
|
249
|
+
throw new Error('Each filter must be a A1 range string or gridrange object');
|
|
204
250
|
});
|
|
205
|
-
return this._spreadsheet.loadCells(
|
|
251
|
+
return this._spreadsheet.loadCells(filtersArrayWithSheetId);
|
|
206
252
|
}
|
|
207
253
|
|
|
208
254
|
async saveUpdatedCells() {
|
|
@@ -213,7 +259,7 @@ class GoogleSpreadsheetWorksheet {
|
|
|
213
259
|
// TODO: do we want to return stats? or the cells that got updated?
|
|
214
260
|
}
|
|
215
261
|
|
|
216
|
-
async saveCells(cellsToUpdate) {
|
|
262
|
+
async saveCells(cellsToUpdate: GoogleSpreadsheetCell[]) {
|
|
217
263
|
// we send an individual "updateCells" request for each cell
|
|
218
264
|
// because the fields that are udpated for each group are the same
|
|
219
265
|
// and we dont want to accidentally overwrite something
|
|
@@ -287,20 +333,26 @@ class GoogleSpreadsheetWorksheet {
|
|
|
287
333
|
|
|
288
334
|
// ROW BASED FUNCTIONS ///////////////////////////////////////////////////////////////////////////
|
|
289
335
|
|
|
290
|
-
async
|
|
336
|
+
async _ensureHeaderRowLoaded() {
|
|
337
|
+
if (!this._headerValues) {
|
|
338
|
+
await this.loadHeaderRow();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async loadHeaderRow(headerRowIndex?: number) {
|
|
291
343
|
if (headerRowIndex !== undefined) this._headerRowIndex = headerRowIndex;
|
|
292
344
|
const rows = await this.getCellsInRange(`A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`);
|
|
293
345
|
if (!rows) {
|
|
294
346
|
throw new Error('No values in the header row - fill the first row with header values before trying to interact with rows');
|
|
295
347
|
}
|
|
296
|
-
this.
|
|
348
|
+
this._headerValues = _.map(rows[0], (header) => header.trim());
|
|
297
349
|
if (!_.compact(this.headerValues).length) {
|
|
298
350
|
throw new Error('All your header cells are blank - fill the first row with header values before trying to interact with rows');
|
|
299
351
|
}
|
|
300
352
|
checkForDuplicateHeaders(this.headerValues);
|
|
301
353
|
}
|
|
302
354
|
|
|
303
|
-
async setHeaderRow(headerValues, headerRowIndex) {
|
|
355
|
+
async setHeaderRow(headerValues: string[], headerRowIndex?: number) {
|
|
304
356
|
if (!headerValues) return;
|
|
305
357
|
if (headerValues.length > this.columnCount) {
|
|
306
358
|
throw new Error(`Sheet is not large enough to fit ${headerValues.length} columns. Resize the sheet first.`);
|
|
@@ -314,7 +366,7 @@ class GoogleSpreadsheetWorksheet {
|
|
|
314
366
|
|
|
315
367
|
if (headerRowIndex) this._headerRowIndex = headerRowIndex;
|
|
316
368
|
|
|
317
|
-
const response = await this._spreadsheet.
|
|
369
|
+
const response = await this._spreadsheet.sheetsApi.request({
|
|
318
370
|
method: 'put',
|
|
319
371
|
url: `/values/${this.encodedA1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
|
|
320
372
|
params: {
|
|
@@ -331,10 +383,14 @@ class GoogleSpreadsheetWorksheet {
|
|
|
331
383
|
]],
|
|
332
384
|
},
|
|
333
385
|
});
|
|
334
|
-
this.
|
|
386
|
+
this._headerValues = response.data.updatedData.values[0];
|
|
335
387
|
}
|
|
336
388
|
|
|
337
|
-
|
|
389
|
+
// TODO: look at these types
|
|
390
|
+
async addRows(
|
|
391
|
+
rows: RawRowData[],
|
|
392
|
+
options: AddRowOptions = {}
|
|
393
|
+
) {
|
|
338
394
|
// adds multiple rows in one API interaction using the append endpoint
|
|
339
395
|
|
|
340
396
|
// each row can be an array or object
|
|
@@ -343,7 +399,7 @@ class GoogleSpreadsheetWorksheet {
|
|
|
343
399
|
// an object must use the header row values as keys
|
|
344
400
|
// ex: { col1: 'column 1', col2: 'column 2', col3: 'column 3' }
|
|
345
401
|
|
|
346
|
-
// google bug that does not handle colons in names
|
|
402
|
+
// google bug that does not handle colons in sheet names
|
|
347
403
|
// see https://issuetracker.google.com/issues/150373119
|
|
348
404
|
if (this.title.includes(':')) {
|
|
349
405
|
throw new Error('Please remove the ":" from your sheet title. There is a bug with the google API which breaks appending rows if any colons are in the sheet title.');
|
|
@@ -351,10 +407,10 @@ class GoogleSpreadsheetWorksheet {
|
|
|
351
407
|
|
|
352
408
|
if (!_.isArray(rows)) throw new Error('You must pass in an array of row values to append');
|
|
353
409
|
|
|
354
|
-
|
|
410
|
+
await this._ensureHeaderRowLoaded();
|
|
355
411
|
|
|
356
412
|
// convert each row into an array of cell values rather than the key/value object
|
|
357
|
-
const rowsAsArrays = [];
|
|
413
|
+
const rowsAsArrays: RawRowData[] = [];
|
|
358
414
|
_.each(rows, (row) => {
|
|
359
415
|
let rowAsArray;
|
|
360
416
|
if (_.isArray(row)) {
|
|
@@ -371,7 +427,7 @@ class GoogleSpreadsheetWorksheet {
|
|
|
371
427
|
rowsAsArrays.push(rowAsArray);
|
|
372
428
|
});
|
|
373
429
|
|
|
374
|
-
const response = await this._spreadsheet.
|
|
430
|
+
const response = await this._spreadsheet.sheetsApi.request({
|
|
375
431
|
method: 'post',
|
|
376
432
|
url: `/values/${this.encodedA1SheetName}!A${this._headerRowIndex}:append`,
|
|
377
433
|
params: {
|
|
@@ -390,12 +446,14 @@ class GoogleSpreadsheetWorksheet {
|
|
|
390
446
|
let rowNumber = updatedRange.match(/![A-Z]+([0-9]+):?/)[1];
|
|
391
447
|
rowNumber = parseInt(rowNumber);
|
|
392
448
|
|
|
449
|
+
|
|
450
|
+
this._ensureInfoLoaded();
|
|
393
451
|
// if new rows were added, we need update sheet.rowRount
|
|
394
452
|
if (options.insert) {
|
|
395
|
-
this._rawProperties
|
|
453
|
+
this._rawProperties!.gridProperties.rowCount += rows.length;
|
|
396
454
|
} else if (rowNumber + rows.length > this.rowCount) {
|
|
397
455
|
// have to subtract 1 since one row was inserted at rowNumber
|
|
398
|
-
this._rawProperties
|
|
456
|
+
this._rawProperties!.gridProperties.rowCount = rowNumber + rows.length - 1;
|
|
399
457
|
}
|
|
400
458
|
|
|
401
459
|
return _.map(response.data.updates.updatedData.values, (rowValues) => {
|
|
@@ -404,12 +462,22 @@ class GoogleSpreadsheetWorksheet {
|
|
|
404
462
|
});
|
|
405
463
|
}
|
|
406
464
|
|
|
407
|
-
|
|
465
|
+
/** add a single row - see addRows for more info */
|
|
466
|
+
async addRow(rowValues: RawRowData, options?: AddRowOptions) {
|
|
408
467
|
const rows = await this.addRows([rowValues], options);
|
|
409
468
|
return rows[0];
|
|
410
469
|
}
|
|
411
470
|
|
|
412
|
-
|
|
471
|
+
|
|
472
|
+
private _rowCache: GoogleSpreadsheetRow[] = [];
|
|
473
|
+
async getRows<T extends Record<string, any>>(
|
|
474
|
+
options?: {
|
|
475
|
+
/** skip first N rows */
|
|
476
|
+
offset?: number,
|
|
477
|
+
/** limit number of rows fetched */
|
|
478
|
+
limit?: number,
|
|
479
|
+
}
|
|
480
|
+
) {
|
|
413
481
|
// https://developers.google.com/sheets/api/guides/migration
|
|
414
482
|
// v4 API does not have equivalents for the row-order query parameters provided
|
|
415
483
|
// Reverse-order is trivial; simply process the returned values array in reverse order.
|
|
@@ -417,18 +485,13 @@ class GoogleSpreadsheetWorksheet {
|
|
|
417
485
|
|
|
418
486
|
// v4 API does not currently have a direct equivalent for the Sheets API v3 structured queries
|
|
419
487
|
// However, you can retrieve the relevant data and sort through it as needed in your application
|
|
488
|
+
const offset = options?.offset || 0;
|
|
489
|
+
const limit = options?.limit || this.rowCount - 1;
|
|
420
490
|
|
|
421
|
-
|
|
422
|
-
// - offset
|
|
423
|
-
// - limit
|
|
424
|
-
|
|
425
|
-
options.offset = options.offset || 0;
|
|
426
|
-
options.limit = options.limit || this.rowCount - 1;
|
|
427
|
-
|
|
428
|
-
if (!this.headerValues) await this.loadHeaderRow();
|
|
491
|
+
await this._ensureHeaderRowLoaded();
|
|
429
492
|
|
|
430
|
-
const firstRow = 1 + this._headerRowIndex +
|
|
431
|
-
const lastRow = firstRow +
|
|
493
|
+
const firstRow = 1 + this._headerRowIndex + offset;
|
|
494
|
+
const lastRow = firstRow + limit - 1; // inclusive so we subtract 1
|
|
432
495
|
const lastColumn = columnToLetter(this.headerValues.length);
|
|
433
496
|
const rawRows = await this.getCellsInRange(
|
|
434
497
|
`A${firstRow}:${lastColumn}${lastRow}`
|
|
@@ -439,30 +502,46 @@ class GoogleSpreadsheetWorksheet {
|
|
|
439
502
|
const rows = [];
|
|
440
503
|
let rowNum = firstRow;
|
|
441
504
|
for (let i = 0; i < rawRows.length; i++) {
|
|
442
|
-
|
|
505
|
+
const row = new GoogleSpreadsheetRow<T>(this, rowNum++, rawRows[i]);
|
|
506
|
+
this._rowCache[row.rowNumber] = row;
|
|
507
|
+
rows.push(row);
|
|
443
508
|
}
|
|
444
509
|
return rows;
|
|
445
510
|
}
|
|
446
511
|
|
|
447
|
-
|
|
512
|
+
/**
|
|
513
|
+
* @internal
|
|
514
|
+
* Used internally to update row numbers after deleting rows.
|
|
515
|
+
* Should not be called directly.
|
|
516
|
+
* */
|
|
517
|
+
_shiftRowCache(deletedRowNumber: number) {
|
|
518
|
+
delete this._rowCache[deletedRowNumber];
|
|
519
|
+
this._rowCache.forEach((row) => {
|
|
520
|
+
if (row.rowNumber > deletedRowNumber) {
|
|
521
|
+
row._updateRowNumber(row.rowNumber - 1);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async clearRows(
|
|
527
|
+
options?: {
|
|
528
|
+
start?: number,
|
|
529
|
+
end?: number,
|
|
530
|
+
}
|
|
531
|
+
) {
|
|
448
532
|
// default to first row after header
|
|
449
|
-
const startRowIndex = options
|
|
450
|
-
const endRowIndex = options
|
|
451
|
-
await this._spreadsheet.
|
|
533
|
+
const startRowIndex = options?.start || this._headerRowIndex + 1;
|
|
534
|
+
const endRowIndex = options?.end || this.rowCount;
|
|
535
|
+
await this._spreadsheet.sheetsApi.post(`/values/${this.encodedA1SheetName}!${startRowIndex}:${endRowIndex}:clear`);
|
|
536
|
+
this._rowCache.forEach((row) => {
|
|
537
|
+
if (row.rowNumber >= startRowIndex && row.rowNumber <= endRowIndex) row._clearRowData();
|
|
538
|
+
});
|
|
452
539
|
}
|
|
453
540
|
|
|
454
541
|
// BASIC PROPS ///////////////////////////////////////////////////////////////////////////////////
|
|
455
|
-
|
|
542
|
+
/** @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSheetPropertiesRequest */
|
|
543
|
+
async updateProperties(properties: Partial<Omit<WorksheetProperties, 'sheetId'>>) {
|
|
456
544
|
// Request type = `updateSheetProperties`
|
|
457
|
-
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSheetPropertiesRequest
|
|
458
|
-
|
|
459
|
-
// properties
|
|
460
|
-
// - title (string)
|
|
461
|
-
// - index (number)
|
|
462
|
-
// - gridProperties ({ object (GridProperties) } - https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/sheets#gridproperties
|
|
463
|
-
// - hidden (boolean)
|
|
464
|
-
// - tabColor ({ object (Color) } - https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/other#Color
|
|
465
|
-
// - rightToLeft (boolean)
|
|
466
545
|
|
|
467
546
|
return this._makeSingleUpdateRequest('updateSheetProperties', {
|
|
468
547
|
properties: {
|
|
@@ -473,48 +552,39 @@ class GoogleSpreadsheetWorksheet {
|
|
|
473
552
|
});
|
|
474
553
|
}
|
|
475
554
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
// gridProperties
|
|
481
|
-
// - rowCount
|
|
482
|
-
// - columnCount
|
|
483
|
-
// - frozenRowCount
|
|
484
|
-
// - frozenColumnCount
|
|
485
|
-
// - hideGridLines
|
|
555
|
+
/**
|
|
556
|
+
* passes through the call to updateProperties to update only the gridProperties object
|
|
557
|
+
*/
|
|
558
|
+
async updateGridProperties(gridProperties: WorksheetGridProperties) {
|
|
486
559
|
return this.updateProperties({ gridProperties });
|
|
487
560
|
}
|
|
488
561
|
|
|
489
|
-
|
|
490
|
-
async resize(gridProperties) {
|
|
562
|
+
/** resize, internally just calls updateGridProperties */
|
|
563
|
+
async resize(gridProperties: Pick<WorksheetGridProperties, 'rowCount' | 'columnCount'>) {
|
|
491
564
|
return this.updateGridProperties(gridProperties);
|
|
492
565
|
}
|
|
493
566
|
|
|
494
|
-
|
|
567
|
+
/**
|
|
568
|
+
*
|
|
569
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#updatedimensionpropertiesrequest
|
|
570
|
+
*/
|
|
571
|
+
async updateDimensionProperties(
|
|
572
|
+
columnsOrRows: WorksheetDimension,
|
|
573
|
+
properties: WorksheetDimensionProperties,
|
|
574
|
+
bounds: Partial<DimensionRangeIndexes>
|
|
575
|
+
) {
|
|
495
576
|
// Request type = `updateDimensionProperties`
|
|
496
|
-
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#updatedimensionpropertiesrequest
|
|
497
577
|
|
|
498
|
-
|
|
499
|
-
// properties
|
|
500
|
-
// - pixelSize
|
|
501
|
-
// - hiddenByUser
|
|
502
|
-
// - developerMetadata
|
|
503
|
-
// bounds
|
|
504
|
-
// - startIndex
|
|
505
|
-
// - endIndex
|
|
578
|
+
Object.keys(properties);
|
|
506
579
|
|
|
507
580
|
return this._makeSingleUpdateRequest('updateDimensionProperties', {
|
|
508
581
|
range: {
|
|
509
582
|
sheetId: this.sheetId,
|
|
510
583
|
dimension: columnsOrRows,
|
|
511
|
-
...bounds
|
|
512
|
-
startIndex: bounds.startIndex,
|
|
513
|
-
endIndex: bounds.endIndex,
|
|
514
|
-
},
|
|
584
|
+
...bounds,
|
|
515
585
|
},
|
|
516
586
|
properties,
|
|
517
|
-
fields: getFieldMask(properties),
|
|
587
|
+
fields: getFieldMask(properties as any),
|
|
518
588
|
});
|
|
519
589
|
}
|
|
520
590
|
|
|
@@ -522,8 +592,8 @@ class GoogleSpreadsheetWorksheet {
|
|
|
522
592
|
|
|
523
593
|
// this uses the "values" getter and does not give all the info about the cell contents
|
|
524
594
|
// it is used internally when loading header cells
|
|
525
|
-
async getCellsInRange(a1Range, options) {
|
|
526
|
-
const response = await this._spreadsheet.
|
|
595
|
+
async getCellsInRange(a1Range: A1Range, options?: GetValuesRequestOptions) {
|
|
596
|
+
const response = await this._spreadsheet.sheetsApi.get(`/values/${this.encodedA1SheetName}!${a1Range}`, {
|
|
527
597
|
params: options,
|
|
528
598
|
});
|
|
529
599
|
return response.data.values;
|
|
@@ -564,32 +634,26 @@ class GoogleSpreadsheetWorksheet {
|
|
|
564
634
|
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#CopyPasteRequest
|
|
565
635
|
}
|
|
566
636
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
637
|
+
// TODO: check types on these ranges
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Merges all cells in the range
|
|
641
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest
|
|
642
|
+
*/
|
|
643
|
+
async mergeCells(range: GridRangeWithOptionalWorksheetId, mergeType = 'MERGE_ALL') {
|
|
573
644
|
await this._makeSingleUpdateRequest('mergeCells', {
|
|
574
645
|
mergeType,
|
|
575
|
-
range:
|
|
576
|
-
...range,
|
|
577
|
-
sheetId: this.sheetId,
|
|
578
|
-
},
|
|
646
|
+
range: this._addSheetIdToRange(range),
|
|
579
647
|
});
|
|
580
648
|
}
|
|
581
649
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
650
|
+
/**
|
|
651
|
+
* Unmerges cells in the given range
|
|
652
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UnmergeCellsRequest
|
|
653
|
+
*/
|
|
654
|
+
async unmergeCells(range: GridRangeWithOptionalWorksheetId) {
|
|
588
655
|
await this._makeSingleUpdateRequest('unmergeCells', {
|
|
589
|
-
range:
|
|
590
|
-
...range,
|
|
591
|
-
sheetId: this.sheetId,
|
|
592
|
-
},
|
|
656
|
+
range: this._addSheetIdToRange(range),
|
|
593
657
|
});
|
|
594
658
|
}
|
|
595
659
|
|
|
@@ -633,14 +697,22 @@ class GoogleSpreadsheetWorksheet {
|
|
|
633
697
|
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateFilterViewRequest
|
|
634
698
|
}
|
|
635
699
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
700
|
+
/**
|
|
701
|
+
* Duplicate worksheet within the document
|
|
702
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateSheetRequest
|
|
703
|
+
*/
|
|
704
|
+
async duplicate(
|
|
705
|
+
options?: {
|
|
706
|
+
id?: WorksheetId,
|
|
707
|
+
title?: string,
|
|
708
|
+
index?: number,
|
|
709
|
+
}
|
|
710
|
+
) {
|
|
639
711
|
const response = await this._makeSingleUpdateRequest('duplicateSheet', {
|
|
640
712
|
sourceSheetId: this.sheetId,
|
|
641
|
-
...options
|
|
642
|
-
...options
|
|
643
|
-
...options
|
|
713
|
+
...options?.index !== undefined && { insertSheetIndex: options.index },
|
|
714
|
+
...options?.id && { newSheetId: options.id },
|
|
715
|
+
...options?.title && { newSheetName: options.title },
|
|
644
716
|
});
|
|
645
717
|
const newSheetId = response.properties.sheetId;
|
|
646
718
|
return this._spreadsheet.sheetsById[newSheetId];
|
|
@@ -651,23 +723,28 @@ class GoogleSpreadsheetWorksheet {
|
|
|
651
723
|
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#FindReplaceRequest
|
|
652
724
|
}
|
|
653
725
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
726
|
+
/**
|
|
727
|
+
* Inserts rows or columns at a particular index
|
|
728
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertDimensionRequest
|
|
729
|
+
*/
|
|
730
|
+
async insertDimension(
|
|
731
|
+
columnsOrRows: WorksheetDimension,
|
|
732
|
+
rangeIndexes: DimensionRangeIndexes,
|
|
733
|
+
inheritFromBefore?: boolean
|
|
734
|
+
) {
|
|
658
735
|
if (!columnsOrRows) throw new Error('You need to specify a dimension. i.e. COLUMNS|ROWS');
|
|
659
|
-
if (!_.isObject(
|
|
660
|
-
if (!_.isInteger(
|
|
661
|
-
if (!_.isInteger(
|
|
662
|
-
if (
|
|
736
|
+
if (!_.isObject(rangeIndexes)) throw new Error('`range` must be an object containing `startIndex` and `endIndex`');
|
|
737
|
+
if (!_.isInteger(rangeIndexes.startIndex) || rangeIndexes.startIndex < 0) throw new Error('range.startIndex must be an integer >=0');
|
|
738
|
+
if (!_.isInteger(rangeIndexes.endIndex) || rangeIndexes.endIndex < 0) throw new Error('range.endIndex must be an integer >=0');
|
|
739
|
+
if (rangeIndexes.endIndex <= rangeIndexes.startIndex) throw new Error('range.endIndex must be greater than range.startIndex');
|
|
663
740
|
|
|
664
741
|
// default inheritFromBefore to true - unless inserting in the first row/column
|
|
665
|
-
if (inheritFromBefore ===
|
|
666
|
-
inheritFromBefore =
|
|
742
|
+
if (inheritFromBefore === undefined) {
|
|
743
|
+
inheritFromBefore = rangeIndexes.startIndex > 0;
|
|
667
744
|
}
|
|
668
745
|
|
|
669
746
|
// do not allow inheritFromBefore if inserting at first row/column
|
|
670
|
-
if (inheritFromBefore &&
|
|
747
|
+
if (inheritFromBefore && rangeIndexes.startIndex === 0) {
|
|
671
748
|
throw new Error('Cannot set inheritFromBefore to true if inserting in first row/column');
|
|
672
749
|
}
|
|
673
750
|
|
|
@@ -675,8 +752,8 @@ class GoogleSpreadsheetWorksheet {
|
|
|
675
752
|
range: {
|
|
676
753
|
sheetId: this.sheetId,
|
|
677
754
|
dimension: columnsOrRows,
|
|
678
|
-
startIndex:
|
|
679
|
-
endIndex:
|
|
755
|
+
startIndex: rangeIndexes.startIndex,
|
|
756
|
+
endIndex: rangeIndexes.endIndex,
|
|
680
757
|
},
|
|
681
758
|
inheritFromBefore,
|
|
682
759
|
});
|
|
@@ -852,35 +929,51 @@ class GoogleSpreadsheetWorksheet {
|
|
|
852
929
|
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateSlicerSpecRequest
|
|
853
930
|
}
|
|
854
931
|
|
|
855
|
-
|
|
932
|
+
/** delete this worksheet */
|
|
856
933
|
async delete() {
|
|
857
934
|
return this._spreadsheet.deleteSheet(this.sheetId);
|
|
858
935
|
}
|
|
859
|
-
async del() { return this.delete(); } // alias to mimic old interface
|
|
860
936
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
937
|
+
/**
|
|
938
|
+
* copies this worksheet into another document/spreadsheet
|
|
939
|
+
* @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.sheets/copyTo
|
|
940
|
+
* */
|
|
941
|
+
async copyToSpreadsheet(destinationSpreadsheetId: SpreadsheetId) {
|
|
942
|
+
return this._spreadsheet.sheetsApi.post(`/sheets/${this.sheetId}:copyTo`, {
|
|
864
943
|
destinationSpreadsheetId,
|
|
865
944
|
});
|
|
866
945
|
}
|
|
867
946
|
|
|
868
|
-
|
|
869
|
-
|
|
947
|
+
/** clear data in the sheet - either the entire sheet or a specific range */
|
|
948
|
+
async clear(
|
|
949
|
+
/** optional A1 range to clear - defaults to entire sheet */
|
|
950
|
+
a1Range?: A1Range
|
|
951
|
+
) {
|
|
870
952
|
const range = a1Range ? `!${a1Range}` : '';
|
|
871
953
|
// sheet name without ie 'sheet1' rather than 'sheet1'!A1:B5 is all cells
|
|
872
|
-
await this._spreadsheet.
|
|
954
|
+
await this._spreadsheet.sheetsApi.post(`/values/${this.encodedA1SheetName}${range}:clear`);
|
|
873
955
|
this.resetLocalCache(true);
|
|
874
956
|
}
|
|
957
|
+
|
|
958
|
+
/** exports worksheet as CSV file (comma-separated values) */
|
|
959
|
+
async downloadAsCSV(): Promise<ArrayBuffer>;
|
|
960
|
+
async downloadAsCSV(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;
|
|
961
|
+
async downloadAsCSV(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;
|
|
875
962
|
async downloadAsCSV(returnStreamInsteadOfBuffer = false) {
|
|
876
963
|
return this._spreadsheet._downloadAs('csv', this.sheetId, returnStreamInsteadOfBuffer);
|
|
877
964
|
}
|
|
965
|
+
/** exports worksheet as TSC file (tab-separated values) */
|
|
966
|
+
async downloadAsTSV(): Promise<ArrayBuffer>;
|
|
967
|
+
async downloadAsTSV(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;
|
|
968
|
+
async downloadAsTSV(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;
|
|
878
969
|
async downloadAsTSV(returnStreamInsteadOfBuffer = false) {
|
|
879
970
|
return this._spreadsheet._downloadAs('tsv', this.sheetId, returnStreamInsteadOfBuffer);
|
|
880
971
|
}
|
|
972
|
+
/** exports worksheet as PDF */
|
|
973
|
+
async downloadAsPDF(): Promise<ArrayBuffer>;
|
|
974
|
+
async downloadAsPDF(returnStreamInsteadOfBuffer: false): Promise<ArrayBuffer>;
|
|
975
|
+
async downloadAsPDF(returnStreamInsteadOfBuffer: true): Promise<ReadableStream>;
|
|
881
976
|
async downloadAsPDF(returnStreamInsteadOfBuffer = false) {
|
|
882
977
|
return this._spreadsheet._downloadAs('pdf', this.sheetId, returnStreamInsteadOfBuffer);
|
|
883
978
|
}
|
|
884
979
|
}
|
|
885
|
-
|
|
886
|
-
module.exports = GoogleSpreadsheetWorksheet;
|