Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9efc199
fix: refactor function handler for follow up auto ack behavior
WilliamBergamin Feb 14, 2025
5c5e84a
Add more unit test to cover function scoped actions
WilliamBergamin Feb 17, 2025
8422fff
Merge branch 'main' into refactor-function-listener-for-future-auto-a…
WilliamBergamin Feb 17, 2025
d61f2cd
Merge branch 'main' into refactor-function-listener-for-future-auto-a…
WilliamBergamin Apr 28, 2025
24f5dbb
fix: always attach the function bot access token to the context if av…
zimeg Apr 29, 2025
6b0c3b9
Update test/types/action.test-d.ts
WilliamBergamin Apr 29, 2025
32f698f
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
0d7a953
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
46ebf61
Add a TODO for create say
WilliamBergamin Apr 29, 2025
3b4342d
Update src/App.ts
WilliamBergamin Apr 29, 2025
28c73d8
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
425c963
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
07ef57a
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
a602dc6
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
70deb4f
fix imports
WilliamBergamin Apr 29, 2025
f9b2fae
Update src/App.ts
WilliamBergamin Apr 29, 2025
0ba5ea8
Improve client pool behavior
WilliamBergamin Apr 29, 2025
9e6ef71
depricate types instead of removing them
WilliamBergamin Apr 29, 2025
a01e3f1
Update src/CustomFunction.ts
WilliamBergamin Apr 29, 2025
4e60cfa
Merge branch 'refactor-function-listener-for-future-auto-ack-changes'…
WilliamBergamin Apr 29, 2025
5d64700
chore: merge w main
zimeg Apr 30, 2025
96967f7
chore: merge w main
zimeg May 2, 2025
090a7c4
test: confirm function executed event details are added to the context
zimeg May 3, 2025
790cd9f
chore: merge w main
zimeg May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -969,10 +969,6 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
if (functionInputs) {
context.functionInputs = functionInputs;
}
}

