google-spreadsheet 5.0.3 → 5.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "google-spreadsheet",
3
- "version": "5.0.3",
3
+ "version": "5.2.0",
4
4
  "description": "Google Sheets API -- simple interface to read/write data and manage sheets",
5
5
  "keywords": [
6
6
  "google spreadsheets",
@@ -66,6 +66,7 @@
66
66
  "eslint-plugin-import": "^2.27.5",
67
67
  "eslint-plugin-no-floating-promise": "^1.0.2",
68
68
  "google-auth-library": "^10.5.0",
69
+ "nock": "^14.0.11",
69
70
  "tsdown": "^0.20.3",
70
71
  "typescript": "^5.9.3",
71
72
  "varlock": "^0.2.2",
@@ -79,4 +80,4 @@
79
80
  "optional": true
80
81
  }
81
82
  }
82
- }
83
+ }
@@ -1,10 +1,18 @@
1
- import ky, { HTTPError, KyInstance } from 'ky'; // eslint-disable-line import/no-extraneous-dependencies
1
+ import ky, { HTTPError, KyInstance, RetryOptions } from 'ky'; // eslint-disable-line import/no-extraneous-dependencies
2
2
  import * as _ from './toolkit';
3
3
  import { GoogleSpreadsheetWorksheet } from './GoogleSpreadsheetWorksheet';
4
4
  import { getFieldMask } from './utils';
5
5
  import {
6
- DataFilter, GridRange, NamedRangeId, ProtectedRange,
7
- SpreadsheetId, SpreadsheetProperties, WorksheetId, WorksheetProperties,
6
+ DataFilter,
7
+ DataFilterObject,
8
+ DeveloperMetadata,
9
+ GridRange,
10
+ NamedRangeId,
11
+ ProtectedRange,
12
+ SpreadsheetId,
13
+ SpreadsheetProperties,
14
+ WorksheetId,
15
+ WorksheetProperties,
8
16
  } from './types/sheets-types';
9
17
  import { PermissionRoles, PermissionsList, PublicPermissionRoles } from './types/drive-types';
10
18
  import { RecursivePartial } from './types/util-types';
@@ -112,11 +120,20 @@ export class GoogleSpreadsheet {
112
120
  * @category Initialization
113
121
  * */
114
122
  constructor(
115
- /** id of google spreadsheet doc */
123
+ /** id of Google spreadsheet doc */
116
124
  spreadsheetId: SpreadsheetId,
117
125
  /** authentication to use with Google Sheets API */
118
- auth: GoogleApiAuth
126
+ auth: GoogleApiAuth,
127
+ /** Additional options */
128
+ options?: {
129
+ /**
130
+ * customize retry behavior --
131
+ * see the [ky documentation](https://github.com/sindresorhus/ky#retry) for details of the available options and defaults.
132
+ * */
133
+ retryConfig?: RetryOptions | number
134
+ }
119
135
  ) {
136
+ const { retryConfig } = options || {};
120
137
  this.spreadsheetId = spreadsheetId;
121
138
  this.auth = auth;
122
139
 
@@ -131,6 +148,7 @@ export class GoogleSpreadsheet {
131
148
  beforeRequest: [(r) => this._setAuthRequestHook(r)],
132
149
  beforeError: [(e) => this._errorHook(e)],
133
150
  },
151
+ retry: retryConfig,
134
152
  });
135
153
  this.driveApi = ky.create({
136
154
  prefixUrl: `${DRIVE_API_BASE_URL}/${spreadsheetId}`,
@@ -138,6 +156,7 @@ export class GoogleSpreadsheet {
138
156
  beforeRequest: [(r) => this._setAuthRequestHook(r)],
139
157
  beforeError: [(e) => this._errorHook(e)],
140
158
  },
159
+ retry: retryConfig,
141
160
  });
142
161
  }
143
162
 
