mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-02 07:01:30 -08:00
feat(Google Sheets Node): Add "By Name" option to selector for Sheets (#8241)
Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
parent
e796e7f06d
commit
dce28f9cb9
|
@ -12,7 +12,7 @@ import { apiRequest } from './v2/transport';
|
||||||
import { sheetsSearch, spreadSheetsSearch } from './v2/methods/listSearch';
|
import { sheetsSearch, spreadSheetsSearch } from './v2/methods/listSearch';
|
||||||
import { GoogleSheet } from './v2/helpers/GoogleSheet';
|
import { GoogleSheet } from './v2/helpers/GoogleSheet';
|
||||||
import { getSheetHeaderRowAndSkipEmpty } from './v2/methods/loadOptions';
|
import { getSheetHeaderRowAndSkipEmpty } from './v2/methods/loadOptions';
|
||||||
import type { ValueRenderOption } from './v2/helpers/GoogleSheets.types';
|
import type { ResourceLocator, ValueRenderOption } from './v2/helpers/GoogleSheets.types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
arrayOfArraysToJson,
|
arrayOfArraysToJson,
|
||||||
|
@ -399,11 +399,21 @@ export class GoogleSheetsTrigger implements INodeType {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
|
||||||
let sheetId = this.getNodeParameter('sheetName', undefined, {
|
const sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
const { mode: sheetMode } = this.getNodeParameter('sheetName', 0) as {
|
||||||
|
mode: ResourceLocator;
|
||||||
|
};
|
||||||
|
|
||||||
sheetId = sheetId === 'gid=0' ? '0' : sheetId;
|
const googleSheet = new GoogleSheet(documentId, this);
|
||||||
|
const { sheetId, title: sheetName } = await googleSheet.spreadsheetGetSheet(
|
||||||
|
this.getNode(),
|
||||||
|
sheetMode,
|
||||||
|
sheetWithinDocument,
|
||||||
|
);
|
||||||
|
|
||||||
|
const options = this.getNodeParameter('options') as IDataObject;
|
||||||
|
|
||||||
// If the documentId or sheetId changed, reset the workflow static data
|
// If the documentId or sheetId changed, reset the workflow static data
|
||||||
if (
|
if (
|
||||||
|
@ -417,13 +427,6 @@ export class GoogleSheetsTrigger implements INodeType {
|
||||||
workflowStaticData.lastIndexChecked = undefined;
|
workflowStaticData.lastIndexChecked = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const googleSheet = new GoogleSheet(documentId, this);
|
|
||||||
const sheetName: string = await googleSheet.spreadsheetGetSheetNameById(
|
|
||||||
this.getNode(),
|
|
||||||
sheetId,
|
|
||||||
);
|
|
||||||
const options = this.getNodeParameter('options') as IDataObject;
|
|
||||||
|
|
||||||
const previousRevision = workflowStaticData.lastRevision as number;
|
const previousRevision = workflowStaticData.lastRevision as number;
|
||||||
const previousRevisionLink = workflowStaticData.lastRevisionLink as string;
|
const previousRevisionLink = workflowStaticData.lastRevisionLink as string;
|
||||||
|
|
||||||
|
|
|
@ -29,17 +29,25 @@ export async function router(this: IExecuteFunctions): Promise<INodeExecutionDat
|
||||||
const googleSheet = new GoogleSheet(spreadsheetId, this);
|
const googleSheet = new GoogleSheet(spreadsheetId, this);
|
||||||
|
|
||||||
let sheetId = '';
|
let sheetId = '';
|
||||||
|
let sheetName = '';
|
||||||
|
|
||||||
if (operation !== 'create') {
|
if (operation !== 'create') {
|
||||||
sheetId = this.getNodeParameter('sheetName', 0, undefined, {
|
const sheetWithinDocument = this.getNodeParameter('sheetName', 0, undefined, {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
const { mode: sheetMode } = this.getNodeParameter('sheetName', 0) as {
|
||||||
|
mode: ResourceLocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await googleSheet.spreadsheetGetSheet(
|
||||||
|
this.getNode(),
|
||||||
|
sheetMode,
|
||||||
|
sheetWithinDocument,
|
||||||
|
);
|
||||||
|
sheetId = result.sheetId.toString();
|
||||||
|
sheetName = result.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sheetId === 'gid=0') {
|
|
||||||
sheetId = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
let sheetName = '';
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case 'create':
|
case 'create':
|
||||||
sheetName = spreadsheetId;
|
sheetName = spreadsheetId;
|
||||||
|
@ -50,8 +58,6 @@ export async function router(this: IExecuteFunctions): Promise<INodeExecutionDat
|
||||||
case 'remove':
|
case 'remove':
|
||||||
sheetName = `${spreadsheetId}||${sheetId}`;
|
sheetName = `${spreadsheetId}||${sheetId}`;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
sheetName = await googleSheet.spreadsheetGetSheetNameById(this.getNode(), sheetId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = await sheet[googleSheets.operation].execute.call(
|
results = await sheet[googleSheets.operation].execute.call(
|
||||||
|
|
|
@ -184,6 +184,12 @@ export const descriptions: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'By Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
placeholder: 'Sheet1',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
|
import get from 'lodash/get';
|
||||||
import type {
|
import type {
|
||||||
|
IDataObject,
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
IDataObject,
|
|
||||||
IPollFunctions,
|
|
||||||
INode,
|
INode,
|
||||||
|
IPollFunctions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { ApplicationError, NodeOperationError } from 'n8n-workflow';
|
import { ApplicationError, NodeOperationError } from 'n8n-workflow';
|
||||||
import { utils as xlsxUtils } from 'xlsx';
|
import { utils as xlsxUtils } from 'xlsx';
|
||||||
import get from 'lodash/get';
|
|
||||||
import { apiRequest } from '../transport';
|
import { apiRequest } from '../transport';
|
||||||
import type {
|
import type {
|
||||||
ILookupValues,
|
ILookupValues,
|
||||||
ISheetUpdateData,
|
ISheetUpdateData,
|
||||||
|
ResourceLocator,
|
||||||
SheetCellDecoded,
|
SheetCellDecoded,
|
||||||
SheetRangeData,
|
SheetRangeData,
|
||||||
SheetRangeDecoded,
|
SheetRangeDecoded,
|
||||||
|
SpreadSheetResponse,
|
||||||
ValueInputOption,
|
ValueInputOption,
|
||||||
ValueRenderOption,
|
ValueRenderOption,
|
||||||
} from './GoogleSheets.types';
|
} from './GoogleSheets.types';
|
||||||
import { removeEmptyColumns } from './GoogleSheets.utils';
|
import { getSheetId, removeEmptyColumns } from './GoogleSheets.utils';
|
||||||
|
|
||||||
export class GoogleSheet {
|
export class GoogleSheet {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -116,32 +118,35 @@ export class GoogleSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of a sheet from a sheet id
|
* Returns the sheet within a spreadsheet based on name or ID
|
||||||
*/
|
*/
|
||||||
async spreadsheetGetSheetNameById(node: INode, sheetId: string) {
|
async spreadsheetGetSheet(node: INode, mode: ResourceLocator, value: string) {
|
||||||
const query = {
|
const query = {
|
||||||
fields: 'sheets.properties',
|
fields: 'sheets.properties',
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await apiRequest.call(
|
const response = (await apiRequest.call(
|
||||||
this.executeFunctions,
|
this.executeFunctions,
|
||||||
'GET',
|
'GET',
|
||||||
`/v4/spreadsheets/${this.id}`,
|
`/v4/spreadsheets/${this.id}`,
|
||||||
{},
|
{},
|
||||||
query,
|
query,
|
||||||
);
|
)) as SpreadSheetResponse;
|
||||||
|
|
||||||
const foundItem = response.sheets.find(
|
const foundItem = response.sheets.find((item) => {
|
||||||
(item: { properties: { sheetId: number } }) => item.properties.sheetId === +sheetId,
|
if (mode === 'name') return item.properties.title === value;
|
||||||
);
|
return item.properties.sheetId === getSheetId(value);
|
||||||
|
});
|
||||||
|
|
||||||
if (!foundItem?.properties?.title) {
|
if (!foundItem?.properties?.title) {
|
||||||
throw new NodeOperationError(node, `Sheet with ID ${sheetId} not found`, {
|
throw new NodeOperationError(
|
||||||
level: 'warning',
|
node,
|
||||||
});
|
`Sheet with ${mode === 'name' ? 'name' : 'ID'} ${value} not found`,
|
||||||
|
{ level: 'warning' },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundItem.properties.title;
|
return foundItem.properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,12 +62,24 @@ export type GoogleSheetsSheet = Entity<GoogleSheetsMap, 'sheet'>;
|
||||||
export type SpreadSheetProperties = PropertiesOf<GoogleSheetsSpreadSheet>;
|
export type SpreadSheetProperties = PropertiesOf<GoogleSheetsSpreadSheet>;
|
||||||
export type SheetProperties = PropertiesOf<GoogleSheetsSheet>;
|
export type SheetProperties = PropertiesOf<GoogleSheetsSheet>;
|
||||||
|
|
||||||
export type ResourceLocator = 'id' | 'url' | 'list';
|
export type ResourceLocator = 'id' | 'url' | 'list' | 'name';
|
||||||
|
|
||||||
export const ResourceLocatorUiNames = {
|
export const ResourceLocatorUiNames = {
|
||||||
id: 'By ID',
|
id: 'By ID',
|
||||||
url: 'By URL',
|
url: 'By URL',
|
||||||
list: 'From List',
|
list: 'From List',
|
||||||
|
name: 'By Name',
|
||||||
|
};
|
||||||
|
|
||||||
|
type SpreadSheetResponseSheet = {
|
||||||
|
properties: {
|
||||||
|
title: string;
|
||||||
|
sheetId: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SpreadSheetResponse = {
|
||||||
|
sheets: SpreadSheetResponseSheet[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SheetCellDecoded = {
|
export type SheetCellDecoded = {
|
||||||
|
|
|
@ -45,6 +45,11 @@ export function getSpreadsheetId(
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSheetId(value: string): number {
|
||||||
|
if (value === 'gid=0') return 0;
|
||||||
|
return parseInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert number to Sheets / Excel column name
|
// Convert number to Sheets / Excel column name
|
||||||
export function getColumnName(colNumber: number): string {
|
export function getColumnName(colNumber: number): string {
|
||||||
const baseChar = 'A'.charCodeAt(0);
|
const baseChar = 'A'.charCodeAt(0);
|
||||||
|
|
|
@ -47,15 +47,18 @@ export async function getSheetHeaderRow(
|
||||||
const spreadsheetId = getSpreadsheetId(this.getNode(), mode as ResourceLocator, value as string);
|
const spreadsheetId = getSpreadsheetId(this.getNode(), mode as ResourceLocator, value as string);
|
||||||
|
|
||||||
const sheet = new GoogleSheet(spreadsheetId, this);
|
const sheet = new GoogleSheet(spreadsheetId, this);
|
||||||
let sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
|
const sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
const { mode: sheetMode } = this.getNodeParameter('sheetName', 0) as {
|
||||||
|
mode: ResourceLocator;
|
||||||
|
};
|
||||||
|
|
||||||
if (sheetWithinDocument === 'gid=0') {
|
const { title: sheetName } = await sheet.spreadsheetGetSheet(
|
||||||
sheetWithinDocument = '0';
|
this.getNode(),
|
||||||
}
|
sheetMode,
|
||||||
|
sheetWithinDocument,
|
||||||
const sheetName = await sheet.spreadsheetGetSheetNameById(this.getNode(), sheetWithinDocument);
|
);
|
||||||
const sheetData = await sheet.getData(`${sheetName}!1:1`, 'FORMATTED_VALUE');
|
const sheetData = await sheet.getData(`${sheetName}!1:1`, 'FORMATTED_VALUE');
|
||||||
|
|
||||||
if (sheetData === undefined) {
|
if (sheetData === undefined) {
|
||||||
|
|
|
@ -20,15 +20,16 @@ export async function getMappingColumns(
|
||||||
const spreadsheetId = getSpreadsheetId(this.getNode(), mode as ResourceLocator, value as string);
|
const spreadsheetId = getSpreadsheetId(this.getNode(), mode as ResourceLocator, value as string);
|
||||||
|
|
||||||
const sheet = new GoogleSheet(spreadsheetId, this);
|
const sheet = new GoogleSheet(spreadsheetId, this);
|
||||||
let sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
|
const sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
const { mode: sheetMode } = this.getNodeParameter('sheetName', 0) as { mode: ResourceLocator };
|
||||||
|
|
||||||
if (sheetWithinDocument === 'gid=0') {
|
const { title: sheetName } = await sheet.spreadsheetGetSheet(
|
||||||
sheetWithinDocument = '0';
|
this.getNode(),
|
||||||
}
|
sheetMode,
|
||||||
|
sheetWithinDocument,
|
||||||
const sheetName = await sheet.spreadsheetGetSheetNameById(this.getNode(), sheetWithinDocument);
|
);
|
||||||
const sheetData = await sheet.getData(`${sheetName}!1:1`, 'FORMATTED_VALUE');
|
const sheetData = await sheet.getData(`${sheetName}!1:1`, 'FORMATTED_VALUE');
|
||||||
|
|
||||||
const columns = sheet.testFilter(sheetData || [], 0, 0).filter((col) => col !== '');
|
const columns = sheet.testFilter(sheetData || [], 0, 0).filter((col) => col !== '');
|
||||||
|
|
Loading…
Reference in a new issue