Refactor email attachment handling and improve error management
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good

This commit refines the `resolveEmailAttachments` function by enhancing error handling for unknown object types and removing unnecessary console logs. It also updates the test suite to mock additional database functions and ensure proper object retrieval during email message processing, improving the robustness of email management features.
This commit is contained in:
Tom Butcher 2026-09-12 21:52:21 +01:00
parent 624a2aefdd
commit 1d473cc755
2 changed files with 18 additions and 11 deletions

View File

@ -30,7 +30,10 @@ jest.unstable_mockModule('../../../database/schemas/misc/usernotifier.schema.js'
userNotifierModel: { create: notifierCreate }, userNotifierModel: { create: notifierCreate },
})); }));
jest.unstable_mockModule('../../../database/database.js', () => ({ jest.unstable_mockModule('../../../database/database.js', () => ({
aggregateRollups: jest.fn(),
aggregateRollupsHistory: jest.fn(),
createFileThumbnails: jest.fn(), createFileThumbnails: jest.fn(),
deleteObject: jest.fn(),
editObject: jest.fn(), editObject: jest.fn(),
getObject: jest.fn(), getObject: jest.fn(),
getModelHistory: jest.fn(), getModelHistory: jest.fn(),
@ -95,7 +98,7 @@ const {
newEmailMessageRouteHandler, newEmailMessageRouteHandler,
processEmailMessage, processEmailMessage,
} = await import('../emailmessages.js'); } = await import('../emailmessages.js');
const { createFileThumbnails, editObject, listObjectsByProperties, newObject } = const { createFileThumbnails, editObject, getObject, listObjectsByProperties, newObject } =
await import('../../../database/database.js'); await import('../../../database/database.js');
const { downloadFile, uploadFile } = await import('../../../database/ceph.js'); const { downloadFile, uploadFile } = await import('../../../database/ceph.js');
const { distributeNew } = await import('../../../utils.js'); const { distributeNew } = await import('../../../utils.js');
@ -215,10 +218,12 @@ describe('email message creation', () => {
it('renders document template attachments into files before sending', async () => { it('renders document template attachments into files before sending', async () => {
const user = { _id: 'user-1' }; const user = { _id: 'user-1' };
const pdfBytes = Buffer.from('pdf-bytes'); const pdfBytes = Buffer.from('pdf-bytes');
const invoice = { _id: 'object-1', name: 'INV-1' };
emailMessageModel.findById.mockReturnValue({ emailMessageModel.findById.mockReturnValue({
lean: jest.fn().mockResolvedValue({ lean: jest.fn().mockResolvedValue({
_id: 'message-1', _id: 'message-1',
emailTemplate: 'template-1', emailTemplate: 'template-1',
objectType: 'invoice',
object: 'object-1', object: 'object-1',
recipientEmail: 'client@example.com', recipientEmail: 'client@example.com',
}), }),
@ -233,6 +238,7 @@ describe('email message creation', () => {
}, },
], ],
}); });
getObject.mockResolvedValue(invoice);
templateManager.renderEmailTemplate.mockResolvedValue({ templateManager.renderEmailTemplate.mockResolvedValue({
subject: 'Invoice', subject: 'Invoice',
html: '<p>Hi</p>', html: '<p>Hi</p>',
@ -253,10 +259,15 @@ describe('email message creation', () => {
user user
); );
expect(getObject).toHaveBeenCalledWith(
expect.objectContaining({
id: 'object-1',
})
);
expect(templateManager.renderDownload).toHaveBeenCalledWith( expect(templateManager.renderDownload).toHaveBeenCalledWith(
'doc-1', 'doc-1',
undefined, undefined,
'object-1', invoice,
'pdf' 'pdf'
); );
expect(newObject).toHaveBeenCalledWith( expect(newObject).toHaveBeenCalledWith(

View File

@ -162,25 +162,24 @@ async function resolveEmailAttachments(emailMessage, user) {
for (const attachment of template.attachments || []) { for (const attachment of template.attachments || []) {
if (attachment.type === 'documentTemplate') { if (attachment.type === 'documentTemplate') {
const templateId = attachment.file?._id ?? attachment.file; const templateId = attachment.file?._id ?? attachment.file;
console.log(templateId);
if (!templateId) throw new Error('Document template attachment is missing a file.'); if (!templateId) throw new Error('Document template attachment is missing a file.');
const model = getModelByName(emailMessage.objectType); const modelEntry = getModelByName(emailMessage.objectType);
if (!modelEntry?.model) {
throw new Error(`Unknown object type for email attachment: ${emailMessage.objectType}`);
}
const object = await getObject({ const object = await getObject({
model: getModelByName(emailMessage.objectType).model, model: modelEntry.model,
id: emailMessage.object?._id || emailMessage.object, id: emailMessage.object?._id || emailMessage.object,
}); });
console.log(object);
const rendered = await templateManager.renderDownload( const rendered = await templateManager.renderDownload(
templateId, templateId,
undefined, undefined,
object, object,
attachment.fileType || 'pdf' attachment.fileType || 'pdf'
); );
console.log(rendered);
if (rendered?.error) throw new Error(rendered.error); if (rendered?.error) throw new Error(rendered.error);
const buffers = rendered.buffers || []; const buffers = rendered.buffers || [];
if (!buffers.length) throw new Error('Document template attachment produced no files.'); if (!buffers.length) throw new Error('Document template attachment produced no files.');
console.log(buffers);
for (let index = 0; index < buffers.length; index += 1) { for (let index = 0; index < buffers.length; index += 1) {
const buffer = Buffer.isBuffer(buffers[index]) const buffer = Buffer.isBuffer(buffers[index])
? buffers[index] ? buffers[index]
@ -275,9 +274,6 @@ export const processEmailMessage = async (id, account, user) => {
const { fileIds, mailAttachments } = await resolveEmailAttachments(emailMessage, user); const { fileIds, mailAttachments } = await resolveEmailAttachments(emailMessage, user);
console.log(fileIds);
console.log(mailAttachments);
await updateEmailMessage( await updateEmailMessage(
id, id,
{ {