@@ -395,15 +414,12 @@ export class GoogleSpreadsheet {
395
414
  async loadCells(
396
415
  /**
397
416
  * single filter or array of filters
398
- * strings are treated as A1 ranges, objects are treated as GridRange objects
417
+ * strings are treated as A1 ranges, objects are treated as GridRange objects,
418
+ * objects with a `developerMetadataLookup` key are treated as DeveloperMetadataLookup filters
399
419
  * pass nothing to fetch all cells
400
420
  * */
401
421
  filters?: DataFilter | DataFilter[]
402
422
  ) {
403
- // TODO: make it support DeveloperMetadataLookup objects
404
-
405
-
406
-
407
423
  // TODO: switch to this mode if using a read-only auth token?
408
424
  const readOnlyMode = this.authMode === AUTH_MODES.API_KEY;
409
425
 
@@ -416,7 +432,9 @@ export class GoogleSpreadsheet {
416
432
  if (readOnlyMode) {
417
433
  throw new Error('Only A1 ranges are supported when fetching cells with read-only access (using only an API key)');
418
434
  }
419
- // TODO: make this support Developer Metadata filters
435
+ if ('developerMetadataLookup' in filter) {
436
+ return { developerMetadataLookup: filter.developerMetadataLookup };
437
+ }
420
438
  return { gridRange: filter };
421
439
  }
422
440
  throw new Error('Each filter must be an A1 range string or a gridrange object');
@@ -631,6 +649,24 @@ export class GoogleSpreadsheet {
631
649
  await this.driveApi.delete(`permissions/${permissionId}`);
632
650
  }
633
651
 
652
+ // DEVELOPER METADATA ///////////////////////////////////////////////////////////////////////////
653
+
654
+ /**
655
+ * search for developer metadata entries matching the given filters
656
+ * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.developerMetadata/search
657
+ */
658
+ async searchDeveloperMetadata(
659
+ /** array of DataFilter objects to match against */
660
+ filters: DataFilterObject[]
661
+ ): Promise<DeveloperMetadata[]> {
662
+ const response = await this.sheetsApi.post('developerMetadata:search', {
663
+ json: { dataFilters: filters },
664
+ });
665
+ const data = await response.json<any>();
666
+ if (!data.matchedDeveloperMetadata) return [];
667
+ return data.matchedDeveloperMetadata.map((m: any) => m.developerMetadata);
668
+ }
669
+
634
670
  //
635
671
  // CREATE NEW DOC ////////////////////////////////////////////////////////////////////////////////
636
672
  static async createNewSpreadsheetDocument(auth: GoogleApiAuth, properties?: Partial<SpreadsheetProperties>) {
@@ -15,6 +15,7 @@ export class GoogleSpreadsheetCell {
15
15
  private _rawData?: CellData;
16
16
  private _draftData: any = {};
17
17
  private _error?: GoogleSpreadsheetCellErrorValue;
18
+ private _deleted = false;
18
19
 
19
20
  constructor(
20
21
  readonly _sheet: GoogleSpreadsheetWorksheet,
@@ -26,6 +27,8 @@ export class GoogleSpreadsheetCell {
26
27
  this._rawData = rawCellData; // so TS does not complain
27
28
  }
28
29
 
30
+ get deleted() { return this._deleted; }
31
+
29
32
  // TODO: figure out how to deal with empty rawData
30
33
  // newData can be undefined/null if the cell is totally empty and unformatted
31
34
  /**
@@ -49,6 +52,25 @@ export class GoogleSpreadsheetCell {
49
52
  get a1Row() { return this._rowIndex + 1; } // a1 row numbers start at 1 instead of 0
50
53
  get a1Address() { return `${this.a1Column}${this.a1Row}`; }
51
54
 
55
+ /**
56
+ * @internal
57
+ * Used internally to update cell indices after deleting rows/columns.
58
+ * Should not be called directly.
59
+ */
60
+ _updateIndices(rowIndex: RowIndex, columnIndex: ColumnIndex) {
61
+ this._rowIndex = rowIndex;
62
+ this._columnIndex = columnIndex;
63
+ }
64
+
65
+ /**
66
+ * @internal
67
+ * Used internally to mark cell as deleted.
68
+ * Should not be called directly.
69
+ */
70
+ _markDeleted() {
71
+ this._deleted = true;
72
+ }
73
+
52
74
  // CELL CONTENTS - VALUE/FORMULA/NOTES ///////////////////////////////////////////////////////////
53
75
  get value(): number | boolean | string | null | GoogleSpreadsheetCellErrorValue {
54
76
  // const typeKey = _.keys(this._rawData.effectiveValue)[0];
@@ -60,6 +82,8 @@ export class GoogleSpreadsheetCell {
60
82
 
61
83
 
62
84
  set value(newValue: number | boolean | Date | string | null | undefined | GoogleSpreadsheetCellErrorValue) {
85
+ if (this._deleted) throw new Error('This cell has been deleted - reload cells before making updates.');
86
+
63
87
  // had to include the GoogleSpreadsheetCellErrorValue in the type to make TS happy
64
88
  if (newValue instanceof GoogleSpreadsheetCellErrorValue) {
65
89
  throw new Error("You can't manually set a value to an error");
@@ -128,10 +152,8 @@ export class GoogleSpreadsheetCell {
128
152
  return this.value as string;
129
153
  }
130
154
  set stringValue(val: string | undefined) {
131
- if (val?.startsWith('=')) {
132
- throw new Error('Use cell.formula to set formula values');
133
- }
134
- this.value = val;
155
+ this._draftData.valueType = 'stringValue';
156
+ this._draftData.value = val || '';
135
157
  }
136
158
 
137
159
  /**
@@ -13,7 +13,15 @@ export class GoogleSpreadsheetRow<T extends Record<string, any> = Record<string,
13
13
  /** raw underlying data for row */
14
14
  private _rawData: any[]
15
15
  ) {
16
+ this._padRawData();
17
+ }
16
18
 
19
+ /** pad _rawData with empty strings so it always matches header length */
20
+ private _padRawData() {
21
+ const headerLength = this._worksheet.headerValues.length;
22
+ while (this._rawData.length < headerLength) {
23
+ this._rawData.push('');
24
+ }
17
25
  }
18
26
 
19
27
  private _deleted = false;
@@ -29,6 +37,16 @@ export class GoogleSpreadsheetRow<T extends Record<string, any> = Record<string,
29
37
  _updateRowNumber(newRowNumber: number) {
30
38
  this._rowNumber = newRowNumber;
31
39
  }
40
+
41
+ /**
42
+ * @internal
43
+ * Used internally to mark row as deleted.
44
+ * Should not be called directly.
45
+ */
46
+ _markDeleted() {
47
+ this._deleted = true;
48
+ }
49
+
32
50
  get a1Range() {
33
51
  return [
34
52
  this._worksheet.a1SheetName,
@@ -82,7 +100,8 @@ export class GoogleSpreadsheetRow<T extends Record<string, any> = Record<string,
82
100
  },
83
101
  });
84
102
  const data = await response.json<any>();
85
- this._rawData = data.updatedData.values[0];
103
+ this._rawData = data.updatedData.values?.[0] || [];
104
+ this._padRawData();
86
105
  }
87
106
 
88
107
  /** delete this row */