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.
@@ -1,63 +1,95 @@
1
- const _ = require('lodash');
2
-
3
- const GoogleSpreadsheetRow = require('./GoogleSpreadsheetRow');
4
- const GoogleSpreadsheetCell = require('./GoogleSpreadsheetCell');
5
-
6
- const { getFieldMask, columnToLetter, letterToColumn } = require('./utils');
7
-
8
- function checkForDuplicateHeaders(headers) {
9
- // check for duplicate headers
10
- const checkForDupes = _.groupBy(headers); // { c1: ['c1'], c2: ['c2', 'c2' ]}
11
- _.each(checkForDupes, (grouped, header) => {
12
- if (!header) return; // empty columns are skipped, so multiple is ok
13
- if (grouped.length > 1) {
14
- throw new Error(`Duplicate header detected: "${header}". Please make sure all non-empty headers are unique`);
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
- this._headerRowIndex = 1; // assume "header row" (for row-based calls) is in first row
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 = properties;
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 (data) this._fillCellData(data);
34
-
35
- return this;
56
+ if (rawCellData) this._fillCellData(rawCellData);
36
57
  }
37
58
 
38
59
  // INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////
39
- async _makeSingleUpdateRequest(requestType, requestParams) {
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
- resetLocalCache(dataOnly) {
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.headerValues = null;
85
+ this._headerValues = undefined;
56
86
  this._headerRowIndex = 1;
57
87
  this._cells = [];
58
88
  }
59
89
 
60
- _fillCellData(dataRanges) {
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
- _getProp(param) {
149
+
150
+ private _getProp<T extends keyof WorksheetProperties>(param: T): WorksheetProperties[T] {
107
151
  this._ensureInfoLoaded();
108
- return this._rawProperties[param];
152
+ // see note about asserting info loaded on GoogleSpreasheet
153
+ return this._rawProperties![param];
109
154
  }
110
- _setProp(param, newVal) { // eslint-disable-line no-unused-vars
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) { return this._setProp('sheetId', newVal); }
124
- set title(newVal) { return this._setProp('title', newVal); }
125
- set index(newVal) { return this._setProp('index', newVal); }
126
- set sheetType(newVal) { return this._setProp('sheetType', newVal); }
127
- set gridProperties(newVal) { return this._setProp('gridProperties', newVal); }
128
- set hidden(newVal) { return this._setProp('hidden', newVal); }
129
- set tabColor(newVal) { return this._setProp('tabColor', newVal); }
130
- set rightToLeft(newVal) { return this._setProp('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() { return columnToLetter(this.columnCount); }
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
- let filtersArray = _.isArray(sheetFilters) ? sheetFilters : [sheetFilters];
185
- filtersArray = _.map(filtersArray, (filter) => {
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
- if (!filter.sheetId) {
194
- return { sheetId: this.sheetId, ...filter };
195
- }
196
- if (filter.sheetId !== this.sheetId) {
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
- } else {
202
- throw new Error('Each filter must be a A1 range string or gridrange object');
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(filtersArray);
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 loadHeaderRow(headerRowIndex) {
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.headerValues = _.map(rows[0], (header) => header.trim());
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.axios.request({
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.headerValues = response.data.updatedData.values[0];
386
+ this._headerValues = response.data.updatedData.values[0];
335
387
  }
336
388
 
337
- async addRows(rows, options = {}) {
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
- if (!this.headerValues) await this.loadHeaderRow();
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.axios.request({
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.gridProperties.rowCount += rows.length;
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.gridProperties.rowCount = rowNumber + rows.length - 1;
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
- async addRow(rowValues, options) {
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
- async getRows(options = {}) {
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
- // options
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 + options.offset;
431
- const lastRow = firstRow + options.limit - 1; // inclusive so we subtract 1
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
- rows.push(new GoogleSpreadsheetRow(this, rowNum++, rawRows[i]));
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
- async clearRows(options = {}) {
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.start || this._headerRowIndex + 1;
450
- const endRowIndex = options.end || this.rowCount;
451
- await this._spreadsheet.axios.post(`/values/${this.encodedA1SheetName}!${startRowIndex}:${endRowIndex}:clear`);
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
- async updateProperties(properties) {
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
- async updateGridProperties(gridProperties) {
477
- // just passes the call through to update gridProperties
478
- // see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/sheets#GridProperties
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
- // just a shortcut because resize makes more sense to change rowCount / columnCount
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
- async updateDimensionProperties(columnsOrRows, properties, bounds) {
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
- // columnsOrRows = COLUMNS|ROWS
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.axios.get(`/values/${this.encodedA1SheetName}!${a1Range}`, {
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
- async mergeCells(range, mergeType = 'MERGE_ALL') {
568
- // Request type = `mergeCells`
569
- // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest
570
- if (range.sheetId && range.sheetId !== this.sheetId) {
571
- throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
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
- async unmergeCells(range) {
583
- // Request type = `unmergeCells`
584
- // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UnmergeCellsRequest
585
- if (range.sheetId && range.sheetId !== this.sheetId) {
586
- throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
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
- async duplicate(options = {}) {
637
- // Request type = `duplicateSheet`
638
- // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DuplicateSheetRequest
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.index !== undefined && { insertSheetIndex: options.index },
642
- ...options.id && { newSheetId: options.id },
643
- ...options.title && { newSheetName: options.title },
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
- async insertDimension(columnsOrRows, range, inheritFromBefore = null) {
655
- // Request type = `insertDimension`
656
- // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertDimensionRequest
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(range)) throw new Error('`range` must be an object containing `startIndex` and `endIndex`');
660
- if (!_.isInteger(range.startIndex) || range.startIndex < 0) throw new Error('range.startIndex must be an integer >=0');
661
- if (!_.isInteger(range.endIndex) || range.endIndex < 0) throw new Error('range.endIndex must be an integer >=0');
662
- if (range.endIndex <= range.startIndex) throw new Error('range.endIndex must be greater than range.startIndex');
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 === null) {
666
- inheritFromBefore = range.startIndex > 0;
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 && range.startIndex === 0) {
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: range.startIndex,
679
- endIndex: range.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
- // delete this worksheet
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
- // copies this worksheet into another document/spreadsheet
862
- async copyToSpreadsheet(destinationSpreadsheetId) {
863
- return this._spreadsheet.axios.post(`/sheets/${this.sheetId}:copyTo`, {
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
- async clear(a1Range) {
869
- // clears data in the sheet - defaults to entire sheet
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.axios.post(`/values/${this.encodedA1SheetName}${range}:clear`);
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;