feat(Google Sheets Node): Overhaul of node

This commit is contained in:
Jonathan Bennetts
2022-11-15 13:57:07 +00:00
committed by GitHub
parent 6eee155ecb
commit d96d6f11db
30 changed files with 5301 additions and 1394 deletions

View File

@@ -0,0 +1,35 @@
import {
ICredentialsDecrypted,
ICredentialTestFunctions,
INodeCredentialTestResult,
} from 'n8n-workflow';
import { getAccessToken, IGoogleAuthCredentials } from '../transport';
export async function googleApiCredentialTest(
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
try {
const tokenRequest = await getAccessToken.call(
this,
credential.data! as unknown as IGoogleAuthCredentials,
);
if (!tokenRequest.access_token) {
return {
status: 'Error',
message: 'Could not generate a token from your private key.',
};
}
} catch (err) {
return {
status: 'Error',
message: `Private key validation failed: ${err.message}`,
};
}
return {
status: 'OK',
message: 'Connection successful!',
};
}

View File

@@ -0,0 +1,3 @@
export * as loadOptions from './loadOptions';
export * as listSearch from './listSearch';
export * as credentialTest from './credentialTest';

View File

@@ -0,0 +1,96 @@
import {
IDataObject,
ILoadOptionsFunctions,
INodeListSearchItems,
INodeListSearchResult,
NodeOperationError,
} from 'n8n-workflow';
import { ResourceLocator } from '../helpers/GoogleSheets.types';
import { getSpreadsheetId, sortLoadOptions } from '../helpers/GoogleSheets.utils';
import { apiRequest, apiRequestAllItems } from '../transport';
export async function spreadSheetsSearch(
this: ILoadOptionsFunctions,
filter?: string,
): Promise<INodeListSearchResult> {
try {
const returnData: INodeListSearchItems[] = [];
const query: string[] = [];
if (filter) {
query.push(`name contains '${filter.replace("'", "\\'")}'`);
}
query.push("mimeType = 'application/vnd.google-apps.spreadsheet'");
const qs = {
pageSize: 50,
orderBy: 'modifiedTime desc',
fields: 'nextPageToken, files(id, name, webViewLink)',
q: query.join(' and '),
includeItemsFromAllDrives: true,
supportsAllDrives: true,
};
const sheets = await apiRequestAllItems.call(
this,
'files',
'GET',
'',
{},
qs,
'https://www.googleapis.com/drive/v3/files',
);
for (const sheet of sheets) {
returnData.push({
name: sheet.name as string,
value: sheet.id as string,
url: sheet.webViewLink as string,
});
}
return { results: sortLoadOptions(returnData) };
} catch (error) {
return { results: [] };
}
}
export async function sheetsSearch(
this: ILoadOptionsFunctions,
_filter?: string,
): Promise<INodeListSearchResult> {
try {
const { mode, value } = this.getNodeParameter('documentId', 0) as IDataObject;
const spreadsheetId = getSpreadsheetId(mode as ResourceLocator, value as string);
const query = {
fields: 'sheets.properties',
};
const responseData = await apiRequest.call(
this,
'GET',
`/v4/spreadsheets/${spreadsheetId}`,
{},
query,
);
if (responseData === undefined) {
throw new NodeOperationError(this.getNode(), 'No data got returned');
}
const returnData: INodeListSearchItems[] = [];
for (const sheet of responseData.sheets!) {
if (sheet.properties!.sheetType !== 'GRID') {
continue;
}
returnData.push({
name: sheet.properties!.title as string,
value: (sheet.properties!.sheetId as number) || 'gid=0',
//prettier-ignore
url: `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit#gid=${sheet.properties!.sheetId}`,
});
}
return { results: returnData };
} catch (error) {
return { results: [] };
}
}

View File

@@ -0,0 +1,112 @@
import {
IDataObject,
ILoadOptionsFunctions,
INodePropertyOptions,
NodeOperationError,
} from 'n8n-workflow';
import { GoogleSheet } from '../helpers/GoogleSheet';
import { getSpreadsheetId } from '../helpers/GoogleSheets.utils';
import { ResourceLocator } from '../helpers/GoogleSheets.types';
export async function getSheets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
try {
const { mode, value } = this.getNodeParameter('documentId', 0) as IDataObject;
const spreadsheetId = getSpreadsheetId(mode as ResourceLocator, value as string);
const sheet = new GoogleSheet(spreadsheetId, this);
const responseData = await sheet.spreadsheetGetSheets();
if (responseData === undefined) {
throw new NodeOperationError(this.getNode(), 'No data got returned');
}
const returnData: INodePropertyOptions[] = [];
for (const sheet of responseData.sheets!) {
if (sheet.properties!.sheetType !== 'GRID') {
continue;
}
returnData.push({
name: sheet.properties!.title as string,
value: sheet.properties!.sheetId as unknown as string,
});
}
return returnData;
} catch (error) {
return [];
}
}
export async function getSheetHeaderRow(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
try {
const { mode, value } = this.getNodeParameter('documentId', 0) as IDataObject;
const spreadsheetId = getSpreadsheetId(mode as ResourceLocator, value as string);
const sheet = new GoogleSheet(spreadsheetId, this);
let sheetWithinDocument = this.getNodeParameter('sheetName', undefined, {
extractValue: true,
}) as string;
if (sheetWithinDocument === 'gid=0') {
sheetWithinDocument = '0';
}
const sheetName = await sheet.spreadsheetGetSheetNameById(sheetWithinDocument);
const sheetData = await sheet.getData(`${sheetName}!1:1`, 'FORMATTED_VALUE');
if (sheetData === undefined) {
throw new NodeOperationError(this.getNode(), 'No data got returned');
}
const columns = sheet.testFilter(sheetData, 0, 0);
const returnData: INodePropertyOptions[] = [];
for (const column of columns) {
returnData.push({
name: column as unknown as string,
value: column as unknown as string,
});
}
return returnData;
} catch (error) {
return [];
}
}
export async function getSheetHeaderRowAndAddColumn(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData = await getSheetHeaderRow.call(this);
returnData.push({
name: 'New column ...',
value: 'newColumn',
});
const columnToMatchOn = this.getNodeParameter('columnToMatchOn', 0) as string;
return returnData.filter((column) => column.value !== columnToMatchOn);
}
export async function getSheetHeaderRowWithGeneratedColumnNames(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData = await getSheetHeaderRow.call(this);
return returnData.map((column, i) => {
if (column.value !== '') return column;
const indexBasedValue = `col_${i + 1}`;
return {
name: indexBasedValue,
value: indexBasedValue,
};
});
}
export async function getSheetHeaderRowAndSkipEmpty(
this: ILoadOptionsFunctions,
): Promise<INodePropertyOptions[]> {
const returnData = await getSheetHeaderRow.call(this);
return returnData.filter((column) => column.value);
}