diff --git a/changelog.d/818.bugfix b/changelog.d/818.bugfix new file mode 100644 index 00000000..f15675c8 --- /dev/null +++ b/changelog.d/818.bugfix @@ -0,0 +1 @@ +Fix transformation scripts breaking if they include a `return` at the top level diff --git a/src/Connections/GenericHook.ts b/src/Connections/GenericHook.ts index 2d02b756..37275eb0 100644 --- a/src/Connections/GenericHook.ts +++ b/src/Connections/GenericHook.ts @@ -332,7 +332,7 @@ export class GenericHookConnection extends BaseConnection implements IConnection ctx.runtime.setInterruptHandler(shouldInterruptAfterDeadline(Date.now() + TRANSFORMATION_TIMEOUT_MS)); try { ctx.setProp(ctx.global, 'HookshotApiVersion', ctx.newString('v2')); - const ctxResult = ctx.evalCode(`const data = ${JSON.stringify(data)};\n\n${this.state.transformationFunction}`); + const ctxResult = ctx.evalCode(`const data = ${JSON.stringify(data)};\n(() => { ${this.state.transformationFunction} })();`); if (ctxResult.error) { const e = Error(`Transformation failed to run: ${JSON.stringify(ctx.dump(ctxResult.error))}`); diff --git a/tests/connections/GenericHookTest.ts b/tests/connections/GenericHookTest.ts index f0c82ec1..3832c7f0 100644 --- a/tests/connections/GenericHookTest.ts +++ b/tests/connections/GenericHookTest.ts @@ -11,6 +11,7 @@ const ROOM_ID = "!foo:bar"; const V1TFFunction = "result = `The answer to '${data.question}' is ${data.answer}`;"; const V2TFFunction = "result = {plain: `The answer to '${data.question}' is ${data.answer}`, version: 'v2'}"; +const V2TFFunctionWithReturn = "result = {plain: `The answer to '${data.question}' is ${data.answer}`, version: 'v2'}; return;"; async function testSimpleWebhook(connection: GenericHookConnection, mq: LocalMQ, testValue: string) { const webhookData = {simple: testValue}; @@ -199,6 +200,29 @@ describe("GenericHookConnection", () => { type: 'm.room.message', }); }); + it("will handle a hook event with a top-level return", async () => { + const webhookData = {question: 'What is the meaning of life?', answer: 42}; + const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V2TFFunctionWithReturn}, { + enabled: true, + urlPrefix: "https://example.com/webhookurl", + allowJsTransformationFunctions: true, + } + ); + const messagePromise = handleMessage(mq); + await connection.onGenericHook(webhookData); + expect(await messagePromise).to.deep.equal({ + roomId: ROOM_ID, + sender: connection.getUserId(), + content: { + body: "The answer to 'What is the meaning of life?' is 42", + format: "org.matrix.custom.html", + formatted_body: "
The answer to 'What is the meaning of life?' is 42
", + msgtype: "m.notice", + "uk.half-shot.hookshot.webhook_data": webhookData, + }, + type: 'm.room.message', + }); + }); it("will fail to handle a webhook with an invalid script", async () => { const webhookData = {question: 'What is the meaning of life?', answer: 42}; const [connection, mq] = createGenericHook({name: 'test', transformationFunction: "bibble bobble"}, {