fix(Telegram Node): Determine the MIME type when downloading the file (#17725)

This commit is contained in:
RomanDavydchuk
2025-07-28 11:31:15 +03:00
committed by GitHub
parent e1aa60ce6f
commit a9c29e340a
2 changed files with 219 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
import { lookup } from 'mime-types';
import type {
IExecuteFunctions,
IDataObject,
@@ -656,6 +657,31 @@ export class Telegram implements INodeType {
default: true,
description: 'Whether to download the file',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: ['get'],
resource: ['file'],
download: [true],
},
},
default: {},
options: [
{
displayName: 'MIME Type',
name: 'mimeType',
type: 'string',
placeholder: 'image/jpeg',
default: '',
description:
'The MIME type of the file. If not specified, the MIME type will be determined by the file extension.',
},
],
},
// ----------------------------------
// message
@@ -2169,11 +2195,15 @@ export class Telegram implements INodeType {
},
);
const fileName = filePath.split('/').pop();
const fileName = filePath.split('/').pop() as string;
const additionalFields = this.getNodeParameter('additionalFields', 0);
const providedMimeType = additionalFields?.mimeType as string | undefined;
const mimeType = providedMimeType ?? (lookup(fileName) || 'application/octet-stream');
const data = await this.helpers.prepareBinaryData(
file.body as Buffer,
fileName as string,
fileName,
mimeType,
);
returnData.push({

View File

@@ -0,0 +1,187 @@
import { mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions, INode } from 'n8n-workflow';
import * as GenericFunctions from '../GenericFunctions';
import { Telegram } from '../Telegram.node';
describe('Telegram node', () => {
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
const apiRequestSpy = jest.spyOn(GenericFunctions, 'apiRequest');
const node = new Telegram();
beforeEach(() => {
jest.resetAllMocks();
executeFunctionsMock.getCredentials.mockResolvedValue({
baseUrl: 'https://api.telegram.org',
accessToken: 'test-token',
});
executeFunctionsMock.getNode.mockReturnValue({
typeVersion: 1.2,
} as INode);
executeFunctionsMock.getInputData.mockReturnValue([{ json: {} }]);
});
describe('file:get', () => {
beforeEach(() => {
executeFunctionsMock.getNodeParameter.mockImplementation((p) => {
switch (p) {
case 'resource':
return 'file';
case 'operation':
return 'get';
case 'download':
return true;
case 'fileId':
return 'file-id';
default:
return undefined;
}
});
});
it('should determine the mime type of the file', async () => {
apiRequestSpy.mockResolvedValueOnce({
result: {
file_id: 'file-id',
file_path: 'documents/file_1.pdf',
},
});
apiRequestSpy.mockResolvedValueOnce({
body: Buffer.from('test-file'),
});
executeFunctionsMock.helpers.prepareBinaryData.mockResolvedValue({
data: 'test-file',
mimeType: 'application/pdf',
});
const result = await node.execute.call(executeFunctionsMock);
expect(result).toEqual([
[
{
json: {
result: {
file_id: 'file-id',
file_path: 'documents/file_1.pdf',
},
},
binary: {
data: {
data: 'test-file',
mimeType: 'application/pdf',
},
},
pairedItem: { item: 0 },
},
],
]);
expect(executeFunctionsMock.helpers.prepareBinaryData).toHaveBeenCalledWith(
Buffer.from('test-file'),
'file_1.pdf',
'application/pdf',
);
});
it('should fallback to application/octet-stream if the mime type cannot be determined', async () => {
apiRequestSpy.mockResolvedValueOnce({
result: {
file_id: 'file-id',
file_path: 'documents/file_1.foo',
},
});
apiRequestSpy.mockResolvedValueOnce({
body: Buffer.from('test-file'),
});
executeFunctionsMock.helpers.prepareBinaryData.mockResolvedValue({
data: 'test-file',
mimeType: 'application/octet-stream',
});
const result = await node.execute.call(executeFunctionsMock);
expect(result).toEqual([
[
{
json: {
result: {
file_id: 'file-id',
file_path: 'documents/file_1.foo',
},
},
binary: {
data: {
data: 'test-file',
mimeType: 'application/octet-stream',
},
},
pairedItem: { item: 0 },
},
],
]);
expect(executeFunctionsMock.helpers.prepareBinaryData).toHaveBeenCalledWith(
Buffer.from('test-file'),
'file_1.foo',
'application/octet-stream',
);
});
it('should use the provided mime type if it is specified', async () => {
executeFunctionsMock.getNodeParameter.mockImplementation((p) => {
switch (p) {
case 'resource':
return 'file';
case 'operation':
return 'get';
case 'download':
return true;
case 'fileId':
return 'file-id';
case 'additionalFields':
return { mimeType: 'image/jpeg' };
default:
return undefined;
}
});
apiRequestSpy.mockResolvedValueOnce({
result: {
file_id: 'file-id',
file_path: 'documents/file_1.pdf',
},
});
apiRequestSpy.mockResolvedValueOnce({
body: Buffer.from('test-file'),
});
executeFunctionsMock.helpers.prepareBinaryData.mockResolvedValue({
data: 'test-file',
mimeType: 'image/jpeg',
});
const result = await node.execute.call(executeFunctionsMock);
expect(result).toEqual([
[
{
json: {
result: {
file_id: 'file-id',
file_path: 'documents/file_1.pdf',
},
},
binary: {
data: {
data: 'test-file',
mimeType: 'image/jpeg',
},
},
pairedItem: { item: 0 },
},
],
]);
expect(executeFunctionsMock.helpers.prepareBinaryData).toHaveBeenCalledWith(
Buffer.from('test-file'),
'file_1.pdf',
'image/jpeg',
);
});
});
});