feat(Google Drive Trigger Node): Use resource locator component (#5148)

* ♻️ move GoogleDrive.node.ts search methods to SearchFuntions.ts

* ️parameter Folder RLC

* ️parameter File RLC

* 🎨 File RLC placeholder use 'drive.google.com/xxx' urls
This commit is contained in:
Marcus 2023-01-16 10:45:44 +01:00 committed by GitHub
parent c53ce1e5b7
commit 9958c324db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 209 additions and 97 deletions

View file

@ -2,9 +2,7 @@ import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core';
import { import {
IDataObject, IDataObject,
ILoadOptionsFunctions,
INodeExecutionData, INodeExecutionData,
INodeListSearchResult,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
NodeOperationError, NodeOperationError,
@ -14,21 +12,10 @@ import { googleApiRequest, googleApiRequestAllItems } from './GenericFunctions';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
import { driveSearch, fileSearch, folderSearch } from './SearchFunctions';
const UPLOAD_CHUNK_SIZE = 256 * 1024; const UPLOAD_CHUNK_SIZE = 256 * 1024;
interface GoogleDriveFilesItem {
id: string;
name: string;
mimeType: string;
webViewLink: string;
}
interface GoogleDriveDriveItem {
id: string;
name: string;
}
export class GoogleDrive implements INodeType { export class GoogleDrive implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'Google Drive', displayName: 'Google Drive',
@ -248,7 +235,7 @@ export class GoogleDrive implements INodeType {
name: 'url', name: 'url',
type: 'string', type: 'string',
placeholder: placeholder:
'https://docs.google.com/spreadsheets/d/1-i6Vx0NN-3333eeeeeeeeee333333333/edit', 'https://drive.google.com/file/d/1anGBg0b5re2VtF2bKu201_a-Vnz5BHq9Y4r-yBDAj5A/edit',
extractValue: { extractValue: {
type: 'regex', type: 'regex',
regex: regex:
@ -2035,73 +2022,9 @@ export class GoogleDrive implements INodeType {
methods = { methods = {
listSearch: { listSearch: {
async fileSearch( fileSearch,
this: ILoadOptionsFunctions, folderSearch,
filter?: string, driveSearch,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const query: string[] = [];
if (filter) {
query.push(`name contains '${filter.replace("'", "\\'")}'`);
}
query.push("mimeType != 'application/vnd.google-apps.folder'");
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/files', undefined, {
q: query.join(' and '),
pageToken: paginationToken,
fields: 'nextPageToken,files(id,name,mimeType,webViewLink)',
orderBy: 'name_natural',
});
return {
results: res.files.map((i: GoogleDriveFilesItem) => ({
name: i.name,
value: i.id,
url: i.webViewLink,
})),
paginationToken: res.nextPageToken,
};
},
async folderSearch(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const query: string[] = [];
if (filter) {
query.push(`name contains '${filter.replace("'", "\\'")}'`);
}
query.push("mimeType = 'application/vnd.google-apps.folder'");
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/files', undefined, {
q: query.join(' and '),
pageToken: paginationToken,
fields: 'nextPageToken,files(id,name,mimeType,webViewLink)',
orderBy: 'name_natural',
});
return {
results: res.files.map((i: GoogleDriveFilesItem) => ({
name: i.name,
value: i.id,
url: i.webViewLink,
})),
paginationToken: res.nextPageToken,
};
},
async driveSearch(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/drives', undefined, {
q: filter ? `name contains '${filter.replace("'", "\\'")}'` : undefined,
pageToken: paginationToken,
});
return {
results: res.drives.map((i: GoogleDriveDriveItem) => ({
name: i.name,
value: i.id,
})),
paginationToken: res.nextPageToken,
};
},
}, },
}; };

View file

@ -13,6 +13,7 @@ import {
import { extractId, googleApiRequest, googleApiRequestAllItems } from './GenericFunctions'; import { extractId, googleApiRequest, googleApiRequestAllItems } from './GenericFunctions';
import moment from 'moment'; import moment from 'moment';
import { fileSearch, folderSearch } from './SearchFunctions';
export class GoogleDriveTrigger implements INodeType { export class GoogleDriveTrigger implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
@ -89,18 +90,65 @@ export class GoogleDriveTrigger implements INodeType {
], ],
}, },
{ {
displayName: 'File URL or ID', displayName: 'File',
name: 'fileToWatch', name: 'fileToWatch',
type: 'string', type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
modes: [
{
displayName: 'File',
name: 'list',
type: 'list',
placeholder: 'Select a file...',
typeOptions: {
searchListMethod: 'fileSearch',
searchable: true,
},
},
{
displayName: 'Link',
name: 'url',
type: 'string',
placeholder: 'https://drive.google.com/file/d/1wroCSfK-hupQIYf_xzeoUEzOhvfTFH2P/edit',
extractValue: {
type: 'regex',
regex:
'https:\\/\\/(?:drive|docs)\\.google\\.com\\/\\w+\\/d\\/([0-9a-zA-Z\\-_]+)(?:\\/.*|)',
},
validation: [
{
type: 'regex',
properties: {
regex:
'https:\\/\\/(?:drive|docs)\\.google.com\\/\\w+\\/d\\/([0-9a-zA-Z\\-_]+)(?:\\/.*|)',
errorMessage: 'Not a valid Google Drive File URL',
},
},
],
},
{
displayName: 'ID',
name: 'id',
type: 'string',
placeholder: '1anGBg0b5re2VtF2bKu201_a-Vnz5BHq9Y4r-yBDAj5A',
validation: [
{
type: 'regex',
properties: {
regex: '[a-zA-Z0-9\\-_]{2,}',
errorMessage: 'Not a valid Google Drive File ID',
},
},
],
url: '=https://drive.google.com/file/d/{{$value}}/view',
},
],
displayOptions: { displayOptions: {
show: { show: {
triggerOn: ['specificFile'], triggerOn: ['specificFile'],
}, },
}, },
default: '',
description:
'The address of this file when you view it in your browser (or just the ID contained within the URL)',
required: true,
}, },
{ {
displayName: 'Watch For', displayName: 'Watch For',
@ -122,18 +170,65 @@ export class GoogleDriveTrigger implements INodeType {
description: 'When to trigger this node', description: 'When to trigger this node',
}, },
{ {
displayName: 'Folder URL or ID', displayName: 'Folder',
name: 'folderToWatch', name: 'folderToWatch',
type: 'string', type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
modes: [
{
displayName: 'Folder',
name: 'list',
type: 'list',
placeholder: 'Select a folder...',
typeOptions: {
searchListMethod: 'folderSearch',
searchable: true,
},
},
{
displayName: 'Link',
name: 'url',
type: 'string',
placeholder: 'https://drive.google.com/drive/folders/1Tx9WHbA3wBpPB4C_HcoZDH9WZFWYxAMU',
extractValue: {
type: 'regex',
regex:
'https:\\/\\/drive\\.google\\.com\\/\\w+\\/folders\\/([0-9a-zA-Z\\-_]+)(?:\\/.*|)',
},
validation: [
{
type: 'regex',
properties: {
regex:
'https:\\/\\/drive\\.google\\.com\\/\\w+\\/folders\\/([0-9a-zA-Z\\-_]+)(?:\\/.*|)',
errorMessage: 'Not a valid Google Drive Folder URL',
},
},
],
},
{
displayName: 'ID',
name: 'id',
type: 'string',
placeholder: '1anGBg0b5re2VtF2bKu201_a-Vnz5BHq9Y4r-yBDAj5A',
validation: [
{
type: 'regex',
properties: {
regex: '[a-zA-Z0-9\\-_]{2,}',
errorMessage: 'Not a valid Google Drive Folder ID',
},
},
],
url: '=https://drive.google.com/drive/folders/{{$value}}',
},
],
displayOptions: { displayOptions: {
show: { show: {
triggerOn: ['specificFolder'], triggerOn: ['specificFolder'],
}, },
}, },
default: '',
description:
'The address of this folder when you view it in your browser (or just the ID contained within the URL)',
required: true,
}, },
{ {
displayName: 'Watch For', displayName: 'Watch For',
@ -303,6 +398,10 @@ export class GoogleDriveTrigger implements INodeType {
}; };
methods = { methods = {
listSearch: {
fileSearch,
folderSearch,
},
loadOptions: { loadOptions: {
// Get all the calendars to display them to user so that he can // Get all the calendars to display them to user so that he can
// select them easily // select them easily
@ -345,7 +444,9 @@ export class GoogleDriveTrigger implements INodeType {
const query = ['trashed = false']; const query = ['trashed = false'];
if (triggerOn === 'specificFolder' && event !== 'watchFolderUpdated') { if (triggerOn === 'specificFolder' && event !== 'watchFolderUpdated') {
const folderToWatch = extractId(this.getNodeParameter('folderToWatch') as string); const folderToWatch = extractId(
this.getNodeParameter('folderToWatch', '', { extractValue: true }) as string,
);
query.push(`'${folderToWatch}' in parents`); query.push(`'${folderToWatch}' in parents`);
} }
@ -387,7 +488,9 @@ export class GoogleDriveTrigger implements INodeType {
} }
if (triggerOn === 'specificFile' && this.getMode() !== 'manual') { if (triggerOn === 'specificFile' && this.getMode() !== 'manual') {
const fileToWatch = extractId(this.getNodeParameter('fileToWatch') as string); const fileToWatch = extractId(
this.getNodeParameter('fileToWatch', '', { extractValue: true }) as string,
);
files = files.filter((file: { id: string }) => file.id === fileToWatch); files = files.filter((file: { id: string }) => file.id === fileToWatch);
} }
@ -396,7 +499,9 @@ export class GoogleDriveTrigger implements INodeType {
event === 'watchFolderUpdated' && event === 'watchFolderUpdated' &&
this.getMode() !== 'manual' this.getMode() !== 'manual'
) { ) {
const folderToWatch = extractId(this.getNodeParameter('folderToWatch') as string); const folderToWatch = extractId(
this.getNodeParameter('folderToWatch', '', { extractValue: true }) as string,
);
files = files.filter((file: { id: string }) => file.id === folderToWatch); files = files.filter((file: { id: string }) => file.id === folderToWatch);
} }

View file

@ -0,0 +1,84 @@
import { ILoadOptionsFunctions, INodeListSearchResult } from 'n8n-workflow';
import { googleApiRequest } from './GenericFunctions';
interface GoogleDriveFilesItem {
id: string;
name: string;
mimeType: string;
webViewLink: string;
}
interface GoogleDriveDriveItem {
id: string;
name: string;
}
export async function fileSearch(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const query: string[] = [];
if (filter) {
query.push(`name contains '${filter.replace("'", "\\'")}'`);
}
query.push("mimeType != 'application/vnd.google-apps.folder'");
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/files', undefined, {
q: query.join(' and '),
pageToken: paginationToken,
fields: 'nextPageToken,files(id,name,mimeType,webViewLink)',
orderBy: 'name_natural',
});
return {
results: res.files.map((i: GoogleDriveFilesItem) => ({
name: i.name,
value: i.id,
url: i.webViewLink,
})),
paginationToken: res.nextPageToken,
};
}
export async function folderSearch(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const query: string[] = [];
if (filter) {
query.push(`name contains '${filter.replace("'", "\\'")}'`);
}
query.push("mimeType = 'application/vnd.google-apps.folder'");
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/files', undefined, {
q: query.join(' and '),
pageToken: paginationToken,
fields: 'nextPageToken,files(id,name,mimeType,webViewLink)',
orderBy: 'name_natural',
});
return {
results: res.files.map((i: GoogleDriveFilesItem) => ({
name: i.name,
value: i.id,
url: i.webViewLink,
})),
paginationToken: res.nextPageToken,
};
}
export async function driveSearch(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const res = await googleApiRequest.call(this, 'GET', '/drive/v3/drives', undefined, {
q: filter ? `name contains '${filter.replace("'", "\\'")}'` : undefined,
pageToken: paginationToken,
});
return {
results: res.drives.map((i: GoogleDriveDriveItem) => ({
name: i.name,
value: i.id,
})),
paginationToken: res.nextPageToken,
};
}