google-spreadsheet 3.2.0 → 4.0.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/README.md +74 -35
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +948 -0
- package/dist/index.mjs +1 -0
- package/package.json +79 -40
- package/src/index.ts +5 -0
- package/src/lib/GoogleSpreadsheet.ts +637 -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} +289 -171
- 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 -387
- package/lib/GoogleSpreadsheetCell.js +0 -239
- package/lib/GoogleSpreadsheetRow.js +0 -72
- package/lib/errors.js +0 -10
- package/lib/utils.js +0 -32
package/.eslintrc.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// https://eslint.org/docs/user-guide/configuring
|
|
2
|
-
module.exports = {
|
|
3
|
-
root: true,
|
|
4
|
-
parserOptions: {
|
|
5
|
-
sourceType: 'module',
|
|
6
|
-
ecmaVersion: 2018,
|
|
7
|
-
},
|
|
8
|
-
env: {
|
|
9
|
-
es6: true,
|
|
10
|
-
node: true,
|
|
11
|
-
},
|
|
12
|
-
extends: [
|
|
13
|
-
'airbnb-base',
|
|
14
|
-
],
|
|
15
|
-
plugins: [
|
|
16
|
-
'async-await',
|
|
17
|
-
],
|
|
18
|
-
// add your custom rules here
|
|
19
|
-
rules: {
|
|
20
|
-
'no-underscore-dangle': 0,
|
|
21
|
-
'no-plusplus': 0, // i++ OK :D
|
|
22
|
-
'class-methods-use-this': 0,
|
|
23
|
-
'radix': 0,
|
|
24
|
-
'prefer-destructuring': 0,
|
|
25
|
-
'no-param-reassign': 0, // sometimes it's just much easier
|
|
26
|
-
'lines-between-class-members': 0, // grouping related one-liners can be nice
|
|
27
|
-
'no-continue': 0,
|
|
28
|
-
// override airbnb - breaks old version of node - https://github.com/eslint/eslint/issues/7749
|
|
29
|
-
'comma-dangle': ['error', {
|
|
30
|
-
arrays: 'always-multiline',
|
|
31
|
-
objects: 'always-multiline',
|
|
32
|
-
imports: 'always-multiline',
|
|
33
|
-
exports: 'always-multiline',
|
|
34
|
-
functions: 'never', // this breaks
|
|
35
|
-
}],
|
|
36
|
-
'no-multiple-empty-lines': 0, // sometimes helpful to break up sections of code
|
|
37
|
-
},
|
|
38
|
-
overrides: [
|
|
39
|
-
{ // extra jest related rules for tests
|
|
40
|
-
files: 'test/*',
|
|
41
|
-
plugins: ["jest"],
|
|
42
|
-
extends: ["plugin:jest/recommended"],
|
|
43
|
-
env: {
|
|
44
|
-
"jest/globals": true,
|
|
45
|
-
},
|
|
46
|
-
rules: {
|
|
47
|
-
"jest/consistent-test-it": "error",
|
|
48
|
-
'jest/expect-expect': 0, // sometimes the lack of an error thrown is a good test
|
|
49
|
-
'no-await-in-loop': 0,
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
{ // relaxed rules for examples
|
|
54
|
-
files: 'examples/*',
|
|
55
|
-
rules: {
|
|
56
|
-
'no-console': 0,
|
|
57
|
-
'no-unused-vars': 0,
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
}
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
11.15.0
|
package/Changelog.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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/TODO
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
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
|
package/UNLICENSE
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
This is free and unencumbered software released into the public domain.
|
|
2
|
-
|
|
3
|
-
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
-
distribute this software, either in source code form or as a compiled
|
|
5
|
-
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
-
means.
|
|
7
|
-
|
|
8
|
-
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
-
of this software dedicate any and all copyright interest in the
|
|
10
|
-
software to the public domain. We make this dedication for the benefit
|
|
11
|
-
of the public at large and to the detriment of our heirs and
|
|
12
|
-
successors. We intend this dedication to be an overt act of
|
|
13
|
-
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
-
software under copyright law.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
-
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
-
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
-
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
-
|
|
24
|
-
For more information, please refer to <http://unlicense.org/>
|
package/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const GoogleSpreadsheet = require('./lib/GoogleSpreadsheet');
|
|
2
|
-
const GoogleSpreadsheetWorksheet = require('./lib/GoogleSpreadsheetWorksheet');
|
|
3
|
-
const GoogleSpreadsheetRow = require('./lib/GoogleSpreadsheetRow');
|
|
4
|
-
|
|
5
|
-
const { GoogleSpreadsheetFormulaError } = require('./lib/errors');
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
GoogleSpreadsheet,
|
|
9
|
-
GoogleSpreadsheetWorksheet,
|
|
10
|
-
GoogleSpreadsheetRow,
|
|
11
|
-
|
|
12
|
-
GoogleSpreadsheetFormulaError,
|
|
13
|
-
};
|
package/lib/GoogleSpreadsheet.js
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const { JWT } = require('google-auth-library');
|
|
3
|
-
const Axios = require('axios');
|
|
4
|
-
|
|
5
|
-
const GoogleSpreadsheetWorksheet = require('./GoogleSpreadsheetWorksheet');
|
|
6
|
-
const { getFieldMask } = require('./utils');
|
|
7
|
-
|
|
8
|
-
const GOOGLE_AUTH_SCOPES = [
|
|
9
|
-
'https://www.googleapis.com/auth/spreadsheets',
|
|
10
|
-
|
|
11
|
-
// the list from the sheets v4 auth for spreadsheets.get
|
|
12
|
-
// 'https://www.googleapis.com/auth/drive',
|
|
13
|
-
// 'https://www.googleapis.com/auth/drive.readonly',
|
|
14
|
-
// 'https://www.googleapis.com/auth/drive.file',
|
|
15
|
-
// 'https://www.googleapis.com/auth/spreadsheets',
|
|
16
|
-
// 'https://www.googleapis.com/auth/spreadsheets.readonly',
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const AUTH_MODES = {
|
|
20
|
-
JWT: 'JWT',
|
|
21
|
-
API_KEY: 'API_KEY',
|
|
22
|
-
RAW_ACCESS_TOKEN: 'RAW_ACCESS_TOKEN',
|
|
23
|
-
OAUTH: 'OAUTH',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
class GoogleSpreadsheet {
|
|
27
|
-
constructor(sheetId) {
|
|
28
|
-
this.spreadsheetId = sheetId;
|
|
29
|
-
this.authMode = null;
|
|
30
|
-
this._rawSheets = {};
|
|
31
|
-
this._rawProperties = null;
|
|
32
|
-
|
|
33
|
-
// create an axios instance with sheet root URL and interceptors to handle auth
|
|
34
|
-
this.axios = Axios.create({
|
|
35
|
-
baseURL: `https://sheets.googleapis.com/v4/spreadsheets/${sheetId || ''}`,
|
|
36
|
-
// send arrays in params with duplicate keys - ie `?thing=1&thing=2` vs `?thing[]=1...`
|
|
37
|
-
// solution taken from https://github.com/axios/axios/issues/604
|
|
38
|
-
paramsSerializer(params) {
|
|
39
|
-
let options = '';
|
|
40
|
-
_.keys(params).forEach((key) => {
|
|
41
|
-
const isParamTypeObject = typeof params[key] === 'object';
|
|
42
|
-
const isParamTypeArray = isParamTypeObject && (params[key].length >= 0);
|
|
43
|
-
if (!isParamTypeObject) options += `${key}=${encodeURIComponent(params[key])}&`;
|
|
44
|
-
if (isParamTypeObject && isParamTypeArray) {
|
|
45
|
-
_.each(params[key], (val) => {
|
|
46
|
-
options += `${key}=${encodeURIComponent(val)}&`;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
return options ? options.slice(0, -1) : options;
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
// have to use bind here or the functions dont have access to `this` :(
|
|
54
|
-
this.axios.interceptors.request.use(this._setAxiosRequestAuth.bind(this));
|
|
55
|
-
this.axios.interceptors.response.use(
|
|
56
|
-
this._handleAxiosResponse.bind(this),
|
|
57
|
-
this._handleAxiosErrors.bind(this)
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
return this;
|
|
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
|
-
|
|
80
|
-
// AUTH RELATED FUNCTIONS ////////////////////////////////////////////////////////////////////////
|
|
81
|
-
async useApiKey(key) {
|
|
82
|
-
this.authMode = AUTH_MODES.API_KEY;
|
|
83
|
-
this.apiKey = key;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// token must be created and managed (refreshed) elsewhere
|
|
87
|
-
async useRawAccessToken(token) {
|
|
88
|
-
this.authMode = AUTH_MODES.RAW_ACCESS_TOKEN;
|
|
89
|
-
this.accessToken = token;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async useOAuth2Client(oAuth2Client) {
|
|
93
|
-
this.authMode = AUTH_MODES.OAUTH;
|
|
94
|
-
this.oAuth2Client = oAuth2Client;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// creds should be an object obtained by loading the json file google gives you
|
|
98
|
-
// impersonateAs is an email of any user in the G Suite domain
|
|
99
|
-
// (only works if service account has domain-wide delegation enabled)
|
|
100
|
-
async useServiceAccountAuth(creds, impersonateAs = null) {
|
|
101
|
-
this.jwtClient = new JWT({
|
|
102
|
-
email: creds.client_email,
|
|
103
|
-
key: creds.private_key,
|
|
104
|
-
scopes: GOOGLE_AUTH_SCOPES,
|
|
105
|
-
subject: impersonateAs,
|
|
106
|
-
});
|
|
107
|
-
await this.renewJwtAuth();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async renewJwtAuth() {
|
|
111
|
-
this.authMode = AUTH_MODES.JWT;
|
|
112
|
-
await this.jwtClient.authorize();
|
|
113
|
-
/*
|
|
114
|
-
returned token looks like
|
|
115
|
-
{
|
|
116
|
-
access_token: 'secret-token...',
|
|
117
|
-
token_type: 'Bearer',
|
|
118
|
-
expiry_date: 1576005020000,
|
|
119
|
-
id_token: undefined,
|
|
120
|
-
refresh_token: 'jwt-placeholder'
|
|
121
|
-
}
|
|
122
|
-
*/
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// TODO: provide mechanism to share single JWT auth between docs?
|
|
126
|
-
|
|
127
|
-
// INTERNAL UTILITY FUNCTIONS ////////////////////////////////////////////////////////////////////
|
|
128
|
-
async _setAxiosRequestAuth(config) {
|
|
129
|
-
// TODO: check auth mode, if valid, renew if expired, etc
|
|
130
|
-
if (this.authMode === AUTH_MODES.JWT) {
|
|
131
|
-
if (!this.jwtClient) throw new Error('JWT auth is not set up properly');
|
|
132
|
-
// this seems to do the right thing and only renew the token if expired
|
|
133
|
-
await this.jwtClient.authorize();
|
|
134
|
-
config.headers.Authorization = `Bearer ${this.jwtClient.credentials.access_token}`;
|
|
135
|
-
} else if (this.authMode === AUTH_MODES.RAW_ACCESS_TOKEN) {
|
|
136
|
-
if (!this.accessToken) throw new Error('Invalid access token');
|
|
137
|
-
config.headers.Authorization = `Bearer ${this.accessToken}`;
|
|
138
|
-
} else if (this.authMode === AUTH_MODES.API_KEY) {
|
|
139
|
-
if (!this.apiKey) throw new Error('Please set API key');
|
|
140
|
-
config.params = config.params || {};
|
|
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}`;
|
|
145
|
-
} else {
|
|
146
|
-
throw new Error('You must initialize some kind of auth before making any requests');
|
|
147
|
-
}
|
|
148
|
-
return config;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async _handleAxiosResponse(response) { return response; }
|
|
152
|
-
async _handleAxiosErrors(error) {
|
|
153
|
-
// console.log(error);
|
|
154
|
-
if (error.response && error.response.data) {
|
|
155
|
-
// usually the error has a code and message, but occasionally not
|
|
156
|
-
if (!error.response.data.error) throw error;
|
|
157
|
-
|
|
158
|
-
const { code, message } = error.response.data.error;
|
|
159
|
-
error.message = `Google API error - [${code}] ${message}`;
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (_.get(error, 'response.status') === 403) {
|
|
164
|
-
if (this.authMode === AUTH_MODES.API_KEY) {
|
|
165
|
-
throw new Error('Sheet is private. Use authentication or make public. (see https://github.com/theoephraim/node-google-spreadsheet#a-note-on-authentication for details)');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async _makeSingleUpdateRequest(requestType, requestParams) {
|
|
172
|
-
const response = await this.axios.post(':batchUpdate', {
|
|
173
|
-
requests: [{ [requestType]: requestParams }],
|
|
174
|
-
includeSpreadsheetInResponse: true,
|
|
175
|
-
// responseRanges: [string]
|
|
176
|
-
// responseIncludeGridData: true
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
this._updateRawProperties(response.data.updatedSpreadsheet.properties);
|
|
180
|
-
_.each(response.data.updatedSpreadsheet.sheets, (s) => this._updateOrCreateSheet(s));
|
|
181
|
-
// console.log('API RESPONSE', response.data.replies[0][requestType]);
|
|
182
|
-
return response.data.replies[0][requestType];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async _makeBatchUpdateRequest(requests, responseRanges) {
|
|
186
|
-
// this is used for updating batches of cells
|
|
187
|
-
const response = await this.axios.post(':batchUpdate', {
|
|
188
|
-
requests,
|
|
189
|
-
includeSpreadsheetInResponse: true,
|
|
190
|
-
...responseRanges && {
|
|
191
|
-
responseIncludeGridData: true,
|
|
192
|
-
...responseRanges !== '*' && { responseRanges },
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
this._updateRawProperties(response.data.updatedSpreadsheet.properties);
|
|
197
|
-
_.each(response.data.updatedSpreadsheet.sheets, (s) => this._updateOrCreateSheet(s));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
_ensureInfoLoaded() {
|
|
201
|
-
if (!this._rawProperties) throw new Error('You must call `doc.loadInfo()` before accessing this property');
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
_updateRawProperties(newProperties) { this._rawProperties = newProperties; }
|
|
205
|
-
|
|
206
|
-
_updateOrCreateSheet({ properties, data }) {
|
|
207
|
-
const { sheetId } = properties;
|
|
208
|
-
if (!this._rawSheets[sheetId]) {
|
|
209
|
-
this._rawSheets[sheetId] = new GoogleSpreadsheetWorksheet(this, { properties, data });
|
|
210
|
-
} else {
|
|
211
|
-
this._rawSheets[sheetId]._rawProperties = properties;
|
|
212
|
-
this._rawSheets[sheetId]._fillCellData(data);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// BASIC PROPS //////////////////////////////////////////////////////////////////////////////
|
|
217
|
-
_getProp(param) {
|
|
218
|
-
this._ensureInfoLoaded();
|
|
219
|
-
return this._rawProperties[param];
|
|
220
|
-
}
|
|
221
|
-
_setProp(param, newVal) { // eslint-disable-line no-unused-vars
|
|
222
|
-
throw new Error('Do not update directly - use `updateProperties()`');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
get title() { return this._getProp('title'); }
|
|
226
|
-
get locale() { return this._getProp('locale'); }
|
|
227
|
-
get timeZone() { return this._getProp('timeZone'); }
|
|
228
|
-
get autoRecalc() { return this._getProp('autoRecalc'); }
|
|
229
|
-
get defaultFormat() { return this._getProp('defaultFormat'); }
|
|
230
|
-
get spreadsheetTheme() { return this._getProp('spreadsheetTheme'); }
|
|
231
|
-
get iterativeCalculationSettings() { return this._getProp('iterativeCalculationSettings'); }
|
|
232
|
-
|
|
233
|
-
set title(newVal) { this._setProp('title', newVal); }
|
|
234
|
-
set locale(newVal) { this._setProp('locale', newVal); }
|
|
235
|
-
set timeZone(newVal) { this._setProp('timeZone', newVal); }
|
|
236
|
-
set autoRecalc(newVal) { this._setProp('autoRecalc', newVal); }
|
|
237
|
-
set defaultFormat(newVal) { this._setProp('defaultFormat', newVal); }
|
|
238
|
-
set spreadsheetTheme(newVal) { this._setProp('spreadsheetTheme', newVal); }
|
|
239
|
-
set iterativeCalculationSettings(newVal) { this._setProp('iterativeCalculationSettings', newVal); }
|
|
240
|
-
|
|
241
|
-
async updateProperties(properties) {
|
|
242
|
-
// updateSpreadsheetProperties
|
|
243
|
-
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets#SpreadsheetProperties
|
|
244
|
-
|
|
245
|
-
/*
|
|
246
|
-
title (string) - title of the spreadsheet
|
|
247
|
-
locale (string) - ISO code
|
|
248
|
-
autoRecalc (enum) - ON_CHANGE|MINUTE|HOUR
|
|
249
|
-
timeZone (string) - timezone code
|
|
250
|
-
iterativeCalculationSettings (object) - see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets#IterativeCalculationSettings
|
|
251
|
-
*/
|
|
252
|
-
|
|
253
|
-
await this._makeSingleUpdateRequest('updateSpreadsheetProperties', {
|
|
254
|
-
properties,
|
|
255
|
-
fields: getFieldMask(properties),
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// BASIC INFO ////////////////////////////////////////////////////////////////////////////////////
|
|
260
|
-
async loadInfo(includeCells) {
|
|
261
|
-
const response = await this.axios.get('/', {
|
|
262
|
-
params: {
|
|
263
|
-
...includeCells && { includeGridData: true },
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
this._rawProperties = response.data.properties;
|
|
267
|
-
_.each(response.data.sheets, (s) => this._updateOrCreateSheet(s));
|
|
268
|
-
}
|
|
269
|
-
async getInfo() { return this.loadInfo(); } // alias to mimic old version
|
|
270
|
-
|
|
271
|
-
resetLocalCache() {
|
|
272
|
-
this._rawProperties = null;
|
|
273
|
-
this._rawSheets = {};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// WORKSHEETS ////////////////////////////////////////////////////////////////////////////////////
|
|
277
|
-
get sheetCount() {
|
|
278
|
-
this._ensureInfoLoaded();
|
|
279
|
-
return _.values(this._rawSheets).length;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
get sheetsById() {
|
|
283
|
-
this._ensureInfoLoaded();
|
|
284
|
-
return this._rawSheets;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
get sheetsByIndex() {
|
|
288
|
-
this._ensureInfoLoaded();
|
|
289
|
-
return _.sortBy(this._rawSheets, 'index');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
get sheetsByTitle() {
|
|
293
|
-
this._ensureInfoLoaded();
|
|
294
|
-
return _.keyBy(this._rawSheets, 'title');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async addSheet(properties = {}) {
|
|
298
|
-
// Request type = `addSheet`
|
|
299
|
-
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest
|
|
300
|
-
|
|
301
|
-
const response = await this._makeSingleUpdateRequest('addSheet', {
|
|
302
|
-
properties: _.omit(properties, 'headers', 'headerValues', 'headerRowIndex'),
|
|
303
|
-
});
|
|
304
|
-
// _makeSingleUpdateRequest already adds the sheet
|
|
305
|
-
const newSheetId = response.properties.sheetId;
|
|
306
|
-
const newSheet = this.sheetsById[newSheetId];
|
|
307
|
-
|
|
308
|
-
// allow it to work with `.headers` but `.headerValues` is the real prop
|
|
309
|
-
const headers = properties.headerValues || properties.headers;
|
|
310
|
-
if (headers) {
|
|
311
|
-
await newSheet.setHeaderRow(headers, properties.headerRowIndex);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return newSheet;
|
|
315
|
-
}
|
|
316
|
-
async addWorksheet(properties) { return this.addSheet(properties); } // alias to mimic old version
|
|
317
|
-
|
|
318
|
-
async deleteSheet(sheetId) {
|
|
319
|
-
// Request type = `deleteSheet`
|
|
320
|
-
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteSheetRequest
|
|
321
|
-
await this._makeSingleUpdateRequest('deleteSheet', { sheetId });
|
|
322
|
-
delete this._rawSheets[sheetId];
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// NAMED RANGES //////////////////////////////////////////////////////////////////////////////////
|
|
326
|
-
async addNamedRange(name, range, namedRangeId) {
|
|
327
|
-
// namedRangeId is optional
|
|
328
|
-
return this._makeSingleUpdateRequest('addNamedRange', {
|
|
329
|
-
name,
|
|
330
|
-
range,
|
|
331
|
-
namedRangeId,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
async deleteNamedRange(namedRangeId) {
|
|
336
|
-
return this._makeSingleUpdateRequest('deleteNamedRange', { namedRangeId });
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// LOADING CELLS /////////////////////////////////////////////////////////////////////////////////
|
|
340
|
-
async loadCells(filters) {
|
|
341
|
-
// you can pass in a single filter or an array of filters
|
|
342
|
-
// strings are treated as a1 ranges
|
|
343
|
-
// objects are treated as GridRange objects
|
|
344
|
-
// TODO: make it support DeveloperMetadataLookup objects
|
|
345
|
-
|
|
346
|
-
// TODO: switch to this mode if using a read-only auth token?
|
|
347
|
-
const readOnlyMode = this.authMode === AUTH_MODES.API_KEY;
|
|
348
|
-
|
|
349
|
-
const filtersArray = _.isArray(filters) ? filters : [filters];
|
|
350
|
-
const dataFilters = _.map(filtersArray, (filter) => {
|
|
351
|
-
if (_.isString(filter)) {
|
|
352
|
-
return readOnlyMode ? filter : { a1Range: filter };
|
|
353
|
-
}
|
|
354
|
-
if (_.isObject(filter)) {
|
|
355
|
-
if (readOnlyMode) {
|
|
356
|
-
throw new Error('Only A1 ranges are supported when fetching cells with read-only access (using only an API key)');
|
|
357
|
-
}
|
|
358
|
-
// TODO: make this support Developer Metadata filters
|
|
359
|
-
return { gridRange: filter };
|
|
360
|
-
}
|
|
361
|
-
throw new Error('Each filter must be an A1 range string or a gridrange object');
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
let result;
|
|
365
|
-
// when using an API key only, we must use the regular get endpoint
|
|
366
|
-
// because :getByDataFilter requires higher access
|
|
367
|
-
if (this.authMode === AUTH_MODES.API_KEY) {
|
|
368
|
-
result = await this.axios.get('/', {
|
|
369
|
-
params: {
|
|
370
|
-
includeGridData: true,
|
|
371
|
-
ranges: dataFilters,
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
// otherwise we use the getByDataFilter endpoint because it is more flexible
|
|
375
|
-
} else {
|
|
376
|
-
result = await this.axios.post(':getByDataFilter', {
|
|
377
|
-
includeGridData: true,
|
|
378
|
-
dataFilters,
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const { sheets } = result.data;
|
|
383
|
-
_.each(sheets, (sheet) => { this._updateOrCreateSheet(sheet); });
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
module.exports = GoogleSpreadsheet;
|