From 770c4fe6ebe70c7a507ee5e57348508b98fda11d Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Fri, 29 Apr 2022 10:06:24 +0200 Subject: [PATCH] feat(Google Sheets Node): Allow to use header names as JSON path (#3165) * Add option to match key by keypath when append data to google sheet * :zap: Fix build issue and option order Co-authored-by: Jack Rudenko Co-authored-by: Jan Oberhauser --- .../nodes/Google/Sheet/GoogleSheet.ts | 19 +++++++++++-------- .../nodes/Google/Sheet/GoogleSheets.node.ts | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts index d7ff16ef3f..1d4d29211f 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheet.ts @@ -15,6 +15,8 @@ import { utils as xlsxUtils, } from 'xlsx'; +import { get } from 'lodash'; + export interface ISheetOptions { scope: string[]; } @@ -245,8 +247,8 @@ export class GoogleSheet { } - async appendSheetData(inputData: IDataObject[], range: string, keyRowIndex: number, valueInputMode: ValueInputOption): Promise { - const data = await this.convertStructuredDataToArray(inputData, range, keyRowIndex); + async appendSheetData(inputData: IDataObject[], range: string, keyRowIndex: number, valueInputMode: ValueInputOption, usePathForKeyRow: boolean): Promise { + const data = await this.convertStructuredDataToArray(inputData, range, keyRowIndex, usePathForKeyRow); return this.appendData(range, data, valueInputMode); } @@ -344,7 +346,7 @@ export class GoogleSheet { if (itemKey === undefined || itemKey === null) { // Item does not have the indexKey so we can ignore it or append it if upsert true if (upsert) { - const data = await this.appendSheetData([inputItem], this.encodeRange(range), keyRowIndex, valueInputMode); + const data = await this.appendSheetData([inputItem], this.encodeRange(range), keyRowIndex, valueInputMode, false); } continue; } @@ -354,7 +356,7 @@ export class GoogleSheet { if (itemKeyIndex === -1) { // Key does not exist in the Sheet so it can not be updated so skip it or append it if upsert true if (upsert) { - const data = await this.appendSheetData([inputItem], this.encodeRange(range), keyRowIndex, valueInputMode); + const data = await this.appendSheetData([inputItem], this.encodeRange(range), keyRowIndex, valueInputMode, false); } continue; } @@ -472,7 +474,7 @@ export class GoogleSheet { } - async convertStructuredDataToArray(inputData: IDataObject[], range: string, keyRowIndex: number): Promise { + async convertStructuredDataToArray(inputData: IDataObject[], range: string, keyRowIndex: number, usePathForKeyRow: boolean): Promise { let startColumn, endColumn; let sheet: string | undefined = undefined; if (range.includes('!')) { @@ -501,9 +503,10 @@ export class GoogleSheet { inputData.forEach((item) => { rowData = []; keyColumnOrder.forEach((key) => { - const data = item[key]; - if (item.hasOwnProperty(key) && data !== null && typeof data !== 'undefined') { - rowData.push(data.toString()); + if (usePathForKeyRow && (get(item, key) !== undefined)) { //match by key path + rowData.push(get(item, key)!.toString()); + } else if (!usePathForKeyRow && item.hasOwnProperty(key) && item[key] !== null && item[key] !== undefined) { //match by exact key name + rowData.push(item[key]!.toString()); } else { rowData.push(''); } diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index c82735f394..4a6b5e4fc7 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -579,6 +579,20 @@ export class GoogleSheets implements INodeType { }, description: 'By default only the first result gets returned. If options gets set all found matches get returned.', }, + { + displayName: 'Use Header Names as JSON Paths', + name: 'usePathForKeyRow', + type: 'boolean', + default: false, + displayOptions: { + show: { + '/operation': [ + 'append', + ], + }, + }, + description: 'Enable if you want to match the headers as path, for example, the row header "category.name" will match the "category" object and get the field "name" from it. By default "category.name" will match with the field with exact name, not nested object.', + }, { displayName: 'Value Input Mode', name: 'valueInputMode', @@ -674,7 +688,6 @@ export class GoogleSheets implements INodeType { default: 'UNFORMATTED_VALUE', description: 'Determines how values should be rendered in the output.', }, - ], }, @@ -1089,8 +1102,10 @@ export class GoogleSheets implements INodeType { setData.push(item.json); }); + const usePathForKeyRow = (options.usePathForKeyRow || false) as boolean; + // Convert data into array format - const data = await sheet.appendSheetData(setData, sheet.encodeRange(range), keyRow, valueInputMode); + const data = await sheet.appendSheetData(setData, sheet.encodeRange(range), keyRow, valueInputMode, usePathForKeyRow); // TODO: Should add this data somewhere // TODO: Should have something like add metadata which does not get passed through