// Attach and make available the JIT/function-related token on context
if (this.attachFunctionToken) {
if (functionBotAccessToken) {
context.functionBotAccessToken = functionBotAccessToken;
}
Expand Down
88 changes: 88 additions & 0 deletions test/unit/App/middlewares/arguments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type Override,
createDummyAppMentionEventMiddlewareArgs,
createDummyBlockActionEventMiddlewareArgs,
createDummyCustomFunctionMiddlewareArgs,
createDummyMessageEventMiddlewareArgs,
createDummyReceiverEvent,
createDummyViewSubmissionMiddlewareArgs,
Expand Down Expand Up @@ -761,5 +762,92 @@ describe('App middleware and listener arguments', () => {

sinon.assert.calledOnce(fakeAck);
});

it('should have function executed event details from a custom step payload', async () => {
const fakeAxiosPost = sinon.fake.resolves({});
overrides = buildOverrides([withNoopWebClient(), withAxiosPost(fakeAxiosPost)]);
const MockApp = await importApp(overrides);
const callbackId = 'reverse_string';
const functionBotAccessToken = 'xwfp-example';
const functionExecutionId = 'Fx1234567890';
const inputs = {
numerics: 12,
stringToReverse: 'palindrome',
trimmedSpaces: false,
};

const app = new MockApp({
attachFunctionToken: false, // https://github.com/slackapi/bolt-js/pull/2513
receiver: fakeReceiver,
authorize: sinon.fake.resolves(dummyAuthorizationResult),
});

let called = false;
app.function(callbackId, async ({ context }) => {
assert.strictEqual(context.functionExecutionId, functionExecutionId);
assert.strictEqual(context.functionBotAccessToken, functionBotAccessToken);
Comment on lines +787 to +788
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.strictEqual(context.functionExecutionId, functionExecutionId);
assert.strictEqual(context.functionBotAccessToken, functionBotAccessToken);
assert.strictEqual(context.functionExecutionId, functionExecutionId);
assert.strictEqual(context.functionBotAccessToken, functionBotAccessToken);
assert.deepStrictEqual(context.functionInputs, inputs);

📣 Here we are not testing that functionInputs are added to context but I'm not sure if this is something we want to change in this PR?

It's not clear to me if this should be included following L1624 from body.event.inputs for convenience:

bolt-js/src/App.ts

Lines 1616 to 1635 in 76140ed

function extractFunctionContext(body: StringIndexed) {
let functionExecutionId: string | undefined = undefined;
let functionBotAccessToken: string | undefined = undefined;
let functionInputs: FunctionInputs | undefined = undefined;
// function_executed event
if (body.event && body.event.type === 'function_executed' && body.event.function_execution_id) {
functionExecutionId = body.event.function_execution_id;
functionBotAccessToken = body.event.bot_access_token;
}
// interactivity (block_actions)
if (body.function_data) {
functionExecutionId = body.function_data.execution_id;
functionBotAccessToken = body.bot_access_token;
functionInputs = body.function_data.inputs;
}
return { functionExecutionId, functionBotAccessToken, functionInputs };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If adding a test for functionInputs in the context is a small change then we can do it in this PR 💯 but if it is larger then maybe we do it in another PR

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thinking. I'm finding this change involves an update to another file which does seem outside the scope of this PR 🫡

called = true;
});
app.error(fakeErrorHandler);

await fakeReceiver.sendEvent({
...createDummyCustomFunctionMiddlewareArgs({
callbackId,
functionBotAccessToken,
functionExecutionId,
inputs,
}),
ack: fakeAck,
});

assert.isTrue(called);
sinon.assert.calledOnce(fakeAck);
});

it('should have function executed event details from a block actions payload', async () => {
const fakeAxiosPost = sinon.fake.resolves({});
overrides = buildOverrides([withNoopWebClient(), withAxiosPost(fakeAxiosPost)]);
const MockApp = await importApp(overrides);
const callbackId = 'reverse_string_button';
const functionBotAccessToken = 'xwfp-example';
const functionExecutionId = 'Fx1234567890';
const inputs = {
numerics: 12,
stringToReverse: 'palindrome',
trimmedSpaces: false,
};

const app = new MockApp({
attachFunctionToken: false, // https://github.com/slackapi/bolt-js/pull/2513
receiver: fakeReceiver,
authorize: sinon.fake.resolves(dummyAuthorizationResult),
});

app.action(callbackId, async ({ ack, context }) => {
assert.strictEqual(context.functionExecutionId, functionExecutionId);
assert.strictEqual(context.functionBotAccessToken, functionBotAccessToken);
assert.deepStrictEqual(context.functionInputs, inputs);
await ack();
});
app.error(fakeErrorHandler);

await fakeReceiver.sendEvent({
...createDummyBlockActionEventMiddlewareArgs(
{
action_id: callbackId,
},
{
bot_access_token: functionBotAccessToken,
function_data: {
execution_id: functionExecutionId,
inputs,
},
},
),
ack: fakeAck,
});

sinon.assert.calledOnce(fakeAck);
});
});
});
22 changes: 16 additions & 6 deletions test/unit/helpers/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,23 @@ export function createDummyCommandMiddlewareArgs(commandOverrides?: DummyCommand
};
}

interface DummyCustomFunctionOverride {
callbackId?: string;
functionBotAccessToken?: string;
functionExecutionId?: string;
inputs?: Record<string, string | number | boolean>;
}
export function createDummyCustomFunctionMiddlewareArgs(
functionOverrides: {
callbackId?: string;
inputs?: Record<string, string | number | boolean>;
} = { callbackId: 'reverse', inputs: { stringToReverse: 'hello' } },
functionOverrides: DummyCustomFunctionOverride = {
callbackId: 'reverse',
functionBotAccessToken: 'xwfp-valid',
functionExecutionId: 'Fx111',
inputs: { stringToReverse: 'hello' },
},
): SlackCustomFunctionMiddlewareArgs {
functionOverrides.callbackId = functionOverrides.callbackId || 'reverse';
functionOverrides.functionBotAccessToken = functionOverrides.functionBotAccessToken || 'xwfp-valid';
functionOverrides.functionExecutionId = functionOverrides.functionExecutionId || 'Fx111';
functionOverrides.inputs = functionOverrides.inputs ? functionOverrides.inputs : { stringToReverse: 'hello' };
const testFunction = {
id: 'Fn111',
Expand Down Expand Up @@ -345,10 +355,10 @@ export function createDummyCustomFunctionMiddlewareArgs(
type: 'function_executed',
function: testFunction,
inputs: functionOverrides.inputs,
function_execution_id: 'Fx111',
function_execution_id: functionOverrides.functionExecutionId,
workflow_execution_id: 'Wf111',
event_ts: '1659055013.509853',
bot_access_token: 'xwfp-valid',
bot_access_token: functionOverrides.functionBotAccessToken,
} as const;

const body = {
Expand Down