Skip to content

Commit 4d59373

Browse files
committed
Mattermost: refresh interaction callback URL cache
1 parent 9d8950a commit 4d59373

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

extensions/mattermost/src/mattermost/interactions.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { resolveMattermostAccount } from "./accounts.js";
55
import type { MattermostClient } from "./client.js";
66
import {
77
buildButtonAttachments,
8+
computeInteractionCallbackUrl,
89
createMattermostInteractionHandler,
910
generateInteractionToken,
1011
getInteractionCallbackUrl,
@@ -136,14 +137,24 @@ describe("callback URL registry", () => {
136137

137138
describe("resolveInteractionCallbackUrl", () => {
138139
afterEach(() => {
139-
setInteractionCallbackUrl("resolve-test", "");
140+
for (const accountId of ["cached", "default", "acct", "myaccount"]) {
141+
setInteractionCallbackUrl(accountId, "");
142+
}
140143
});
141144

142145
it("prefers cached URL from registry", () => {
143146
setInteractionCallbackUrl("cached", "http://cached:1234/path");
144147
expect(resolveInteractionCallbackUrl("cached")).toBe("http://cached:1234/path");
145148
});
146149

150+
it("recomputes from config when bypassing the cache explicitly", () => {
151+
setInteractionCallbackUrl("acct", "http://cached:1234/path");
152+
const url = computeInteractionCallbackUrl("acct", {
153+
gateway: { port: 9999, customBindHost: "gateway.internal" },
154+
});
155+
expect(url).toBe("http://gateway.internal:9999/mattermost/interactions/acct");
156+
});
157+
147158
it("uses interactions.callbackBaseUrl when configured", () => {
148159
const url = resolveInteractionCallbackUrl("default", {
149160
channels: {

extensions/mattermost/src/mattermost/interactions.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,12 @@ function normalizeCallbackBaseUrl(baseUrl: string): string {
6767

6868
/**
6969
* Resolve the interaction callback URL for an account.
70-
* Prefers the in-memory registered URL (set by the gateway monitor).
7170
* Falls back to computing it from interactions.callbackBaseUrl or gateway host config.
7271
*/
73-
export function resolveInteractionCallbackUrl(
72+
export function computeInteractionCallbackUrl(
7473
accountId: string,
7574
cfg?: InteractionCallbackConfig,
7675
): string {
77-
const cached = callbackUrls.get(accountId);
78-
if (cached) {
79-
return cached;
80-
}
8176
const path = resolveInteractionCallbackPath(accountId);
8277
// Prefer merged per-account config when available, but keep the top-level path for
8378
// callers/tests that still pass the root Mattermost config shape directly.
@@ -101,6 +96,22 @@ export function resolveInteractionCallbackUrl(
10196
return `http://${host}:${port}${path}`;
10297
}
10398

99+
/**
100+
* Resolve the interaction callback URL for an account.
101+
* Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the
102+
* monitor lifecycle can reuse the runtime-validated callback destination.
103+
*/
104+
export function resolveInteractionCallbackUrl(
105+
accountId: string,
106+
cfg?: InteractionCallbackConfig,
107+
): string {
108+
const cached = callbackUrls.get(accountId);
109+
if (cached) {
110+
return cached;
111+
}
112+
return computeInteractionCallbackUrl(accountId, cfg);
113+
}
114+
104115
// ── HMAC token management ──────────────────────────────────────────────
105116
// Secret is derived from the bot token so it's stable across CLI and gateway processes.
106117

extensions/mattermost/src/mattermost/monitor.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ import {
4444
type MattermostUser,
4545
} from "./client.js";
4646
import {
47+
computeInteractionCallbackUrl,
4748
createMattermostInteractionHandler,
48-
resolveInteractionCallbackUrl,
4949
resolveInteractionCallbackPath,
5050
setInteractionCallbackUrl,
5151
setInteractionSecret,
@@ -456,7 +456,9 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
456456
// Register HTTP callback endpoint for interactive button clicks.
457457
// Mattermost POSTs to this URL when a user clicks a button action.
458458
const interactionPath = resolveInteractionCallbackPath(account.accountId);
459-
const callbackUrl = resolveInteractionCallbackUrl(account.accountId, {
459+
// Recompute from config on each monitor start so reconnects or config reloads can refresh the
460+
// cached callback URL for downstream callers such as `message action=send`.
461+
const callbackUrl = computeInteractionCallbackUrl(account.accountId, {
460462
gateway: cfg.gateway,
461463
interactions: account.config.interactions,
462464
});

0 commit comments

Comments
 (0)