mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(GoogleDrive Node): Fix google service accounts uploading to shared drives (#18952)
This commit is contained in:
@@ -61,9 +61,14 @@ describe('test GoogleDriveV2: file createFromText', () => {
|
|||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
expect.anything(), // Buffer of content goes here
|
expect.anything(), // Buffer of content goes here
|
||||||
{ uploadType: 'media' },
|
{ uploadType: 'multipart', supportsAllDrives: true },
|
||||||
undefined,
|
undefined,
|
||||||
{ headers: { 'Content-Length': 12, 'Content-Type': 'text/plain' } },
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Length': 503,
|
||||||
|
'Content-Type': expect.stringMatching(/^multipart\/related; boundary=(\\S)*/),
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import * as utils from '../../../../v2/helpers/utils';
|
|||||||
import * as transport from '../../../../v2/transport';
|
import * as transport from '../../../../v2/transport';
|
||||||
import { createMockExecuteFunction, createTestStream, driveNode } from '../helpers';
|
import { createMockExecuteFunction, createTestStream, driveNode } from '../helpers';
|
||||||
|
|
||||||
|
const fileContent = Buffer.from('Hello Drive!');
|
||||||
|
const originalFilename = 'original.txt';
|
||||||
|
const contentLength = 123;
|
||||||
|
const mimeType = 'text/plain';
|
||||||
|
|
||||||
jest.mock('../../../../v2/transport', () => {
|
jest.mock('../../../../v2/transport', () => {
|
||||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||||
return {
|
return {
|
||||||
@@ -26,10 +31,10 @@ jest.mock('../../../../v2/helpers/utils', () => {
|
|||||||
...originalModule,
|
...originalModule,
|
||||||
getItemBinaryData: jest.fn(async function () {
|
getItemBinaryData: jest.fn(async function () {
|
||||||
return {
|
return {
|
||||||
contentLength: '123',
|
contentLength,
|
||||||
fileContent: Buffer.from('Hello Drive!'),
|
fileContent,
|
||||||
originalFilename: 'original.txt',
|
originalFilename,
|
||||||
mimeType: 'text/plain',
|
mimeType,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -41,11 +46,13 @@ describe('test GoogleDriveV2: file upload', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should upload buffers', async () => {
|
it('should upload buffers', async () => {
|
||||||
|
const name = 'newFile.txt';
|
||||||
|
const parent = 'folderIDxxxxxx';
|
||||||
const nodeParameters = {
|
const nodeParameters = {
|
||||||
name: 'newFile.txt',
|
name,
|
||||||
folderId: {
|
folderId: {
|
||||||
__rl: true,
|
__rl: true,
|
||||||
value: 'folderIDxxxxxx',
|
value: parent,
|
||||||
mode: 'list',
|
mode: 'list',
|
||||||
cachedResultName: 'testFolder 3',
|
cachedResultName: 'testFolder 3',
|
||||||
cachedResultUrl: 'https://drive.google.com/drive/folders/folderIDxxxxxx',
|
cachedResultUrl: 'https://drive.google.com/drive/folders/folderIDxxxxxx',
|
||||||
@@ -65,16 +72,21 @@ describe('test GoogleDriveV2: file upload', () => {
|
|||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
expect.any(Buffer),
|
expect.any(Buffer),
|
||||||
{ uploadType: 'media' },
|
{ uploadType: 'multipart', supportsAllDrives: true },
|
||||||
undefined,
|
undefined,
|
||||||
{ headers: { 'Content-Length': '123', 'Content-Type': 'text/plain' } },
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Length': 498,
|
||||||
|
'Content-Type': expect.stringMatching(/^multipart\/related; boundary=(\\S)*/),
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
'/drive/v3/files/undefined',
|
'/drive/v3/files/undefined',
|
||||||
{ mimeType: 'text/plain', name: 'newFile.txt', originalFilename: 'original.txt' },
|
{ mimeType, name, originalFilename },
|
||||||
{
|
{
|
||||||
addParents: 'folderIDxxxxxx',
|
addParents: parent,
|
||||||
supportsAllDrives: true,
|
supportsAllDrives: true,
|
||||||
corpora: 'allDrives',
|
corpora: 'allDrives',
|
||||||
includeItemsFromAllDrives: true,
|
includeItemsFromAllDrives: true,
|
||||||
@@ -87,11 +99,15 @@ describe('test GoogleDriveV2: file upload', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should stream large files in 2MB chunks', async () => {
|
it('should stream large files in 2MB chunks', async () => {
|
||||||
|
const name = 'newFile.jpg';
|
||||||
|
const parent = 'folderIDxxxxxx';
|
||||||
|
const originalFilename = 'test.jpg';
|
||||||
|
const mimeType = 'image/jpg';
|
||||||
const nodeParameters = {
|
const nodeParameters = {
|
||||||
name: 'newFile.jpg',
|
name,
|
||||||
folderId: {
|
folderId: {
|
||||||
__rl: true,
|
__rl: true,
|
||||||
value: 'folderIDxxxxxx',
|
value: parent,
|
||||||
mode: 'list',
|
mode: 'list',
|
||||||
cachedResultName: 'testFolder 3',
|
cachedResultName: 'testFolder 3',
|
||||||
cachedResultUrl: 'https://drive.google.com/drive/folders/folderIDxxxxxx',
|
cachedResultUrl: 'https://drive.google.com/drive/folders/folderIDxxxxxx',
|
||||||
@@ -106,8 +122,8 @@ describe('test GoogleDriveV2: file upload', () => {
|
|||||||
|
|
||||||
const fileSize = 7 * 1024 * 1024; // 7MB
|
const fileSize = 7 * 1024 * 1024; // 7MB
|
||||||
jest.mocked(utils.getItemBinaryData).mockResolvedValue({
|
jest.mocked(utils.getItemBinaryData).mockResolvedValue({
|
||||||
mimeType: 'image/jpg',
|
mimeType,
|
||||||
originalFilename: 'test.jpg',
|
originalFilename,
|
||||||
contentLength: fileSize,
|
contentLength: fileSize,
|
||||||
fileContent: createTestStream(fileSize),
|
fileContent: createTestStream(fileSize),
|
||||||
});
|
});
|
||||||
@@ -123,17 +139,17 @@ describe('test GoogleDriveV2: file upload', () => {
|
|||||||
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
undefined,
|
{ name, parents: [parent] },
|
||||||
{ uploadType: 'resumable' },
|
{ uploadType: 'resumable', supportsAllDrives: true },
|
||||||
undefined,
|
undefined,
|
||||||
{ returnFullResponse: true, headers: { 'X-Upload-Content-Type': 'image/jpg' } },
|
{ returnFullResponse: true, headers: { 'X-Upload-Content-Type': 'image/jpg' } },
|
||||||
);
|
);
|
||||||
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
expect(transport.googleApiRequest).toHaveBeenCalledWith(
|
||||||
'PATCH',
|
'PATCH',
|
||||||
'/drive/v3/files/undefined',
|
'/drive/v3/files/undefined',
|
||||||
{ mimeType: 'image/jpg', name: 'newFile.jpg', originalFilename: 'test.jpg' },
|
{ mimeType, name, originalFilename },
|
||||||
{
|
{
|
||||||
addParents: 'folderIDxxxxxx',
|
addParents: parent,
|
||||||
supportsAllDrives: true,
|
supportsAllDrives: true,
|
||||||
corpora: 'allDrives',
|
corpora: 'allDrives',
|
||||||
includeItemsFromAllDrives: true,
|
includeItemsFromAllDrives: true,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { setFileProperties, setParentFolder, setUpdateCommonParams } from '../..
|
|||||||
import { googleApiRequest } from '../../transport';
|
import { googleApiRequest } from '../../transport';
|
||||||
import { driveRLC, folderRLC, updateCommonOptions } from '../common.descriptions';
|
import { driveRLC, folderRLC, updateCommonOptions } from '../common.descriptions';
|
||||||
|
|
||||||
|
import FormData from 'form-data';
|
||||||
|
|
||||||
const properties: INodeProperties[] = [
|
const properties: INodeProperties[] = [
|
||||||
{
|
{
|
||||||
displayName: 'File Content',
|
displayName: 'File Content',
|
||||||
@@ -88,14 +90,13 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
|
||||||
const bodyParameters = setFileProperties(
|
const metadata = {
|
||||||
{
|
name,
|
||||||
name,
|
parents: [setParentFolder(folderId, driveId)],
|
||||||
parents: [setParentFolder(folderId, driveId)],
|
mimeType,
|
||||||
mimeType,
|
};
|
||||||
},
|
|
||||||
options,
|
const bodyParameters = setFileProperties(metadata, options);
|
||||||
);
|
|
||||||
|
|
||||||
const qs = setUpdateCommonParams(
|
const qs = setUpdateCommonParams(
|
||||||
{
|
{
|
||||||
@@ -146,19 +147,29 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||||||
const content = Buffer.from(this.getNodeParameter('content', i, '') as string, 'utf8');
|
const content = Buffer.from(this.getNodeParameter('content', i, '') as string, 'utf8');
|
||||||
const contentLength = content.byteLength;
|
const contentLength = content.byteLength;
|
||||||
|
|
||||||
|
const multiPartBody = new FormData();
|
||||||
|
multiPartBody.append('metadata', JSON.stringify(metadata), {
|
||||||
|
contentType: 'application/json',
|
||||||
|
});
|
||||||
|
multiPartBody.append('data', content, {
|
||||||
|
contentType: mimeType,
|
||||||
|
knownLength: contentLength,
|
||||||
|
});
|
||||||
|
|
||||||
const uploadData = await googleApiRequest.call(
|
const uploadData = await googleApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
content,
|
multiPartBody.getBuffer(),
|
||||||
{
|
{
|
||||||
uploadType: 'media',
|
uploadType: 'multipart',
|
||||||
|
supportsAllDrives: true,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': mimeType,
|
'Content-Type': `multipart/related; boundary=${multiPartBody.getBoundary()}`,
|
||||||
'Content-Length': contentLength,
|
'Content-Length': multiPartBody.getLengthSync(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import FormData from 'form-data';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
@@ -97,20 +99,34 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||||||
}) as string;
|
}) as string;
|
||||||
|
|
||||||
let uploadId;
|
let uploadId;
|
||||||
|
const metadata = {
|
||||||
|
name,
|
||||||
|
parents: [setParentFolder(folderId, driveId)],
|
||||||
|
};
|
||||||
if (Buffer.isBuffer(fileContent)) {
|
if (Buffer.isBuffer(fileContent)) {
|
||||||
|
const multiPartBody = new FormData();
|
||||||
|
multiPartBody.append('metadata', JSON.stringify(metadata), {
|
||||||
|
contentType: 'application/json',
|
||||||
|
});
|
||||||
|
multiPartBody.append('data', fileContent, {
|
||||||
|
contentType: mimeType,
|
||||||
|
knownLength: contentLength,
|
||||||
|
});
|
||||||
|
|
||||||
const response = await googleApiRequest.call(
|
const response = await googleApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
fileContent,
|
multiPartBody.getBuffer(),
|
||||||
{
|
{
|
||||||
uploadType: 'media',
|
uploadType: 'multipart',
|
||||||
|
supportsAllDrives: true,
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': mimeType,
|
'Content-Type': `multipart/related; boundary=${multiPartBody.getBoundary()}`,
|
||||||
'Content-Length': contentLength,
|
'Content-Length': multiPartBody.getLengthSync(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -121,8 +137,11 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
'/upload/drive/v3/files',
|
'/upload/drive/v3/files',
|
||||||
undefined,
|
metadata,
|
||||||
{ uploadType: 'resumable' },
|
{
|
||||||
|
uploadType: 'resumable',
|
||||||
|
supportsAllDrives: true,
|
||||||
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
returnFullResponse: true,
|
returnFullResponse: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user