From dc38223dd0a6701157ef0edecfd941808526dcc2 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Fri, 28 Feb 2020 12:41:56 +0000 Subject: [PATCH] bunch of fixes --- README.md | 2 +- docs/README.md | 2 +- docs/classes/google-spreadsheet-row.md | 2 +- docs/classes/google-spreadsheet-worksheet.md | 2 +- lib/GoogleSpreadsheet.js | 7 ++++--- lib/GoogleSpreadsheetRow.js | 2 ++ lib/GoogleSpreadsheetWorksheet.js | 22 +++++++++++++------- package.json | 2 +- test/cells.test.js | 2 +- test/manage.test.js | 4 ++-- test/rows.test.js | 9 +++++--- 11 files changed, 35 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d0d0df0..8bb3443 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ More info: ### Working with rows ```javascript // create a sheet and set the header row -const sheet = await doc.addSheet({ headers: ['name', 'email'] }); +const sheet = await doc.addSheet({ headerValues: ['name', 'email'] }); // append rows const larryRow = await sheet.addRow({ name: 'Larry Page', email: 'larry@google.com' }); diff --git a/docs/README.md b/docs/README.md index bf2cd35..4534bdb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -61,7 +61,7 @@ More info: ### Working with rows ```javascript // create a sheet and set the header row -const sheet = await doc.addSheet({ headers: ['name', 'email'] }); +const sheet = await doc.addSheet({ headerValues: ['name', 'email'] }); // append rows const larryRow = await sheet.addRow({ name: 'Larry Page', email: 'larry@google.com' }); diff --git a/docs/classes/google-spreadsheet-row.md b/docs/classes/google-spreadsheet-row.md index fb37029..c4564cc 100644 --- a/docs/classes/google-spreadsheet-row.md +++ b/docs/classes/google-spreadsheet-row.md @@ -43,7 +43,7 @@ Google uses both row/column indices and A1-style notation, available as **read-o Property|Type|Description ---|---|--- -`rowIndex`|Number
_int >= 0_|Row number in the sheet of this row +`rowNumber`|Number
_int >= 1_|A1 row number in the sheet of this row `a1Range`|String|Full A1 range of this row, including the sheet name
_Ex: "sheet1!A5:D5"_ ### Row Values diff --git a/docs/classes/google-spreadsheet-worksheet.md b/docs/classes/google-spreadsheet-worksheet.md index 7f38cbc..ea61451 100644 --- a/docs/classes/google-spreadsheet-worksheet.md +++ b/docs/classes/google-spreadsheet-worksheet.md @@ -148,7 +148,7 @@ Param|Type|Required|Description #### `getCell(rowIndex, columnIndex)` :id=fn-getCell -> retrieve a cell from the cache based on A1 address +> retrieve a cell from the cache based on zero-indexed row/column Param|Type|Required|Description ---|---|---|--- diff --git a/lib/GoogleSpreadsheet.js b/lib/GoogleSpreadsheet.js index 9d79a36..b7a1cf1 100644 --- a/lib/GoogleSpreadsheet.js +++ b/lib/GoogleSpreadsheet.js @@ -249,15 +249,16 @@ class GoogleSpreadsheet { // https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddSheetRequest const response = await this._makeSingleUpdateRequest('addSheet', { - properties: _.omit(properties, 'headers'), + properties: _.omit(properties, 'headers', 'headerValues'), }); // _makeSingleUpdateRequest already adds the sheet const newSheetId = response.properties.sheetId; const newSheet = this.sheetsById[newSheetId]; // allow it to work with `.headers` but `.headerValues` is the real prop - if (properties.headers) { await newSheet.setHeaderRow(properties.headers); } - if (properties.headerValues) { await newSheet.setHeaderRow(properties.headerValues); } + if (properties.headerValues || properties.headers) { + await newSheet.setHeaderRow(properties.headerValues || properties.headers); + } return newSheet; } diff --git a/lib/GoogleSpreadsheetRow.js b/lib/GoogleSpreadsheetRow.js index fdb3ae1..a1ff760 100644 --- a/lib/GoogleSpreadsheetRow.js +++ b/lib/GoogleSpreadsheetRow.js @@ -19,6 +19,8 @@ class GoogleSpreadsheetRow { return this; } + get rowNumber() { return this._rowNumber; } + // TODO: deprecate rowIndex - the name implies it should be zero indexed :( get rowIndex() { return this._rowNumber; } get a1Range() { return [ diff --git a/lib/GoogleSpreadsheetWorksheet.js b/lib/GoogleSpreadsheetWorksheet.js index 195374c..ef39692 100644 --- a/lib/GoogleSpreadsheetWorksheet.js +++ b/lib/GoogleSpreadsheetWorksheet.js @@ -138,7 +138,8 @@ class GoogleSpreadsheetWorksheet { set rowCount(newVal) { throw new Error('Do not update directly. Use resize()'); } set columnCount(newVal) { throw new Error('Do not update directly. Use resize()'); } - get a1SheetName() { return `'${this.title}'`; } + get a1SheetName() { return `'${this.title.replace(/'/g, "''")}'`; } + get encodedA1SheetName() { return encodeURIComponent(this.a1SheetName); } get lastColumnLetter() { return columnToLetter(this.columnCount); } @@ -311,13 +312,13 @@ class GoogleSpreadsheetWorksheet { const response = await this._spreadsheet.axios.request({ method: 'put', - url: `/values/${encodeURIComponent(this.a1SheetName)}!A1`, + url: `/values/${this.encodedA1SheetName}!1:1`, params: { valueInputOption: 'USER_ENTERED', // other option is RAW includeValuesInResponse: true, }, data: { - range: `${this.a1SheetName}!A1`, + range: `${this.a1SheetName}!1:1`, majorDimension: 'ROWS', values: [[ ...trimmedHeaderValues, @@ -338,6 +339,12 @@ class GoogleSpreadsheetWorksheet { // an object must use the header row values as keys // ex: { col1: 'column 1', col2: 'column 2', col3: 'column 3' } + // google bug that does not handle colons in names + // see https://issuetracker.google.com/issues/150373119 + if (this.title.includes(':')) { + 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.'); + } + if (!_.isArray(rows)) throw new Error('You must pass in an array of row values to append'); if (!this.headerValues) await this.loadHeaderRow(); @@ -362,7 +369,7 @@ class GoogleSpreadsheetWorksheet { const response = await this._spreadsheet.axios.request({ method: 'post', - url: `/values/${encodeURIComponent(this.a1SheetName)}:append`, + url: `/values/${this.encodedA1SheetName}:append`, params: { valueInputOption: options.raw ? 'RAW' : 'USER_ENTERED', insertDataOption: options.insert ? 'INSERT_ROWS' : 'OVERWRITE', @@ -383,7 +390,8 @@ class GoogleSpreadsheetWorksheet { if (options.insert) { this._rawProperties.gridProperties.rowCount += rows.length; } else if (rowNumber + rows.length > this.rowCount) { - this._rawProperties.gridProperties.rowCount = rowNumber + rows.length; + // have to subtract 1 since one row was inserted at rowNumber + this._rawProperties.gridProperties.rowCount = rowNumber + rows.length - 1; } return _.map(response.data.updates.updatedData.values, (rowValues) => { @@ -504,7 +512,7 @@ class GoogleSpreadsheetWorksheet { // this uses the "values" getter and does not give all the info about the cell contents // it is used internally when loading header cells async getCellsInRange(a1Range, options) { - const response = await this._spreadsheet.axios.get(`/values/${encodeURIComponent(this.a1SheetName)}!${a1Range}`, { + const response = await this._spreadsheet.axios.get(`/values/${this.encodedA1SheetName}!${a1Range}`, { params: options, }); return response.data.values; @@ -796,7 +804,7 @@ class GoogleSpreadsheetWorksheet { async clear() { // clears all the data in the sheet // sheet name without ie 'sheet1' rather than 'sheet1'!A1:B5 is all cells - await this._spreadsheet.axios.post(`/values/${encodeURIComponent(this.a1SheetName)}:clear`); + await this._spreadsheet.axios.post(`/values/${this.encodedA1SheetName}:clear`); this.resetLocalCache(true); } } diff --git a/package.json b/package.json index 1165aec..a5c06dc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Theo Ephraim (https://theoephraim.com)", "name": "google-spreadsheet", "description": "Google Sheets API (v4) -- simple interface to read/write data and manage sheets", - "version": "3.0.8", + "version": "3.0.9", "license": "Unlicense", "keywords": [ "google spreadsheets", diff --git a/test/cells.test.js b/test/cells.test.js index 2f951e0..7b27108 100644 --- a/test/cells.test.js +++ b/test/cells.test.js @@ -21,7 +21,7 @@ describe('Cell-based operations', () => { rowCount: NUM_ROWS, columnCount: NUM_COLS, }, - headers: ['col1', 'col2', 'col3'], + headerValues: ['col1', 'col2', 'col3'], }); }); afterAll(async () => { diff --git a/test/manage.test.js b/test/manage.test.js index 8178360..068c959 100644 --- a/test/manage.test.js +++ b/test/manage.test.js @@ -70,7 +70,7 @@ describe('Managing doc info and sheets', () => { rowCount: 7, columnCount: 11, }, - headers: ['col1', 'col2', 'col3', 'col4', 'col5'], + headerValues: ['col1', 'col2', 'col3', 'col4', 'col5'], }); expect(doc.sheetCount).toBe(numSheets + 1); @@ -174,7 +174,7 @@ describe('Managing doc info and sheets', () => { beforeAll(async () => { sheet = await doc.addSheet({ title: `Sheet to copy ${+new Date()}`, - headers: ['copy', 'this', 'sheet'], + headerValues: ['copy', 'this', 'sheet'], }); }); afterAll(async () => { diff --git a/test/rows.test.js b/test/rows.test.js index e77bf93..5fb44b8 100644 --- a/test/rows.test.js +++ b/test/rows.test.js @@ -23,8 +23,8 @@ describe('Row-based operations', () => { beforeAll(async () => { await doc.useServiceAccountAuth(creds); sheet = await doc.addSheet({ - headers: HEADERS, - title: `Spécial CнArs - ${+new Date()}`, // some urls have sheet title in them + headerValues: HEADERS, + title: `Spécial CнArs ${+new Date()}`, // some urls have sheet title in them gridProperties: { rowCount: INITIAL_ROW_COUNT }, }); await sheet.addRows(INITIAL_DATA); @@ -118,8 +118,11 @@ describe('Row-based operations', () => { numbers: '999', letters: 'ZZZ', })); const newRows = await sheet.addRows(dataForMoreRowsThanFit); + const updatedRowCount = sheet.rowCount; + await doc.loadInfo(); // actually reload to make sure the logic is correct + expect(sheet.rowCount).toEqual(updatedRowCount); expect(sheet.rowCount).toBeGreaterThan(oldRowCount); - expect(newRows[newRows.length - 1].rowIndex).toEqual(sheet.rowCount - 1); + expect(newRows[newRows.length - 1].rowNumber).toEqual(sheet.rowCount); }); it('can add rows with options.raw', async () => {