diff --git a/.changeset/popular-pumpkins-relax.md b/.changeset/popular-pumpkins-relax.md new file mode 100644 index 0000000000000..545b3ae4c0db8 --- /dev/null +++ b/.changeset/popular-pumpkins-relax.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/apps-engine': patch +'@rocket.chat/meteor': patch +--- + +Fixes an issue with error handling where errors were not properly propagated to integrated apps. diff --git a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyCreator.ts b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyCreator.ts index f4509990edc99..e209cb44ea8ef 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyCreator.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyCreator.ts @@ -60,6 +60,12 @@ export class ModifyCreator implements IModifyCreator { }) .then((response) => response.result) .catch((err) => { + if (err instanceof Error) { + throw err; + } + if (err?.error?.message) { + throw new Error(err.error.message); + } throw new Error(err.error); }); }, @@ -82,6 +88,12 @@ export class ModifyCreator implements IModifyCreator { }) .then((response) => response.result) .catch((err) => { + if (err instanceof Error) { + throw err; + } + if (err?.error?.message) { + throw new Error(err.error.message); + } throw new Error(err.error); }), }, @@ -92,7 +104,7 @@ export class ModifyCreator implements IModifyCreator { return new Proxy( { __kind: 'getEmailCreator' }, { - get: (_target: unknown, prop: string) => + get: (_target: unknown, prop: string) => (...params: unknown[]) => prop === 'toJSON' ? {} @@ -102,6 +114,12 @@ export class ModifyCreator implements IModifyCreator { }) .then((response) => response.result) .catch((err) => { + if (err instanceof Error) { + throw err; + } + if (err?.error?.message) { + throw new Error(err.error.message); + } throw new Error(err.error); }), } @@ -112,7 +130,7 @@ export class ModifyCreator implements IModifyCreator { return new Proxy( { __kind: 'getContactCreator' }, { - get: (_target: unknown, prop: string) => + get: (_target: unknown, prop: string) => (...params: unknown[]) => prop === 'toJSON' ? {} @@ -122,6 +140,12 @@ export class ModifyCreator implements IModifyCreator { }) .then((response) => response.result) .catch((err) => { + if (err instanceof Error) { + throw err; + } + if (err?.error?.message) { + throw new Error(err.error.message); + } throw new Error(err.error); }), } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyCreator.test.ts b/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyCreator.test.ts index 5927869e6c84f..f3d83b32e4e9d 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyCreator.test.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyCreator.test.ts @@ -1,7 +1,7 @@ // deno-lint-ignore-file no-explicit-any import { afterAll, beforeEach, describe, it } from 'https://deno.land/std@0.203.0/testing/bdd.ts'; import { assertSpyCall, spy } from 'https://deno.land/std@0.203.0/testing/mock.ts'; -import { assert, assertEquals, assertNotInstanceOf } from 'https://deno.land/std@0.203.0/assert/mod.ts'; +import { assert, assertEquals, assertNotInstanceOf, assertRejects } from 'https://deno.land/std@0.203.0/assert/mod.ts'; import { AppObjectRegistry } from '../../../AppObjectRegistry.ts'; import { ModifyCreator } from '../modify/ModifyCreator.ts'; @@ -103,4 +103,157 @@ describe('ModifyCreator', () => { assertNotInstanceOf(result, Promise); assert(typeof result === 'string', `Expected "${result}" to be of type "string", but got "${typeof result}"`); }); + + it('throws an error when a proxy method of getLivechatCreator fails', async () => { + const failingSenderFn = () => Promise.reject(new Error('Test error')); + const modifyCreator = new ModifyCreator(failingSenderFn); + const livechatCreator = modifyCreator.getLivechatCreator(); + + await assertRejects( + () => + livechatCreator.createAndReturnVisitor({ + token: 'visitor-token', + username: 'visitor-username', + name: 'Visitor Name', + }), + Error, + 'Test error', + ); + }); + + it('throws an instance of Error when getLivechatCreator fails with a specific error object', async () => { + const failingSenderFn = () => Promise.reject({ error: { message: 'Livechat method error' } }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const livechatCreator = modifyCreator.getLivechatCreator(); + + await assertRejects( + () => + livechatCreator.createVisitor({ + token: 'visitor-token', + username: 'visitor-username', + name: 'Visitor Name', + }), + Error, + 'Livechat method error', + ); + }); + + it('throws a default Error when getLivechatCreator fails with an unknown error object', async () => { + const failingSenderFn = () => Promise.reject({ error: {} }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const livechatCreator = modifyCreator.getLivechatCreator(); + + await assertRejects( + () => + livechatCreator.createVisitor({ + token: 'visitor-token', + username: 'visitor-username', + name: 'Visitor Name', + }), + Error, + '[object Object]', + ); + }); + + it('throws an error when a proxy method of getUploadCreator fails', async () => { + const failingSenderFn = () => Promise.reject(new Error('Upload error')); + const modifyCreator = new ModifyCreator(failingSenderFn); + const uploadCreator = modifyCreator.getUploadCreator(); + + await assertRejects(() => uploadCreator.uploadBuffer(new Uint8Array([9, 10, 11, 12]), 'image/png'), Error, 'Upload error'); + }); + + it('throws an instance of Error when getUploadCreator fails with a specific error object', async () => { + const failingSenderFn = () => Promise.reject({ error: { message: 'Upload method error' } }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const uploadCreator = modifyCreator.getUploadCreator(); + + await assertRejects(() => uploadCreator.uploadBuffer(new Uint8Array([1, 2, 3]), 'image/png'), Error, 'Upload method error'); + }); + + it('throws a default Error when getUploadCreator fails with an unknown error object', async () => { + const failingSenderFn = () => Promise.reject({ error: {} }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const uploadCreator = modifyCreator.getUploadCreator(); + + await assertRejects(() => uploadCreator.uploadBuffer(new Uint8Array([1, 2, 3]), 'image/png'), Error, '[object Object]'); + }); + + it('throws an error when a proxy method of getEmailCreator fails', async () => { + const failingSenderFn = () => Promise.reject(new Error('Email error')); + const modifyCreator = new ModifyCreator(failingSenderFn); + const emailCreator = modifyCreator.getEmailCreator(); + + await assertRejects( + () => + emailCreator.send({ + to: 'test@example.com', + from: 'sender@example.com', + subject: 'Test Email', + text: 'This is a test email.', + }), + Error, + 'Email error', + ); + }); + + it('throws an instance of Error when getEmailCreator fails with a specific error object', async () => { + const failingSenderFn = () => Promise.reject({ error: { message: 'Email method error' } }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const emailCreator = modifyCreator.getEmailCreator(); + + await assertRejects( + () => + emailCreator.send({ + to: 'test@example.com', + from: 'sender@example.com', + subject: 'Test Email', + text: 'This is a test email.', + }), + Error, + 'Email method error', + ); + }); + + it('throws a default Error when getEmailCreator fails with an unknown error object', async () => { + const failingSenderFn = () => Promise.reject({ error: {} }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const emailCreator = modifyCreator.getEmailCreator(); + + await assertRejects( + () => + emailCreator.send({ + to: 'test@example.com', + from: 'sender@example.com', + subject: 'Test Email', + text: 'This is a test email.', + }), + Error, + '[object Object]', + ); + }); + + it('throws an error when a proxy method of getContactCreator fails', async () => { + const failingSenderFn = () => Promise.reject(new Error('Contact creation error')); + const modifyCreator = new ModifyCreator(failingSenderFn); + const contactCreator = modifyCreator.getContactCreator(); + + await assertRejects(() => contactCreator.addContactEmail('test-contact-id', 'test@example.com'), Error, 'Contact creation error'); + }); + + it('throws an instance of Error when getContactCreator fails with a specific error object', async () => { + const failingSenderFn = () => Promise.reject({ error: { message: 'Contact creation error' } }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const contactCreator = modifyCreator.getContactCreator(); + + await assertRejects(() => contactCreator.addContactEmail('test-contact-id', 'test@example.com'), Error, 'Contact creation error'); + }); + + it('throws a default Error when getContactCreator fails with an unknown error object', async () => { + const failingSenderFn = () => Promise.reject({ error: {} }); + const modifyCreator = new ModifyCreator(failingSenderFn); + const contactCreator = modifyCreator.getContactCreator(); + + await assertRejects(() => contactCreator.addContactEmail('test-contact-id', 'test@example.com'), Error, '[object Object]'); + }); });