mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
✨ Add GoogleDrive-Node
This commit is contained in:
parent
713535087f
commit
a94d289d81
22
packages/nodes-base/nodes/Google/GoogleApi.ts
Normal file
22
packages/nodes-base/nodes/Google/GoogleApi.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
import { JWT } from 'google-auth-library';
|
||||
import { google } from 'googleapis';
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authentication client needed to access spreadsheet
|
||||
*/
|
||||
export async function getAuthenticationClient(email: string, privateKey: string, scopes: string[]): Promise <JWT> {
|
||||
const client = new google.auth.JWT(
|
||||
email,
|
||||
undefined,
|
||||
privateKey,
|
||||
scopes,
|
||||
undefined
|
||||
);
|
||||
|
||||
// TODO: Check later if this or the above should be cached
|
||||
await client.authorize();
|
||||
|
||||
return client;
|
||||
}
|
954
packages/nodes-base/nodes/Google/GoogleDrive.node.ts
Normal file
954
packages/nodes-base/nodes/Google/GoogleDrive.node.ts
Normal file
|
@ -0,0 +1,954 @@
|
|||
import { google } from 'googleapis';
|
||||
const { Readable } = require('stream');
|
||||
|
||||
import {
|
||||
BINARY_ENCODING,
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
INodeTypeDescription,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { getAuthenticationClient } from './GoogleApi';
|
||||
|
||||
|
||||
export class GoogleDrive implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Google Drive',
|
||||
name: 'googleDrive',
|
||||
icon: 'file:googleDrive.png',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Access data on Google Drive',
|
||||
defaults: {
|
||||
name: 'Google Drive',
|
||||
color: '#3f87f2',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'googleApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
name: 'Folder',
|
||||
value: 'folder',
|
||||
},
|
||||
],
|
||||
default: 'file',
|
||||
description: 'The resource to operate on.',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// operations
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a file',
|
||||
},
|
||||
{
|
||||
name: 'Download',
|
||||
value: 'download',
|
||||
description: 'Download a file',
|
||||
},
|
||||
{
|
||||
name: 'List',
|
||||
value: 'list',
|
||||
description: 'Returns files and folders',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Upload a file',
|
||||
},
|
||||
],
|
||||
default: 'upload',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'folder',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a folder',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a folder',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// file
|
||||
// ----------------------------------
|
||||
|
||||
// ----------------------------------
|
||||
// file/folder:delete
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
'folder',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the file/folder to delete.',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// file:download
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'File Id',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the file to download.',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Property',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'download'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// file:list
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Use Query String',
|
||||
name: 'useQueryString',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'list'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If a query string should be used to filter results.',
|
||||
},
|
||||
{
|
||||
displayName: 'Query String',
|
||||
name: 'queryString',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'list',
|
||||
],
|
||||
useQueryString: [
|
||||
true,
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'name contains \'invoice\'',
|
||||
description: 'Query to use to return only specific files.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'list',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
default: 100,
|
||||
description: 'How many files to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'queryFilters',
|
||||
placeholder: 'Add Filter',
|
||||
description: 'Filters to use to return only specific files.',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'list',
|
||||
],
|
||||
useQueryString: [
|
||||
false,
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'name',
|
||||
displayName: 'Name',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Contains',
|
||||
value: 'contains'
|
||||
},
|
||||
{
|
||||
name: 'Is',
|
||||
value: 'is'
|
||||
},
|
||||
{
|
||||
name: 'Is Not',
|
||||
value: 'isNot'
|
||||
},
|
||||
|
||||
],
|
||||
default: 'contains',
|
||||
description: 'Operation to perform.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value for operation.',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'mimeType',
|
||||
displayName: 'Mime Type',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Mime Type',
|
||||
name: 'mimeType',
|
||||
type: 'options',
|
||||
options: [
|
||||
|
||||
{
|
||||
name: 'Custom Mime Type',
|
||||
value: 'custom',
|
||||
},
|
||||
{
|
||||
name: ' 3rd party shortcut',
|
||||
value: 'application/vnd.google-apps.drive-sdk',
|
||||
},
|
||||
{
|
||||
name: 'Audio',
|
||||
value: 'application/vnd.google-apps.audio',
|
||||
},
|
||||
{
|
||||
name: 'Google Apps Scripts',
|
||||
value: 'application/vnd.google-apps.script',
|
||||
},
|
||||
{
|
||||
name: 'Google Docs',
|
||||
value: 'application/vnd.google-apps.document',
|
||||
},
|
||||
{
|
||||
name: 'Google Drawing',
|
||||
value: 'application/vnd.google-apps.drawing',
|
||||
},
|
||||
{
|
||||
name: 'Google Drive file',
|
||||
value: 'application/vnd.google-apps.file',
|
||||
},
|
||||
{
|
||||
name: 'Google Drive folder',
|
||||
value: 'application/vnd.google-apps.folder',
|
||||
},
|
||||
{
|
||||
name: 'Google Forms',
|
||||
value: 'application/vnd.google-apps.form',
|
||||
},
|
||||
{
|
||||
name: 'Google Fusion Tables',
|
||||
value: 'application/vnd.google-apps.fusiontable',
|
||||
},
|
||||
{
|
||||
name: 'Google My Maps',
|
||||
value: 'application/vnd.google-apps.map',
|
||||
},
|
||||
{
|
||||
name: 'Google Sheets',
|
||||
value: 'application/vnd.google-apps.spreadsheet',
|
||||
},
|
||||
{
|
||||
name: 'Google Sites',
|
||||
value: 'application/vnd.google-apps.site',
|
||||
},
|
||||
{
|
||||
name: 'Google Slides',
|
||||
value: 'application/vnd.google-apps.presentation',
|
||||
},
|
||||
{
|
||||
name: 'Photo',
|
||||
value: 'application/vnd.google-apps.photo',
|
||||
},
|
||||
{
|
||||
name: 'Unknown',
|
||||
value: 'application/vnd.google-apps.unknown',
|
||||
},
|
||||
{
|
||||
name: 'Video',
|
||||
value: 'application/vnd.google-apps.video',
|
||||
},
|
||||
|
||||
],
|
||||
default: 'application/vnd.google-apps.file',
|
||||
description: 'The Mime-Type of the files to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Mime Type',
|
||||
name: 'customMimeType',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
mimeType: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Custom Mime Type',
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// file:upload
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'File Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'invoice_1.pdf',
|
||||
description: 'The name the file should be saved as.',
|
||||
},
|
||||
{
|
||||
displayName: 'Parents',
|
||||
name: 'parents',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The IDs of the parent folders which contain the file.',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Data',
|
||||
name: 'binaryData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If the data to upload should be taken from binary field.',
|
||||
},
|
||||
{
|
||||
displayName: 'File Content',
|
||||
name: 'fileContent',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
binaryData: [
|
||||
false
|
||||
],
|
||||
},
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'The text content of the file to upload.',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Property',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload'
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
binaryData: [
|
||||
true
|
||||
],
|
||||
},
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// folder
|
||||
// ----------------------------------
|
||||
|
||||
// ----------------------------------
|
||||
// folder:create
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Folder',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create'
|
||||
],
|
||||
resource: [
|
||||
'folder',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'invoices',
|
||||
description: 'The name of folder to create.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: '*',
|
||||
value: '*',
|
||||
description: 'All fields.',
|
||||
},
|
||||
{
|
||||
name: 'explicitlyTrashed',
|
||||
value: 'explicitlyTrashed',
|
||||
},
|
||||
{
|
||||
name: 'exportLinks',
|
||||
value: 'exportLinks',
|
||||
},
|
||||
{
|
||||
name: 'iconLink',
|
||||
value: 'iconLink',
|
||||
},
|
||||
{
|
||||
name: 'hasThumbnail',
|
||||
value: 'hasThumbnail',
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
value: 'id',
|
||||
},
|
||||
{
|
||||
name: 'kind',
|
||||
value: 'kind',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
name: 'mimeType',
|
||||
value: 'mimeType',
|
||||
},
|
||||
{
|
||||
name: 'permissions',
|
||||
value: 'permissions',
|
||||
},
|
||||
{
|
||||
name: 'shared',
|
||||
value: 'shared',
|
||||
},
|
||||
{
|
||||
name: 'spaces',
|
||||
value: 'spaces',
|
||||
},
|
||||
{
|
||||
name: 'starred',
|
||||
value: 'starred',
|
||||
},
|
||||
{
|
||||
name: 'thumbnailLink',
|
||||
value: 'thumbnailLink',
|
||||
},
|
||||
{
|
||||
name: 'trashed',
|
||||
value: 'trashed',
|
||||
},
|
||||
{
|
||||
name: 'version',
|
||||
value: 'version',
|
||||
},
|
||||
{
|
||||
name: 'webViewLink',
|
||||
value: 'webViewLink',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The fields to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Spaces',
|
||||
name: 'spaces',
|
||||
type: 'multiOptions',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/operation': [
|
||||
'list'
|
||||
],
|
||||
'/resource': [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: '*',
|
||||
value: '*',
|
||||
description: 'All spaces.',
|
||||
},
|
||||
{
|
||||
name: 'appDataFolder',
|
||||
value: 'appDataFolder',
|
||||
},
|
||||
{
|
||||
name: 'drive',
|
||||
value: 'drive',
|
||||
},
|
||||
{
|
||||
name: 'photos',
|
||||
value: 'photos',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The spaces to operate on.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
'https://www.googleapis.com/auth/drive.appdata',
|
||||
'https://www.googleapis.com/auth/drive.photos.readonly',
|
||||
];
|
||||
|
||||
const client = await getAuthenticationClient(credentials.email as string, credentials.privateKey as string, scopes);
|
||||
|
||||
const drive = google.drive({
|
||||
version: 'v3',
|
||||
auth: client,
|
||||
});
|
||||
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
let queryFields = 'id, name';
|
||||
if (options && options.fields) {
|
||||
const fields = options.fields as string[];
|
||||
if (fields.includes('*')) {
|
||||
queryFields = '*';
|
||||
} else {
|
||||
queryFields = fields.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'file') {
|
||||
if (operation === 'download') {
|
||||
// ----------------------------------
|
||||
// download
|
||||
// ----------------------------------
|
||||
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
|
||||
const response = await drive.files.get(
|
||||
{
|
||||
fileId,
|
||||
alt: 'media',
|
||||
},
|
||||
{
|
||||
responseType: 'arraybuffer',
|
||||
},
|
||||
);
|
||||
|
||||
let mimeType: string | undefined;
|
||||
if (response.headers['content-type']) {
|
||||
mimeType = response.headers['content-type'];
|
||||
}
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[i].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[i].binary !== undefined) {
|
||||
// Create a shallow copy of the binary data so that the old
|
||||
// data references which do not get changed still stay behind
|
||||
// but the incoming data does not get changed.
|
||||
Object.assign(newItem.binary, items[i].binary);
|
||||
}
|
||||
|
||||
items[i] = newItem;
|
||||
|
||||
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
|
||||
const data = Buffer.from(response.data as string);
|
||||
|
||||
items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, undefined, mimeType);
|
||||
|
||||
} else if (operation === 'list') {
|
||||
// ----------------------------------
|
||||
// list
|
||||
// ----------------------------------
|
||||
|
||||
let querySpaces = '';
|
||||
if (options.spaces) {
|
||||
const spaces = options.spaces as string[];
|
||||
if (spaces.includes('*')) {
|
||||
querySpaces = 'appDataFolder, drive, photos';
|
||||
} else {
|
||||
querySpaces = spaces.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
let queryString = '';
|
||||
const useQueryString = this.getNodeParameter('useQueryString', i) as boolean;
|
||||
if (useQueryString === true) {
|
||||
// Use the user defined query string
|
||||
queryString = this.getNodeParameter('queryString', i) as string;
|
||||
} else {
|
||||
// Build query string out of parameters set by user
|
||||
const queryFilters = this.getNodeParameter('queryFilters', i) as IDataObject;
|
||||
|
||||
const queryFilterFields: string[] = [];
|
||||
if (queryFilters.name) {
|
||||
(queryFilters.name as IDataObject[]).forEach(nameFilter => {
|
||||
let operation = nameFilter.operation;
|
||||
if (operation === 'is') {
|
||||
operation = '=';
|
||||
} else if (operation === 'isNot') {
|
||||
operation = '!=';
|
||||
}
|
||||
queryFilterFields.push(`name ${operation} '${nameFilter.value}'`);
|
||||
});
|
||||
|
||||
queryString += queryFilterFields.join(' or ');
|
||||
}
|
||||
|
||||
queryFilterFields.length = 0;
|
||||
if (queryFilters.mimeType) {
|
||||
(queryFilters.mimeType as IDataObject[]).forEach(mimeTypeFilter => {
|
||||
let mimeType = mimeTypeFilter.mimeType;
|
||||
if (mimeTypeFilter.mimeType === 'custom') {
|
||||
mimeType = mimeTypeFilter.customMimeType;
|
||||
}
|
||||
queryFilterFields.push(`mimeType = '${mimeType}'`);
|
||||
});
|
||||
|
||||
if (queryFilterFields.length) {
|
||||
if (queryString !== '') {
|
||||
queryString += ' and ';
|
||||
}
|
||||
|
||||
queryString += queryFilterFields.join(' or ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pageSize = this.getNodeParameter('limit', i) as number;
|
||||
|
||||
const res = await drive.files.list({
|
||||
pageSize,
|
||||
orderBy: 'modifiedTime',
|
||||
fields: `nextPageToken, files(${queryFields})`,
|
||||
spaces: querySpaces,
|
||||
q: queryString,
|
||||
});
|
||||
|
||||
const files = res!.data.files;
|
||||
|
||||
return [this.helpers.returnJsonArray(files as IDataObject[])];
|
||||
|
||||
} else if (operation === 'upload') {
|
||||
// ----------------------------------
|
||||
// upload
|
||||
// ----------------------------------
|
||||
|
||||
let mimeType = 'text/plain';
|
||||
let body;
|
||||
let originalFilename: string | undefined;
|
||||
if (this.getNodeParameter('binaryData', i) === true) {
|
||||
// Is binary file to upload
|
||||
const item = items[i];
|
||||
|
||||
if (item.binary === undefined) {
|
||||
throw new Error('No binary data exists on item!');
|
||||
}
|
||||
|
||||
const propertyNameUpload = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
|
||||
if (item.binary[propertyNameUpload] === undefined) {
|
||||
throw new Error(`No binary data property "${propertyNameUpload}" does not exists on item!`);
|
||||
}
|
||||
|
||||
if (item.binary[propertyNameUpload].mimeType) {
|
||||
mimeType = item.binary[propertyNameUpload].mimeType;
|
||||
}
|
||||
|
||||
if (item.binary[propertyNameUpload].fileName) {
|
||||
originalFilename = item.binary[propertyNameUpload].fileName;
|
||||
}
|
||||
|
||||
body = Buffer.from(item.binary[propertyNameUpload].data, BINARY_ENCODING);
|
||||
} else {
|
||||
// Is text file
|
||||
body = Buffer.from(this.getNodeParameter('fileContent', i) as string, 'utf8');
|
||||
}
|
||||
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
const parents = this.getNodeParameter('parents', i) as string[];
|
||||
|
||||
const response = await drive.files.create({
|
||||
requestBody: {
|
||||
name,
|
||||
originalFilename,
|
||||
parents,
|
||||
},
|
||||
fields: queryFields,
|
||||
media: {
|
||||
mimeType,
|
||||
body: ((buffer: Buffer) => {
|
||||
const readableInstanceStream = new Readable({
|
||||
read() {
|
||||
this.push(buffer);
|
||||
this.push(null);
|
||||
}
|
||||
});
|
||||
|
||||
return readableInstanceStream;
|
||||
})(body),
|
||||
},
|
||||
});
|
||||
|
||||
returnData.push(response.data as IDataObject);
|
||||
}
|
||||
|
||||
} else if (resource === 'folder') {
|
||||
if (operation === 'create') {
|
||||
// ----------------------------------
|
||||
// folder:create
|
||||
// ----------------------------------
|
||||
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
|
||||
const fileMetadata = {
|
||||
name,
|
||||
mimeType: 'application/vnd.google-apps.folder'
|
||||
};
|
||||
|
||||
const response = await drive.files.create({
|
||||
// @ts-ignore
|
||||
resource: fileMetadata,
|
||||
fields: queryFields,
|
||||
});
|
||||
|
||||
returnData.push(response.data as IDataObject);
|
||||
}
|
||||
}
|
||||
if (['file', 'folder'].includes(resource)) {
|
||||
if (operation === 'delete') {
|
||||
// ----------------------------------
|
||||
// delete
|
||||
// ----------------------------------
|
||||
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
|
||||
await drive.files.delete({
|
||||
fileId,
|
||||
});
|
||||
|
||||
// If we are still here it did succeed
|
||||
returnData.push({
|
||||
fileId,
|
||||
success: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error(`The resource "${resource}" is not known!`);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'file' && operation === 'download') {
|
||||
// For file downloads the files get attached to the existing items
|
||||
return this.prepareOutputData(items);
|
||||
} else {
|
||||
// For all other ones does the output items get replaced
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { IDataObject } from 'n8n-workflow';
|
||||
import { google } from 'googleapis';
|
||||
import { JWT } from 'google-auth-library';
|
||||
import { getAuthenticationClient } from './GoogleApi';
|
||||
|
||||
const Sheets = google.sheets('v4'); // tslint:disable-line:variable-name
|
||||
|
||||
|
@ -135,18 +136,7 @@ export class GoogleSheet {
|
|||
* Returns the authentication client needed to access spreadsheet
|
||||
*/
|
||||
async getAuthenticationClient(): Promise<JWT> {
|
||||
const client = new google.auth.JWT(
|
||||
this.credentials.email,
|
||||
undefined,
|
||||
this.credentials.privateKey,
|
||||
this.scopes,
|
||||
undefined
|
||||
);
|
||||
|
||||
// TODO: Check later if this or the above should be cached
|
||||
await client.authorize();
|
||||
|
||||
return client;
|
||||
return getAuthenticationClient(this.credentials.email, this.credentials.privateKey, this.scopes);
|
||||
}
|
||||
|
||||
|
BIN
packages/nodes-base/nodes/Google/googleDrive.png
Normal file
BIN
packages/nodes-base/nodes/Google/googleDrive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -77,7 +77,8 @@
|
|||
"dist/nodes/Github/GithubTrigger.node.js",
|
||||
"dist/nodes/Gitlab/Gitlab.node.js",
|
||||
"dist/nodes/Gitlab/GitlabTrigger.node.js",
|
||||
"dist/nodes/GoogleSheets/GoogleSheets.node.js",
|
||||
"dist/nodes/Google/GoogleDrive.node.js",
|
||||
"dist/nodes/Google/GoogleSheets.node.js",
|
||||
"dist/nodes/HttpRequest.node.js",
|
||||
"dist/nodes/If.node.js",
|
||||
"dist/nodes/Interval.node.js",
|
||||
|
|
Loading…
Reference in a new issue