n8n-nodes-service-account-google-drive
Version:
n8n node for Google Drive with Service Account authentication
1,068 lines • 46.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleDriveServiceAccount = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const googleapis_1 = require("googleapis");
class GoogleDriveServiceAccount {
constructor() {
this.description = {
displayName: 'Google Drive (Service Account)',
name: 'googleDriveServiceAccount',
icon: 'file:googleDrive.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Access Google Drive API using Service Account',
defaults: {
name: 'Google Drive SA',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'googleDriveServiceAccountApi',
required: false,
},
],
properties: [
// Authentication Type Selector
{
displayName: 'Authentication',
name: 'authType',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Credential',
value: 'credential',
description: 'Use saved n8n credentials',
},
{
name: 'Manual',
value: 'manual',
description: 'Enter credentials manually for each execution',
},
],
default: 'credential',
description: 'Choose authentication method',
},
// Manual Authentication Fields
{
displayName: 'Service Account Email',
name: 'serviceAccountEmail',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
authType: ['manual'],
},
},
description: 'Service account email address (from service account JSON)',
},
{
displayName: 'Private Key',
name: 'privateKey',
type: 'string',
default: '',
required: true,
typeOptions: {
alwaysOpenEditWindow: true,
},
displayOptions: {
show: {
authType: ['manual'],
},
},
description: 'Private key (from service account JSON)',
},
{
displayName: 'Impersonate Email (Optional)',
name: 'impersonateEmail',
type: 'string',
default: '',
required: false,
displayOptions: {
show: {
authType: ['manual'],
},
},
description: 'Email of user to impersonate (requires domain-wide delegation)',
},
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'File',
value: 'file',
},
{
name: 'Folder',
value: 'folder',
},
{
name: 'Permission',
value: 'permission',
},
],
default: 'file',
},
// File Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['file'],
},
},
options: [
{
name: 'Copy',
value: 'copy',
description: 'Copy a file',
action: 'Copy a file',
},
{
name: 'Create',
value: 'create',
description: 'Create a file',
action: 'Create a file',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a file',
action: 'Delete a file',
},
{
name: 'Download',
value: 'download',
description: 'Download a file',
action: 'Download a file',
},
{
name: 'Export',
value: 'export',
description: 'Export a Google Workspace file',
action: 'Export a file',
},
{
name: 'Get',
value: 'get',
description: 'Get a file',
action: 'Get a file',
},
{
name: 'Import',
value: 'import',
description: 'Import file converting to Google Workspace format',
action: 'Import a file',
},
{
name: 'List',
value: 'list',
description: 'List files',
action: 'List files',
},
{
name: 'Move',
value: 'move',
description: 'Move a file',
action: 'Move a file',
},
{
name: 'Update',
value: 'update',
description: 'Update a file',
action: 'Update a file',
},
{
name: 'Upload',
value: 'upload',
description: 'Upload a file',
action: 'Upload a file',
},
],
default: 'list',
},
// Folder Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['folder'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a folder',
action: 'Create a folder',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a folder',
action: 'Delete a folder',
},
{
name: 'List',
value: 'list',
description: 'List folders',
action: 'List folders',
},
],
default: 'create',
},
// Permission Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['permission'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a permission',
action: 'Create a permission',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a permission',
action: 'Delete a permission',
},
{
name: 'Get',
value: 'get',
description: 'Get a permission',
action: 'Get a permission',
},
{
name: 'List',
value: 'list',
description: 'List permissions',
action: 'List permissions',
},
{
name: 'Update',
value: 'update',
description: 'Update a permission',
action: 'Update a permission',
},
],
default: 'create',
},
// File ID (for most file operations)
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['get', 'delete', 'download', 'update', 'copy', 'move', 'export'],
},
},
description: 'The ID of the file',
},
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['permission'],
},
},
description: 'The ID of the file',
},
// Folder ID
{
displayName: 'Folder ID',
name: 'folderId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['folder'],
operation: ['delete'],
},
},
description: 'The ID of the folder',
},
// Permission ID
{
displayName: 'Permission ID',
name: 'permissionId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['permission'],
operation: ['get', 'delete', 'update'],
},
},
description: 'The ID of the permission',
},
// Export Format
{
displayName: 'Export Format',
name: 'exportFormat',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['export'],
},
},
options: [
// Google Docs
{ name: 'PDF', value: 'application/pdf' },
{ name: 'Microsoft Word (DOCX)', value: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' },
{ name: 'Rich Text (RTF)', value: 'application/rtf' },
{ name: 'Plain Text', value: 'text/plain' },
{ name: 'HTML', value: 'text/html' },
{ name: 'EPUB', value: 'application/epub+zip' },
// Google Sheets
{ name: 'Microsoft Excel (XLSX)', value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' },
{ name: 'CSV', value: 'text/csv' },
{ name: 'TSV', value: 'text/tab-separated-values' },
{ name: 'OpenDocument Spreadsheet (ODS)', value: 'application/vnd.oasis.opendocument.spreadsheet' },
// Google Slides
{ name: 'Microsoft PowerPoint (PPTX)', value: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' },
{ name: 'OpenDocument Presentation (ODP)', value: 'application/vnd.oasis.opendocument.presentation' },
// Google Drawings
{ name: 'JPEG', value: 'image/jpeg' },
{ name: 'PNG', value: 'image/png' },
{ name: 'SVG', value: 'image/svg+xml' },
],
default: 'application/pdf',
description: 'Format to export the file to',
},
// File Create/Upload Fields
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['create', 'upload', 'import'],
},
},
description: 'Name of the file',
},
{
displayName: 'File Content',
name: 'fileContent',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['create', 'upload', 'import'],
},
},
description: 'Content of the file (text or base64)',
},
// Import Convert Type
{
displayName: 'Convert To',
name: 'convertTo',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['import'],
},
},
options: [
{ name: 'Google Docs', value: 'application/vnd.google-apps.document' },
{ name: 'Google Sheets', value: 'application/vnd.google-apps.spreadsheet' },
{ name: 'Google Slides', value: 'application/vnd.google-apps.presentation' },
{ name: 'Google Drawings', value: 'application/vnd.google-apps.drawing' },
],
default: 'application/vnd.google-apps.document',
description: 'Google Workspace format to convert the file to',
},
// Folder Create Fields
{
displayName: 'Folder Name',
name: 'folderName',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['folder'],
operation: ['create'],
},
},
description: 'Name of the folder',
},
// Parent Folder ID
{
displayName: 'Parent Folder ID',
name: 'parentFolderId',
type: 'string',
default: '',
displayOptions: {
show: {
resource: ['file', 'folder'],
operation: ['create', 'upload', 'import'],
},
},
description: 'ID of the parent folder (leave empty for root)',
},
{
displayName: 'New Parent Folder ID',
name: 'newParentFolderId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['file'],
operation: ['move'],
},
},
description: 'ID of the destination folder',
},
// Copy file fields
{
displayName: 'New File Name',
name: 'newFileName',
type: 'string',
default: '',
displayOptions: {
show: {
resource: ['file'],
operation: ['copy'],
},
},
description: 'Name for the copied file (optional)',
},
// Permission fields
{
displayName: 'Role',
name: 'role',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['permission'],
operation: ['create', 'update'],
},
},
options: [
{
name: 'Owner',
value: 'owner',
},
{
name: 'Organizer',
value: 'organizer',
},
{
name: 'File Organizer',
value: 'fileOrganizer',
},
{
name: 'Writer',
value: 'writer',
},
{
name: 'Commenter',
value: 'commenter',
},
{
name: 'Reader',
value: 'reader',
},
],
default: 'reader',
description: 'The role granted by this permission',
},
{
displayName: 'Type',
name: 'permissionType',
type: 'options',
required: true,
displayOptions: {
show: {
resource: ['permission'],
operation: ['create'],
},
},
options: [
{
name: 'User',
value: 'user',
},
{
name: 'Group',
value: 'group',
},
{
name: 'Domain',
value: 'domain',
},
{
name: 'Anyone',
value: 'anyone',
},
],
default: 'user',
description: 'The type of the grantee',
},
{
displayName: 'Email Address',
name: 'emailAddress',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['permission'],
operation: ['create'],
permissionType: ['user', 'group'],
},
},
description: 'Email address of the user or group',
},
{
displayName: 'Domain',
name: 'domain',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['permission'],
operation: ['create'],
permissionType: ['domain'],
},
},
description: 'The domain name',
},
// List Options
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: ['file', 'folder'],
operation: ['list'],
},
},
options: [
{
displayName: 'Query',
name: 'q',
type: 'string',
default: '',
description: 'Query string for filtering files (e.g., name contains "report")',
},
{
displayName: 'Page Size',
name: 'pageSize',
type: 'number',
default: 100,
description: 'Maximum number of files to return',
},
{
displayName: 'Order By',
name: 'orderBy',
type: 'string',
default: '',
description: 'Sort order (e.g., "createdTime desc")',
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '*',
description: 'Fields to return (e.g., "files(id,name,mimeType)")',
},
],
},
// Update Options
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['file'],
operation: ['update'],
},
},
options: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'New name for the file',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'New description for the file',
},
{
displayName: 'Starred',
name: 'starred',
type: 'boolean',
default: false,
description: 'Whether the file is starred',
},
{
displayName: 'Trashed',
name: 'trashed',
type: 'boolean',
default: false,
description: 'Whether the file is trashed',
},
],
},
// Additional Options
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['file'],
operation: ['create', 'upload', 'import'],
},
},
options: [
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Description of the file',
},
{
displayName: 'MIME Type',
name: 'mimeType',
type: 'string',
default: '',
description: 'MIME type of the file (for upload/create operations)',
},
],
},
{
displayName: 'Options',
name: 'permissionOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: ['permission'],
operation: ['create'],
},
},
options: [
{
displayName: 'Send Notification Email',
name: 'sendNotificationEmail',
type: 'boolean',
default: true,
description: 'Whether to send a notification email',
},
{
displayName: 'Email Message',
name: 'emailMessage',
type: 'string',
default: '',
description: 'A custom message to include in the notification email',
},
],
},
],
};
}
async execute() {
var _a;
const items = this.getInputData();
const returnData = [];
const authType = this.getNodeParameter('authType', 0);
let serviceAccountEmail;
let privateKey;
let impersonateEmail;
if (authType === 'credential') {
const credentials = await this.getCredentials('googleDriveServiceAccountApi');
if (!credentials) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials provided. Please select a credential or switch to Manual authentication.');
}
serviceAccountEmail = credentials.serviceAccountEmail;
privateKey = credentials.privateKey.replace(/\\n/g, '\n');
impersonateEmail = credentials.impersonateEmail || undefined;
}
else {
serviceAccountEmail = this.getNodeParameter('serviceAccountEmail', 0);
privateKey = this.getNodeParameter('privateKey', 0).replace(/\\n/g, '\n');
impersonateEmail = this.getNodeParameter('impersonateEmail', 0) || undefined;
}
// Setup JWT auth with service account
const auth = new googleapis_1.google.auth.JWT({
email: serviceAccountEmail,
key: privateKey,
scopes: ['https://www.googleapis.com/auth/drive'],
subject: impersonateEmail,
});
const drive = googleapis_1.google.drive({ version: 'v3', auth });
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'file') {
if (operation === 'list') {
const options = this.getNodeParameter('options', i);
const params = {
pageSize: options.pageSize || 100,
fields: options.fields || 'files(id, name, mimeType, createdTime, modifiedTime, size, parents)',
};
if (options.q) {
params.q = options.q;
}
if (options.orderBy) {
params.orderBy = options.orderBy;
}
const response = await drive.files.list(params);
const files = response.data.files || [];
files.forEach((file) => {
returnData.push({ json: file });
});
}
else if (operation === 'get') {
const fileId = this.getNodeParameter('fileId', i);
const response = await drive.files.get({
fileId,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'create' || operation === 'upload') {
const fileName = this.getNodeParameter('fileName', i);
const fileContent = this.getNodeParameter('fileContent', i);
const parentFolderId = this.getNodeParameter('parentFolderId', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const fileMetadata = {
name: fileName,
};
if (parentFolderId) {
fileMetadata.parents = [parentFolderId];
}
if (additionalFields.description) {
fileMetadata.description = additionalFields.description;
}
if (additionalFields.mimeType) {
fileMetadata.mimeType = additionalFields.mimeType;
}
const media = {
mimeType: additionalFields.mimeType || 'text/plain',
body: fileContent,
};
const response = await drive.files.create({
requestBody: fileMetadata,
media: media,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'delete') {
const fileId = this.getNodeParameter('fileId', i);
await drive.files.delete({
fileId,
});
returnData.push({ json: { success: true, fileId } });
}
else if (operation === 'download') {
const fileId = this.getNodeParameter('fileId', i);
const response = await drive.files.get({
fileId,
alt: 'media',
}, { responseType: 'text' });
returnData.push({ json: { fileId, content: response.data } });
}
else if (operation === 'update') {
const fileId = this.getNodeParameter('fileId', i);
const updateFields = this.getNodeParameter('updateFields', i);
const fileMetadata = {};
if (updateFields.name) {
fileMetadata.name = updateFields.name;
}
if (updateFields.description !== undefined) {
fileMetadata.description = updateFields.description;
}
if (updateFields.starred !== undefined) {
fileMetadata.starred = updateFields.starred;
}
if (updateFields.trashed !== undefined) {
fileMetadata.trashed = updateFields.trashed;
}
const response = await drive.files.update({
fileId,
requestBody: fileMetadata,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'copy') {
const fileId = this.getNodeParameter('fileId', i);
const newFileName = this.getNodeParameter('newFileName', i);
const fileMetadata = {};
if (newFileName) {
fileMetadata.name = newFileName;
}
const response = await drive.files.copy({
fileId,
requestBody: fileMetadata,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'move') {
const fileId = this.getNodeParameter('fileId', i);
const newParentFolderId = this.getNodeParameter('newParentFolderId', i);
// Get current parents
const file = await drive.files.get({
fileId,
fields: 'parents',
});
const previousParents = ((_a = file.data.parents) === null || _a === void 0 ? void 0 : _a.join(',')) || '';
const response = await drive.files.update({
fileId,
addParents: newParentFolderId,
removeParents: previousParents,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'export') {
const fileId = this.getNodeParameter('fileId', i);
const exportFormat = this.getNodeParameter('exportFormat', i);
const response = await drive.files.export({
fileId,
mimeType: exportFormat,
}, { responseType: 'text' });
// Get file metadata for name
const fileMetadata = await drive.files.get({
fileId,
fields: 'name,mimeType',
});
returnData.push({
json: {
fileId,
fileName: fileMetadata.data.name,
originalMimeType: fileMetadata.data.mimeType,
exportFormat,
content: response.data
}
});
}
else if (operation === 'import') {
const fileName = this.getNodeParameter('fileName', i);
const fileContent = this.getNodeParameter('fileContent', i);
const convertTo = this.getNodeParameter('convertTo', i);
const parentFolderId = this.getNodeParameter('parentFolderId', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const fileMetadata = {
name: fileName,
mimeType: convertTo,
};
if (parentFolderId) {
fileMetadata.parents = [parentFolderId];
}
if (additionalFields.description) {
fileMetadata.description = additionalFields.description;
}
// Determine source MIME type based on conversion target
let sourceMimeType = additionalFields.mimeType || 'text/plain';
const media = {
mimeType: sourceMimeType,
body: fileContent,
};
const response = await drive.files.create({
requestBody: fileMetadata,
media: media,
fields: '*',
});
returnData.push({
json: Object.assign(Object.assign({}, response.data), { convertedTo: convertTo })
});
}
}
else if (resource === 'folder') {
if (operation === 'create') {
const folderName = this.getNodeParameter('folderName', i);
const parentFolderId = this.getNodeParameter('parentFolderId', i);
const fileMetadata = {
name: folderName,
mimeType: 'application/vnd.google-apps.folder',
};
if (parentFolderId) {
fileMetadata.parents = [parentFolderId];
}
const response = await drive.files.create({
requestBody: fileMetadata,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'delete') {
const folderId = this.getNodeParameter('folderId', i);
await drive.files.delete({
fileId: folderId,
});
returnData.push({ json: { success: true, folderId } });
}
else if (operation === 'list') {
const options = this.getNodeParameter('options', i);
const params = {
q: "mimeType='application/vnd.google-apps.folder'",
pageSize: options.pageSize || 100,
fields: options.fields || 'files(id, name, createdTime, modifiedTime, parents)',
};
if (options.q) {
params.q += ` and (${options.q})`;
}
if (options.orderBy) {
params.orderBy = options.orderBy;
}
const response = await drive.files.list(params);
const folders = response.data.files || [];
folders.forEach((folder) => {
returnData.push({ json: folder });
});
}
}
else if (resource === 'permission') {
const fileId = this.getNodeParameter('fileId', i);
if (operation === 'create') {
const role = this.getNodeParameter('role', i);
const permissionType = this.getNodeParameter('permissionType', i);
const permissionOptions = this.getNodeParameter('permissionOptions', i);
const permission = {
type: permissionType,
role,
};
if (permissionType === 'user' || permissionType === 'group') {
const emailAddress = this.getNodeParameter('emailAddress', i);
permission.emailAddress = emailAddress;
}
if (permissionType === 'domain') {
const domain = this.getNodeParameter('domain', i);
permission.domain = domain;
}
const params = {
fileId,
requestBody: permission,
fields: '*',
};
if (permissionOptions.sendNotificationEmail !== undefined) {
params.sendNotificationEmail = permissionOptions.sendNotificationEmail;
}
if (permissionOptions.emailMessage) {
params.emailMessage = permissionOptions.emailMessage;
}
const response = await drive.permissions.create(params);
returnData.push({ json: response.data });
}
else if (operation === 'list') {
const response = await drive.permissions.list({
fileId,
fields: '*',
});
const permissions = response.data.permissions || [];
permissions.forEach((permission) => {
returnData.push({ json: permission });
});
}
else if (operation === 'get') {
const permissionId = this.getNodeParameter('permissionId', i);
const response = await drive.permissions.get({
fileId,
permissionId,
fields: '*',
});
returnData.push({ json: response.data });
}
else if (operation === 'delete') {
const permissionId = this.getNodeParameter('permissionId', i);
await drive.permissions.delete({
fileId,
permissionId,
});
returnData.push({ json: { success: true, permissionId } });
}
else if (operation === 'update') {
const permissionId = this.getNodeParameter('permissionId', i);
const role = this.getNodeParameter('role', i);
const response = await drive.permissions.update({
fileId,
permissionId,
requestBody: {
role,
},
fields: '*',
});
returnData.push({ json: response.data });
}
}
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({ json: { error: error.message } });
continue;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
}
}
return [returnData];
}
}
exports.GoogleDriveServiceAccount = GoogleDriveServiceAccount;
//# sourceMappingURL=GoogleDriveServiceAccount.node.js.map