mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
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:
parent
c53ce1e5b7
commit
9958c324db
|
@ -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,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
packages/nodes-base/nodes/Google/Drive/SearchFunctions.ts
Normal file
84
packages/nodes-base/nodes/Google/Drive/SearchFunctions.ts
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue