From 31ddbf7baa9635b3d79f835ef78c64d132eedbe8 Mon Sep 17 00:00:00 2001 From: jroehl Date: Thu, 25 May 2023 07:40:03 +0200 Subject: [PATCH] feat: add rename worksheet capability --- docs/worksheet.md | 32 ++++++++++++++++++++++++ src/commands/worksheet/rename.ts | 34 ++++++++++++++++++++++++++ src/lib/google-sheet.ts | 30 +++++++++++++++++++++++ test/commands/data/append.test.ts | 6 ++--- test/commands/data/get.test.ts | 14 +++++------ test/commands/data/update.test.ts | 4 +-- test/commands/helper.ts | 29 +++++++++++++++++----- test/commands/spreadsheet/get.test.ts | 6 ++--- test/commands/worksheet/add.test.ts | 4 +-- test/commands/worksheet/get.test.ts | 6 ++--- test/commands/worksheet/remove.test.ts | 4 +-- test/commands/worksheet/rename.test.ts | 11 +++++++++ 12 files changed, 152 insertions(+), 28 deletions(-) create mode 100644 src/commands/worksheet/rename.ts create mode 100644 test/commands/worksheet/rename.test.ts diff --git a/docs/worksheet.md b/docs/worksheet.md index f16b5f4..9744843 100644 --- a/docs/worksheet.md +++ b/docs/worksheet.md @@ -6,6 +6,7 @@ Manage worksheets * [`google-sheet worksheet:add`](#google-sheet-worksheetadd) * [`google-sheet worksheet:get`](#google-sheet-worksheetget) * [`google-sheet worksheet:remove`](#google-sheet-worksheetremove) +* [`google-sheet worksheet:rename`](#google-sheet-worksheetrename) ## `google-sheet worksheet:add` @@ -96,3 +97,34 @@ EXAMPLES ``` _See code: [src/commands/worksheet/remove.ts](https://github.com/jroehl/google-sheet-cli/blob/master/src/commands/worksheet/remove.ts)_ + +## `google-sheet worksheet:rename` + +Add a worksheet with the specified title to the spreadsheet + +``` +USAGE + $ google-sheet worksheet:rename -t --newWorksheetTitle -s [-h] [-r] [-c ] [-p ] + +FLAGS + -h, --help Show CLI help. + -r, --rawOutput Get the raw output as a JSON string + -s, --spreadsheetId= (required) ID of the spreadsheet to use + -t, --worksheetTitle= (required) Title of the worksheet to use + --newWorksheetTitle= (required) New title of the worksheet to use + +AUTHENTICATION FLAGS + -c, --clientEmail= The client email to use for authentication. Uses the GSHEET_CLIENT_EMAIL env variable if + not provided. + -p, --privateKey= The private key to use for authentication. Uses the GSHEET_PRIVATE_KEY env variable if not + provided. + +DESCRIPTION + Add a worksheet with the specified title to the spreadsheet + +EXAMPLES + $ gsheet worksheet:add --spreadsheetId= --worksheetTitle= + Worksheet "" () successfully created +``` + +_See code: [src/commands/worksheet/rename.ts](https://github.com/jroehl/google-sheet-cli/blob/master/src/commands/worksheet/rename.ts)_ diff --git a/src/commands/worksheet/rename.ts b/src/commands/worksheet/rename.ts new file mode 100644 index 0000000..fce1092 --- /dev/null +++ b/src/commands/worksheet/rename.ts @@ -0,0 +1,34 @@ +import { Flags } from '@oclif/core'; +import Command, { spreadsheetId, worksheetTitle } from '../../lib/base-class'; + +export default class Add extends Command { + static description = 'Add a worksheet with the specified title to the spreadsheet'; + + static examples = [ + `$ gsheet worksheet:add --spreadsheetId= --worksheetTitle= + +Worksheet "" () successfully created +`, + ]; + + static flags = { + ...Command.flags, + worksheetTitle, + newWorksheetTitle: Flags.string({ + description: 'New title of the worksheet to use', + required: true, + }), + spreadsheetId, + }; + + async run() { + const { + flags: { worksheetTitle = '', spreadsheetId, newWorksheetTitle }, + } = await this.parse(Add); + + this.start('Renaming worksheet'); + await this.gsheet.renameWorksheet(worksheetTitle, newWorksheetTitle, spreadsheetId); + this.stop(); + this.logRaw(`Worksheet "${worksheetTitle}" successfully renamed to "${newWorksheetTitle}"`, { operation: this.id }); + } +} diff --git a/src/lib/google-sheet.ts b/src/lib/google-sheet.ts index 3337144..b876616 100644 --- a/src/lib/google-sheet.ts +++ b/src/lib/google-sheet.ts @@ -299,6 +299,36 @@ export default class GoogleSheet { this.worksheetTitle = ''; } + /** + * Rename a worksheet by title + * + * @param {string} title + * @param {string} newTitle + * @param {string} [spreadsheetId] + * @returns {Promise} + * @memberof GoogleSheet + */ + async renameWorksheet(title: string, newTitle: string, spreadsheetId?: string): Promise { + const worksheet = await this.getWorksheet(title, spreadsheetId); + await this.sheets.spreadsheets.batchUpdate({ + spreadsheetId: spreadsheetId || this.spreadsheetId, + requestBody: { + requests: [ + { + updateSheetProperties: { + properties: { + sheetId: worksheet.properties?.sheetId || -1, + title: newTitle, + }, + fields: 'title', + }, + }, + ], + }, + }); + this.worksheetTitle = newTitle; + } + /** * Add a spreadsheet with title * diff --git a/test/commands/data/append.test.ts b/test/commands/data/append.test.ts index 0610278..8e7a1ae 100644 --- a/test/commands/data/append.test.ts +++ b/test/commands/data/append.test.ts @@ -10,17 +10,17 @@ const DATA = [ const DATA_STRING = JSON.stringify(DATA); describe(baseCommand, () => { - testRun([baseCommand, DATA_STRING], worksheetTitle, (stdout: string) => { + testRun([baseCommand, DATA_STRING], { worksheetTitle }, (stdout: string) => { expect(stdout).to.contain(`Data successfully appended to "${worksheetTitle}"`); }); - testRun([baseCommand, DATA_STRING, '--rawOutput'], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, DATA_STRING, '--rawOutput'], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:append', worksheetTitle, data: DATA, }); }); - testRun(['data:get', '--rawOutput'], worksheetTitle, (parsed: {}) => { + testRun(['data:get', '--rawOutput'], { worksheetTitle }, (parsed: {}) => { expect((parsed as any).rawData).to.eql([...DATA, ...DATA]); }); }); diff --git a/test/commands/data/get.test.ts b/test/commands/data/get.test.ts index 572e45e..415fc9a 100644 --- a/test/commands/data/get.test.ts +++ b/test/commands/data/get.test.ts @@ -1,10 +1,10 @@ import { expect } from '@oclif/test'; -import { testRun, DATA_GET as worksheetTitle, RAW_DATA } from '../helper'; +import { RAW_DATA, testRun, DATA_GET as worksheetTitle } from '../helper'; const baseCommand = 'data:get'; describe(baseCommand, () => { - testRun([baseCommand], worksheetTitle, (stdout: string) => { + testRun([baseCommand], { worksheetTitle }, (stdout: string) => { expect(stdout).to.contain('(a)'); expect(stdout).to.contain('(b)'); expect(stdout).to.contain('(c)'); @@ -13,7 +13,7 @@ describe(baseCommand, () => { expect(stdout).to.contain('C3'); }); - testRun([baseCommand, '--rawOutput'], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, '--rawOutput'], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:get', rawData: RAW_DATA, @@ -59,7 +59,7 @@ describe(baseCommand, () => { }); }); - testRun([baseCommand, '--rawOutput', '--hasHeaderRow'], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, '--rawOutput', '--hasHeaderRow'], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:get', rawData: [ @@ -107,7 +107,7 @@ describe(baseCommand, () => { }); }); - testRun([baseCommand, '--rawOutput', '--hasHeaderRow', '--minCol=2', '--minRow=2', '--maxCol=2', '--maxRow=2'], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, '--rawOutput', '--hasHeaderRow', '--minCol=2', '--minRow=2', '--maxCol=2', '--maxRow=2'], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:get', rawData: [['B2']], @@ -121,7 +121,7 @@ describe(baseCommand, () => { }); }); - testRun([baseCommand, '--rawOutput', '--hasHeaderRow', `--range='${worksheetTitle}'!C3`], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, '--rawOutput', '--hasHeaderRow', `--range='${worksheetTitle}'!C3`], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:get', rawData: [['C3']], @@ -135,7 +135,7 @@ describe(baseCommand, () => { }); }); - testRun([baseCommand, '--rawOutput', '--hasHeaderRow', `--range='${worksheetTitle}'!A2`], worksheetTitle, (parsed: {}) => { + testRun([baseCommand, '--rawOutput', '--hasHeaderRow', `--range='${worksheetTitle}'!A2`], { worksheetTitle }, (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:get', rawData: [], diff --git a/test/commands/data/update.test.ts b/test/commands/data/update.test.ts index 1e97b2b..73ec6af 100644 --- a/test/commands/data/update.test.ts +++ b/test/commands/data/update.test.ts @@ -7,10 +7,10 @@ const DATA = [['1', '2'], ['foo']]; const DATA_STRING = JSON.stringify(DATA); describe(baseCommand, () => { - testRun([baseCommand, DATA_STRING], worksheetTitle, async (stdout: string) => { + testRun([baseCommand, DATA_STRING], { worksheetTitle }, async (stdout: string) => { expect(stdout).to.contain(`Data successfully updated in "${worksheetTitle}"`); }); - testRun([baseCommand, DATA_STRING, '--rawOutput'], worksheetTitle, async (parsed: {}) => { + testRun([baseCommand, DATA_STRING, '--rawOutput'], { worksheetTitle }, async (parsed: {}) => { expect(parsed).to.eql({ operation: 'data:update', worksheetTitle, diff --git a/test/commands/helper.ts b/test/commands/helper.ts index 17ef845..d281959 100644 --- a/test/commands/helper.ts +++ b/test/commands/helper.ts @@ -14,6 +14,8 @@ export const DATA_GET = getID('data_get_'); export const WORKSHEET_GET = getID('worksheet_get_'); export const WORKSHEET_ADD = getID('worksheet_add_'); export const WORKSHEET_REMOVE = getID('worksheet_remove_'); +export const WORKSHEET_RENAME = getID('worksheet_rename_'); +export const WORKSHEET_RENAMED = getID('worksheet_rename_'); const authorize = async (): Promise => { const gsheet = new GoogleSheet(); @@ -21,8 +23,8 @@ const authorize = async (): Promise => { return gsheet; }; -const worksheetsToAdd = [DATA_APPEND, DATA_UPDATE, WORKSHEET_GET, WORKSHEET_REMOVE]; -const worksheetsToRemove = [DATA_APPEND, DATA_UPDATE, WORKSHEET_GET, WORKSHEET_ADD, DATA_GET]; +const worksheetsToAdd = [DATA_APPEND, DATA_UPDATE, WORKSHEET_GET, WORKSHEET_REMOVE, WORKSHEET_RENAME]; +const worksheetsToRemove = [DATA_APPEND, DATA_UPDATE, WORKSHEET_GET, WORKSHEET_ADD, WORKSHEET_RENAMED, DATA_GET]; export const RAW_DATA = [ ['A1', 'B1', 'C1'], @@ -74,17 +76,32 @@ export const addTestData = async () => { console.log(''); }; -export const getCmd = (parts: string[], worksheetTitle?: string): string[] => { +interface Args { + [key: string]: string | undefined; + spreadsheetId?: string; + worksheetTitle?: string; +} + +export const getCmd = (parts: string[], args: Args = {}): string[] => { const [base, ...flags] = parts; - return [base, `--spreadsheetId=${SPREADSHEET_ID}`, worksheetTitle ? `--worksheetTitle=${worksheetTitle}` : '', ...flags].filter(Boolean); + if (!args.spreadsheetId) { + args.spreadsheetId = SPREADSHEET_ID; + } + return [ + base, + ...Object.entries(args || {}).reduce((acc, [key, value]) => { + return value === undefined ? acc : [...acc, `--${key}=${value}`]; + }, []), + ...flags, + ].filter(Boolean); }; export const getRun = (parts: string[]): string => { return `runs "${parts.join(' ')}"`; }; -export const testRun = (cmd: string[], worksheetTitle?: string, cb: Function = () => {}) => { - const parsedCommand = getCmd(cmd, worksheetTitle); +export const testRun = (cmd: string[], args?: Args, cb: Function = () => {}) => { + const parsedCommand = getCmd(cmd, args); const commandString = `runs "${parsedCommand.join(' ')}"`; test .stdout() diff --git a/test/commands/spreadsheet/get.test.ts b/test/commands/spreadsheet/get.test.ts index 449559b..9c7eb80 100644 --- a/test/commands/spreadsheet/get.test.ts +++ b/test/commands/spreadsheet/get.test.ts @@ -1,14 +1,14 @@ import { expect } from '@oclif/test'; -import { testRun, SPREADSHEET_ID } from '../helper'; import { sheets_v4 } from 'googleapis'; +import { SPREADSHEET_ID, testRun } from '../helper'; const baseCommand = 'spreadsheet:get'; describe(baseCommand, () => { - testRun([baseCommand], '', (stdout: string) => { + testRun([baseCommand], undefined, (stdout: string) => { expect(stdout).to.contain(`Fetched spreadsheet "github-actions-test" (${SPREADSHEET_ID})`); }); - testRun([baseCommand, '--rawOutput'], '', (parsed: sheets_v4.Schema$Spreadsheet) => { + testRun([baseCommand, '--rawOutput'], undefined, (parsed: sheets_v4.Schema$Spreadsheet) => { expect(parsed.spreadsheetId).to.equal(SPREADSHEET_ID); expect(parsed.spreadsheetUrl).to.equal(`https://docs.google.com/spreadsheets/d/${SPREADSHEET_ID}/edit`); expect(parsed).to.haveOwnProperty('properties'); diff --git a/test/commands/worksheet/add.test.ts b/test/commands/worksheet/add.test.ts index ef6a950..b4ab97a 100644 --- a/test/commands/worksheet/add.test.ts +++ b/test/commands/worksheet/add.test.ts @@ -1,11 +1,11 @@ import { expect } from '@oclif/test'; -import { testRun, WORKSHEET_ADD as worksheetTitle } from '../helper'; import { sheets_v4 } from 'googleapis'; +import { testRun, WORKSHEET_ADD as worksheetTitle } from '../helper'; const baseCommand = 'worksheet:add'; describe(baseCommand, () => { - testRun([baseCommand, '--rawOutput'], worksheetTitle, (parsed: sheets_v4.Schema$Sheet) => { + testRun([baseCommand, '--rawOutput'], { worksheetTitle }, (parsed: sheets_v4.Schema$Sheet) => { if (!parsed.properties) throw parsed; expect(parsed.properties.title).to.equal(worksheetTitle); expect(parsed.properties).to.haveOwnProperty('sheetId'); diff --git a/test/commands/worksheet/get.test.ts b/test/commands/worksheet/get.test.ts index 546ffeb..0e59101 100644 --- a/test/commands/worksheet/get.test.ts +++ b/test/commands/worksheet/get.test.ts @@ -1,14 +1,14 @@ import { expect } from '@oclif/test'; -import { testRun, WORKSHEET_GET as worksheetTitle } from '../helper'; import { sheets_v4 } from 'googleapis'; +import { testRun, WORKSHEET_GET as worksheetTitle } from '../helper'; const baseCommand = 'worksheet:get'; describe(baseCommand, () => { - testRun([baseCommand], worksheetTitle, (stdout: string) => { + testRun([baseCommand], { worksheetTitle }, (stdout: string) => { expect(stdout).to.contain(`Fetched "${worksheetTitle}" (`); }); - testRun([baseCommand, '--rawOutput'], worksheetTitle, (parsed: sheets_v4.Schema$Sheet) => { + testRun([baseCommand, '--rawOutput'], { worksheetTitle }, (parsed: sheets_v4.Schema$Sheet) => { if (!parsed.properties) throw parsed; expect(parsed.properties.sheetId).to.not.be.undefined; expect(parsed.properties.title).to.equal(worksheetTitle); diff --git a/test/commands/worksheet/remove.test.ts b/test/commands/worksheet/remove.test.ts index 8f07973..b0e41e3 100644 --- a/test/commands/worksheet/remove.test.ts +++ b/test/commands/worksheet/remove.test.ts @@ -1,11 +1,11 @@ import { expect } from '@oclif/test'; -import { testRun, WORKSHEET_REMOVE as worksheetTitle } from '../helper'; import { sheets_v4 } from 'googleapis'; +import { testRun, WORKSHEET_REMOVE as worksheetTitle } from '../helper'; const baseCommand = 'worksheet:remove'; describe(baseCommand, () => { - testRun([baseCommand], worksheetTitle, (stdout: sheets_v4.Schema$Sheet) => { + testRun([baseCommand], { worksheetTitle }, (stdout: sheets_v4.Schema$Sheet) => { expect(stdout).to.contain(`Worksheet "${worksheetTitle}" successfully removed`); }); }); diff --git a/test/commands/worksheet/rename.test.ts b/test/commands/worksheet/rename.test.ts new file mode 100644 index 0000000..0bb96ea --- /dev/null +++ b/test/commands/worksheet/rename.test.ts @@ -0,0 +1,11 @@ +import { expect } from '@oclif/test'; +import { sheets_v4 } from 'googleapis'; +import { WORKSHEET_RENAMED as newWorksheetTitle, testRun, WORKSHEET_RENAME as worksheetTitle } from '../helper'; + +const baseCommand = 'worksheet:rename'; + +describe(baseCommand, () => { + testRun([baseCommand], { worksheetTitle, newWorksheetTitle }, (stdout: sheets_v4.Schema$Sheet) => { + expect(stdout).to.contain(`Worksheet "${worksheetTitle}" successfully renamed to "${newWorksheetTitle}"`); + }); +});