google-spreadsheet 3.0.12 → 3.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/.eslintrc.js CHANGED
@@ -33,6 +33,7 @@ module.exports = {
33
33
  exports: 'always-multiline',
34
34
  functions: 'never', // this breaks
35
35
  }],
36
+ 'no-multiple-empty-lines': 0, // sometimes helpful to break up sections of code
36
37
  },
37
38
  overrides: [
38
39
  { // extra jest related rules for tests
package/Changelog.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ TODO: set this up to be automated and pulled from git commits... for now just going to leave some notes manually!
4
+
5
+ ### 3.2.0 (2021-11-07)
6
+
7
+ - Added `insertDimension` functionality
8
+ - Added custom header row index for row-based API
9
+ - Bumped dependency versions
10
+ - Readme/docs cleanup
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # google-spreadsheet
2
+ > The most popular [Google Sheets API](https://developers.google.com/sheets/api/reference/rest) wrapper for javascript
3
+
4
+ [![NPM version](https://img.shields.io/npm/v/google-spreadsheet)](https://www.npmjs.com/package/google-spreadsheet)
5
+ [![CircleCI](https://circleci.com/gh/theoephraim/node-google-spreadsheet.svg?style=shield)](https://circleci.com/gh/theoephraim/node-google-spreadsheet)
6
+ [![Known Vulnerabilities](https://snyk.io/test/github/theoephraim/node-google-spreadsheet/badge.svg?targetFile=package.json)](https://snyk.io/test/github/theoephraim/node-google-spreadsheet?targetFile=package.json)
7
+ [![NPM](https://img.shields.io/npm/dw/google-spreadsheet)](https://www.npmtrends.com/google-spreadsheet)
8
+
9
+ - multiple auth options - service account (w/ optional impersonation), OAuth 2.0, API key (read-only)
10
+ - cell-based API - read, write, bulk-updates, formatting
11
+ - row-based API - read, update, delete (based on the old v3 row-based calls)
12
+ - managing worksheets - add, remove, resize, change title, formatting
13
+
14
+ **Docs site -**
15
+ Full docs available at [https://theoephraim.github.io/node-google-spreadsheet](https://theoephraim.github.io/node-google-spreadsheet)
16
+
17
+ > **🚨 Google Deprecation Warning - affects older version (v2) of this module 🚨**
18
+ >
19
+ > Google is [phasing out their old v3 api](https://cloud.google.com/blog/products/g-suite/migrate-your-apps-use-latest-sheets-api), which the older version of this module used. Originally they were going to shut it down on March 3rd 2020, but have pushed that date back to June 2021.
20
+
21
+
22
+ **Regardless, please upgrade to the latest version of this module (v3) which uses the newer sheets v4 API**
23
+
24
+ -------------
25
+
26
+ > 🌈 **Installation** - `npm i google-spreadsheet --save` or `yarn add google-spreadsheet`
27
+
28
+ ## Examples
29
+ _the following examples are meant to give you an idea of just some of the things you can do_
30
+
31
+ > **IMPORTANT NOTE** - To keep the examples concise, I'm calling await [at the top level](https://v8.dev/features/top-level-await) which is not allowed by default in most versions of node. If you need to call await in a script at the root level, you must instead wrap it in an async function like so:
32
+
33
+ ```javascript
34
+ (async function() {
35
+ await someAsyncFunction();
36
+ }());
37
+ ```
38
+
39
+
40
+ ### The Basics
41
+ ```javascript
42
+ const { GoogleSpreadsheet } = require('google-spreadsheet');
43
+
44
+ // Initialize the sheet - doc ID is the long id in the sheets URL
45
+ const doc = new GoogleSpreadsheet('<the sheet ID from the url>');
46
+
47
+ // Initialize Auth - see https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication
48
+ await doc.useServiceAccountAuth({
49
+ // env var values are copied from service account credentials generated by google
50
+ // see "Authentication" section in docs for more info
51
+ client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
52
+ private_key: process.env.GOOGLE_PRIVATE_KEY,
53
+ });
54
+
55
+ await doc.loadInfo(); // loads document properties and worksheets
56
+ console.log(doc.title);
57
+ await doc.updateProperties({ title: 'renamed doc' });
58
+
59
+ const sheet = doc.sheetsByIndex[0]; // or use doc.sheetsById[id] or doc.sheetsByTitle[title]
60
+ console.log(sheet.title);
61
+ console.log(sheet.rowCount);
62
+
63
+ // adding / removing sheets
64
+ const newSheet = await doc.addSheet({ title: 'hot new sheet!' });
65
+ await newSheet.delete();
66
+ ```
67
+ More info:
68
+ - [GoogleSpreadsheet](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet)
69
+ - [GoogleSpreadsheetWorksheet](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet-worksheet)
70
+ - [Authentication](https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication)
71
+
72
+
73
+
74
+ ### Working with rows
75
+ ```javascript
76
+ // create a sheet and set the header row
77
+ const sheet = await doc.addSheet({ headerValues: ['name', 'email'] });
78
+
79
+ // append rows
80
+ const larryRow = await sheet.addRow({ name: 'Larry Page', email: 'larry@google.com' });
81
+ const moreRows = await sheet.addRows([
82
+ { name: 'Sergey Brin', email: 'sergey@google.com' },
83
+ { name: 'Eric Schmidt', email: 'eric@google.com' },
84
+ ]);
85
+
86
+ // read rows
87
+ const rows = await sheet.getRows(); // can pass in { limit, offset }
88
+
89
+ // read/write row values
90
+ console.log(rows[0].name); // 'Larry Page'
91
+ rows[1].email = 'sergey@abc.xyz'; // update a value
92
+ await rows[1].save(); // save updates
93
+ await rows[1].delete(); // delete a row
94
+ ```
95
+ More info:
96
+ - [GoogleSpreadsheetWorksheet > Working With Rows](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet-worksheet#working-with-rows)
97
+ - [GoogleSpreadsheetRow](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet-row)
98
+
99
+
100
+
101
+ ### Working with cells
102
+ ```javascript
103
+ await sheet.loadCells('A1:E10'); // loads a range of cells
104
+ console.log(sheet.cellStats); // total cells, loaded, how many non-empty
105
+ const a1 = sheet.getCell(0, 0); // access cells using a zero-based index
106
+ const c6 = sheet.getCellByA1('C6'); // or A1 style notation
107
+ // access everything about the cell
108
+ console.log(a1.value);
109
+ console.log(a1.formula);
110
+ console.log(a1.formattedValue);
111
+ // update the cell contents and formatting
112
+ a1.value = 123.456;
113
+ c6.formula = '=A1';
114
+ a1.textFormat = { bold: true };
115
+ c6.note = 'This is a note!';
116
+ await sheet.saveUpdatedCells(); // save all updates in one call
117
+ ```
118
+ More info:
119
+ - [GoogleSpreadsheetWorksheet > Working With Cells](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet-worksheet#working-with-cells)
120
+ - [GoogleSpreadsheetCell](https://theoephraim.github.io/node-google-spreadsheet/#/classes/google-spreadsheet-cell)
121
+
122
+
123
+
124
+ ## Why?
125
+ > **This module provides an intuitive wrapper around Google's API to simplify common interactions**
126
+
127
+ While Google's v4 sheets api is much easier to use than v3 was, the official [googleapis npm module](https://www.npmjs.com/package/googleapis) is a giant meta-tool that handles _every Google product_. The module and the API itself are awkward and the docs are pretty terrible, at least to get started.
128
+
129
+ **In what situation should you use Google's API directly?**<br>
130
+ This module makes trade-offs for simplicity of the interface.
131
+ Google's API provides a mechanism to make many requests in parallel, so if speed and efficiency is extremely important to your use case, you may want to use their API directly. There are also several features of their API that are not implemented here yet.
132
+
133
+
134
+ ## Support & Contributions
135
+
136
+ This module was written and is actively maintained by [Theo Ephraim](https://theoephraim.com).
137
+
138
+ **Are you actively using this module for a commercial project? Want to help support it?**<br>
139
+ [Buy Theo a beer](https://paypal.me/theoephraim)
140
+
141
+ #### Sponsors
142
+
143
+ None yet - get in touch!
144
+
145
+ #### Contributing
146
+
147
+ Contributions are welcome, but please follow the existing conventions, use the linter, add relevant tests, add relevant documentation.
148
+
149
+ The docs site is generated using [docsify](https://docsify.js.org). To preview and run locally so you can make edits, run `npm run docs:preview` and head to http://localhost:3000
150
+ The content lives in markdown files in the docs folder.
151
+
152
+ ## License
153
+ This is free and unencumbered public domain software. For more info, see https://unlicense.org.
package/TODO ADDED
@@ -0,0 +1,73 @@
1
+ - support more cell functionality
2
+ - data validation rules - https://github.com/theoephraim/node-google-spreadsheet/issues/487
3
+
4
+ - dont explode if sheet contains charts - https://github.com/theoephraim/node-google-spreadsheet/issues/444
5
+ - add row.toJSON()
6
+ https://github.com/theoephraim/node-google-spreadsheet/issues/247
7
+ - google auth - Application Default Creds
8
+ https://github.com/theoephraim/node-google-spreadsheet/issues/215
9
+ https://github.com/theoephraim/node-google-spreadsheet/issues/345
10
+
11
+ - escape issues for strings starting with = `'=not a formula`
12
+ https://github.com/theoephraim/node-google-spreadsheet/issues/323
13
+
14
+
15
+
16
+ - browser/react native, older ES support
17
+ https://github.com/theoephraim/node-google-spreadsheet/issues/339
18
+ https://github.com/theoephraim/node-google-spreadsheet/issues/382
19
+ https://github.com/theoephraim/node-google-spreadsheet/issues/406
20
+ https://github.com/theoephraim/node-google-spreadsheet/issues/404
21
+
22
+
23
+ - nice tips here - https://github.com/mikro-orm/mikro-orm/issues/12
24
+ - start using semantic release? or similar https://semantic-release.gitbook.io/semantic-release/
25
+ - better commit messages to autogenerate changelog
26
+ - commitizen - https://github.com/commitizen/cz-cli
27
+ - https://gist.github.com/brianclements/841ea7bffdb01346392c
28
+ - commitlint checks - https://github.com/conventional-changelog/commitlint
29
+ - https://github.com/conventional-changelog/conventional-changelog
30
+
31
+
32
+
33
+
34
+
35
+ BUGS
36
+ - fetching single row via GRO has issues
37
+ https://github.com/theoephraim/node-google-spreadsheet/issues/347
38
+
39
+
40
+ MISSING FUNCTIONS
41
+
42
+ - support batchGet
43
+ https://github.com/theoephraim/node-google-spreadsheet/issues/326
44
+
45
+ - update filters
46
+ https://github.com/theoephraim/node-google-spreadsheet/issues/328
47
+ - insertDimension
48
+ https://github.com/theoephraim/node-google-spreadsheet/issues/366
49
+ - conditional formatting
50
+ https://github.com/theoephraim/node-google-spreadsheet/issues/410
51
+
52
+
53
+ OTHER
54
+ - row to cell interface
55
+ https://github.com/theoephraim/node-google-spreadsheet/issues/343
56
+ - reading from draftValue
57
+ https://github.com/theoephraim/node-google-spreadsheet/issues/350
58
+ https://github.com/theoephraim/node-google-spreadsheet/issues/402
59
+ - heroku config docs
60
+ https://github.com/theoephraim/node-google-spreadsheet/issues/354
61
+ - document metadata from drive api
62
+ https://github.com/theoephraim/node-google-spreadsheet/issues/364
63
+ - date formatting, escaping issues
64
+ https://github.com/theoephraim/node-google-spreadsheet/issues/367
65
+ https://github.com/theoephraim/node-google-spreadsheet/issues/363
66
+ - release notes
67
+ https://github.com/theoephraim/node-google-spreadsheet/issues/384
68
+ - delete rows from google forms
69
+ https://github.com/theoephraim/node-google-spreadsheet/issues/393
70
+ - better handling of merged cells
71
+ https://github.com/theoephraim/node-google-spreadsheet/issues/400
72
+ - specify header row
73
+ https://github.com/theoephraim/node-google-spreadsheet/issues/407
@@ -20,6 +20,7 @@ const AUTH_MODES = {
20
20
  JWT: 'JWT',
21
21
  API_KEY: 'API_KEY',
22
22
  RAW_ACCESS_TOKEN: 'RAW_ACCESS_TOKEN',
23
+ OAUTH: 'OAUTH',
23
24
  };
24
25
 
25
26
  class GoogleSpreadsheet {
@@ -31,7 +32,7 @@ class GoogleSpreadsheet {
31
32
 
32
33
  // create an axios instance with sheet root URL and interceptors to handle auth
33
34
  this.axios = Axios.create({
34
- baseURL: `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}`,
35
+ baseURL: `https://sheets.googleapis.com/v4/spreadsheets/${sheetId || ''}`,
35
36
  // send arrays in params with duplicate keys - ie `?thing=1&thing=2` vs `?thing[]=1...`
36
37
  // solution taken from https://github.com/axios/axios/issues/604
37
38
  paramsSerializer(params) {
@@ -59,6 +60,23 @@ class GoogleSpreadsheet {
59
60
  return this;
60
61
  }
61
62
 
63
+ // CREATE NEW DOC ////////////////////////////////////////////////////////////////////////////////
64
+ async createNewSpreadsheetDocument(properties) {
65
+ // see updateProperties for more info about available properties
66
+
67
+ if (this.spreadsheetId) {
68
+ throw new Error('Only call `createNewSpreadsheetDocument()` on a GoogleSpreadsheet object that has no spreadsheetId set');
69
+ }
70
+ const response = await this.axios.post(this.url, {
71
+ properties,
72
+ });
73
+ this.spreadsheetId = response.data.spreadsheetId;
74
+ this.axios.defaults.baseURL += this.spreadsheetId;
75
+
76
+ this._rawProperties = response.data.properties;
77
+ _.each(response.data.sheets, (s) => this._updateOrCreateSheet(s));
78
+ }
79
+
62
80
  // AUTH RELATED FUNCTIONS ////////////////////////////////////////////////////////////////////////
63
81
  async useApiKey(key) {
64
82
  this.authMode = AUTH_MODES.API_KEY;
@@ -71,6 +89,11 @@ class GoogleSpreadsheet {
71
89
  this.accessToken = token;
72
90
  }
73
91
 
92
+ async useOAuth2Client(oAuth2Client) {
93
+ this.authMode = AUTH_MODES.OAUTH;
94
+ this.oAuth2Client = oAuth2Client;
95
+ }
96
+
74
97
  // creds should be an object obtained by loading the json file google gives you
75
98
  // impersonateAs is an email of any user in the G Suite domain
76
99
  // (only works if service account has domain-wide delegation enabled)
@@ -116,6 +139,9 @@ class GoogleSpreadsheet {
116
139
  if (!this.apiKey) throw new Error('Please set API key');
117
140
  config.params = config.params || {};
118
141
  config.params.key = this.apiKey;
142
+ } else if (this.authMode === AUTH_MODES.OAUTH) {
143
+ const credentials = await this.oAuth2Client.getAccessToken();
144
+ config.headers.Authorization = `Bearer ${credentials.token}`;
119
145
  } else {
120
146
  throw new Error('You must initialize some kind of auth before making any requests');
121
147
  }
@@ -172,7 +198,7 @@ class GoogleSpreadsheet {
172
198
  }
173
199
 
174
200
  _ensureInfoLoaded() {
175
- if (!this._rawProperties) throw new Error('You must call `sheet.loadInfo()` before accessing this property');
201
+ if (!this._rawProperties) throw new Error('You must call `doc.loadInfo()` before accessing this property');
176
202
  }
177
203
 
178
204
  _updateRawProperties(newProperties) { this._rawProperties = newProperties; }
@@ -273,15 +299,16 @@ class GoogleSpreadsheet {
273
299
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest
274
300
 
275
301
  const response = await this._makeSingleUpdateRequest('addSheet', {
276
- properties: _.omit(properties, 'headers', 'headerValues'),
302
+ properties: _.omit(properties, 'headers', 'headerValues', 'headerRowIndex'),
277
303
  });
278
304
  // _makeSingleUpdateRequest already adds the sheet
279
305
  const newSheetId = response.properties.sheetId;
280
306
  const newSheet = this.sheetsById[newSheetId];
281
307
 
282
308
  // allow it to work with `.headers` but `.headerValues` is the real prop
283
- if (properties.headerValues || properties.headers) {
284
- await newSheet.setHeaderRow(properties.headerValues || properties.headers);
309
+ const headers = properties.headerValues || properties.headers;
310
+ if (headers) {
311
+ await newSheet.setHeaderRow(headers, properties.headerRowIndex);
285
312
  }
286
313
 
287
314
  return newSheet;
@@ -202,7 +202,6 @@ class GoogleSpreadsheetCell {
202
202
  delete (format.backgroundColorStyle);
203
203
  }
204
204
 
205
-
206
205
  return {
207
206
  updateCells: {
208
207
  rows: [{
@@ -20,6 +20,8 @@ class GoogleSpreadsheetWorksheet {
20
20
  constructor(parentSpreadsheet, { properties, data }) {
21
21
  this._spreadsheet = parentSpreadsheet; // the parent GoogleSpreadsheet instance
22
22
 
23
+ this._headerRowIndex = 1; // assume "header row" (for row-based calls) is in first row
24
+
23
25
  // basic properties
24
26
  this._rawProperties = properties;
25
27
 
@@ -51,6 +53,7 @@ class GoogleSpreadsheetWorksheet {
51
53
  resetLocalCache(dataOnly) {
52
54
  if (!dataOnly) this._rawProperties = null;
53
55
  this.headerValues = null;
56
+ this._headerRowIndex = 1;
54
57
  this._cells = [];
55
58
  }
56
59
 
@@ -174,7 +177,6 @@ class GoogleSpreadsheetWorksheet {
174
177
  return this._cells[rowIndex][columnIndex];
175
178
  }
176
179
 
177
-
178
180
  async loadCells(sheetFilters) {
179
181
  // load the whole sheet
180
182
  if (!sheetFilters) return this._spreadsheet.loadCells(this.a1SheetName);
@@ -283,11 +285,11 @@ class GoogleSpreadsheetWorksheet {
283
285
  // });
284
286
  // }
285
287
 
286
-
287
288
  // ROW BASED FUNCTIONS ///////////////////////////////////////////////////////////////////////////
288
289
 
289
- async loadHeaderRow() {
290
- const rows = await this.getCellsInRange(`A1:${this.lastColumnLetter}1`);
290
+ async loadHeaderRow(headerRowIndex) {
291
+ if (headerRowIndex !== undefined) this._headerRowIndex = headerRowIndex;
292
+ const rows = await this.getCellsInRange(`A${this._headerRowIndex}:${this.lastColumnLetter}${this._headerRowIndex}`);
291
293
  if (!rows) {
292
294
  throw new Error('No values in the header row - fill the first row with header values before trying to interact with rows');
293
295
  }
@@ -298,7 +300,7 @@ class GoogleSpreadsheetWorksheet {
298
300
  checkForDuplicateHeaders(this.headerValues);
299
301
  }
300
302
 
301
- async setHeaderRow(headerValues) {
303
+ async setHeaderRow(headerValues, headerRowIndex) {
302
304
  if (!headerValues) return;
303
305
  if (headerValues.length > this.columnCount) {
304
306
  throw new Error(`Sheet is not large enough to fit ${headerValues.length} columns. Resize the sheet first.`);
@@ -310,15 +312,17 @@ class GoogleSpreadsheetWorksheet {
310
312
  throw new Error('All your header cells are blank -');
311
313
  }
312
314
 
315
+ if (headerRowIndex) this._headerRowIndex = headerRowIndex;
316
+
313
317
  const response = await this._spreadsheet.axios.request({
314
318
  method: 'put',
315
- url: `/values/${this.encodedA1SheetName}!1:1`,
319
+ url: `/values/${this.encodedA1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
316
320
  params: {
317
321
  valueInputOption: 'USER_ENTERED', // other option is RAW
318
322
  includeValuesInResponse: true,
319
323
  },
320
324
  data: {
321
- range: `${this.a1SheetName}!1:1`,
325
+ range: `${this.a1SheetName}!${this._headerRowIndex}:${this._headerRowIndex}`,
322
326
  majorDimension: 'ROWS',
323
327
  values: [[
324
328
  ...trimmedHeaderValues,
@@ -369,7 +373,7 @@ class GoogleSpreadsheetWorksheet {
369
373
 
370
374
  const response = await this._spreadsheet.axios.request({
371
375
  method: 'post',
372
- url: `/values/${this.encodedA1SheetName}!A1:append`,
376
+ url: `/values/${this.encodedA1SheetName}!A${this._headerRowIndex}:append`,
373
377
  params: {
374
378
  valueInputOption: options.raw ? 'RAW' : 'USER_ENTERED',
375
379
  insertDataOption: options.insert ? 'INSERT_ROWS' : 'OVERWRITE',
@@ -423,7 +427,7 @@ class GoogleSpreadsheetWorksheet {
423
427
 
424
428
  if (!this.headerValues) await this.loadHeaderRow();
425
429
 
426
- const firstRow = 2 + options.offset; // skip first row AND not zero indexed
430
+ const firstRow = 1 + this._headerRowIndex + options.offset;
427
431
  const lastRow = firstRow + options.limit - 1; // inclusive so we subtract 1
428
432
  const lastColumn = columnToLetter(this.headerValues.length);
429
433
  const rawRows = await this.getCellsInRange(
@@ -553,14 +557,33 @@ class GoogleSpreadsheetWorksheet {
553
557
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#CopyPasteRequest
554
558
  }
555
559
 
556
- async mergeCells() {
560
+ async mergeCells(range, mergeType = 'MERGE_ALL') {
557
561
  // Request type = `mergeCells`
558
562
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest
563
+ if (range.sheetId && range.sheetId !== this.sheetId) {
564
+ throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
565
+ }
566
+ await this._makeSingleUpdateRequest('mergeCells', {
567
+ mergeType,
568
+ range: {
569
+ ...range,
570
+ sheetId: this.sheetId,
571
+ },
572
+ });
559
573
  }
560
574
 
561
- async unmergeCells() {
575
+ async unmergeCells(range) {
562
576
  // Request type = `unmergeCells`
563
577
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UnmergeCellsRequest
578
+ if (range.sheetId && range.sheetId !== this.sheetId) {
579
+ throw new Error('Leave sheet ID blank or set to matching ID of this sheet');
580
+ }
581
+ await this._makeSingleUpdateRequest('unmergeCells', {
582
+ range: {
583
+ ...range,
584
+ sheetId: this.sheetId,
585
+ },
586
+ });
564
587
  }
565
588
 
566
589
  async updateBorders() {
@@ -613,9 +636,35 @@ class GoogleSpreadsheetWorksheet {
613
636
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#FindReplaceRequest
614
637
  }
615
638
 
616
- async insertDimension() {
639
+ async insertDimension(columnsOrRows, range, inheritFromBefore = null) {
617
640
  // Request type = `insertDimension`
618
641
  // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#InsertDimensionRequest
642
+
643
+ if (!columnsOrRows) throw new Error('You need to specify a dimension. i.e. COLUMNS|ROWS');
644
+ if (!_.isObject(range)) throw new Error('`range` must be an object containing `startIndex` and `endIndex`');
645
+ if (!_.isInteger(range.startIndex) || range.startIndex < 0) throw new Error('range.startIndex must be an integer >=0');
646
+ if (!_.isInteger(range.endIndex) || range.endIndex < 0) throw new Error('range.endIndex must be an integer >=0');
647
+ if (range.endIndex <= range.startIndex) throw new Error('range.endIndex must be greater than range.startIndex');
648
+
649
+ // default inheritFromBefore to true - unless inserting in the first row/column
650
+ if (inheritFromBefore === null) {
651
+ inheritFromBefore = range.startIndex > 0;
652
+ }
653
+
654
+ // do not allow inheritFromBefore if inserting at first row/column
655
+ if (inheritFromBefore && range.startIndex === 0) {
656
+ throw new Error('Cannot set inheritFromBefore to true if inserting in first row/column');
657
+ }
658
+
659
+ return this._makeSingleUpdateRequest('insertDimension', {
660
+ range: {
661
+ sheetId: this.sheetId,
662
+ dimension: columnsOrRows,
663
+ startIndex: range.startIndex,
664
+ endIndex: range.endIndex,
665
+ },
666
+ inheritFromBefore,
667
+ });
619
668
  }
620
669
 
621
670
  async insertRange() {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Theo Ephraim <theozero@gmail.com> (https://theoephraim.com)",
3
3
  "name": "google-spreadsheet",
4
4
  "description": "Google Sheets API (v4) -- simple interface to read/write data and manage sheets",
5
- "version": "3.0.12",
5
+ "version": "3.2.0",
6
6
  "license": "Unlicense",
7
7
  "keywords": [
8
8
  "google spreadsheets",
@@ -26,9 +26,9 @@
26
26
  "node": ">=0.8.0"
27
27
  },
28
28
  "dependencies": {
29
- "axios": "^0.19.2",
30
- "google-auth-library": "^6.0.6",
31
- "lodash": "^4.17.20"
29
+ "axios": "^0.21.4",
30
+ "google-auth-library": "^6.1.3",
31
+ "lodash": "^4.17.21"
32
32
  },
33
33
  "devDependencies": {
34
34
  "delay": "^4.3.0",
@@ -46,7 +46,8 @@
46
46
  "test": "jest --runInBand",
47
47
  "lint": "eslint ./",
48
48
  "lint:fix": "eslint ./ --fix",
49
- "docs:preview": "docsify serve docs"
49
+ "docs:preview": "docsify serve docs",
50
+ "readme:copy": "echo \"<!-- DO NOT EDIT THIS FILE, EDIT MAIN README.md AND RUN \\`npm readme:copy\\` instead -->\n\n_Welcome to the docs site for_\n\" | cat - README.md > docs/README.md"
50
51
  },
51
52
  "husky": {
52
53
  "hooks": {