From 4d593731be5a5dcbf3106d596b38acfeb8cf0aa8 Mon Sep 17 00:00:00 2001 From: Muhammed Mukhthar CM Date: Fri, 6 Mar 2026 09:53:04 +0000 Subject: [PATCH] Mattermost: refresh interaction callback URL cache --- .../src/mattermost/interactions.test.ts | 13 ++++++++++- .../mattermost/src/mattermost/interactions.ts | 23 ++++++++++++++----- .../mattermost/src/mattermost/monitor.ts | 6 +++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/extensions/mattermost/src/mattermost/interactions.test.ts b/extensions/mattermost/src/mattermost/interactions.test.ts index 2ad979b1a49..19d39676a27 100644 --- a/extensions/mattermost/src/mattermost/interactions.test.ts +++ b/extensions/mattermost/src/mattermost/interactions.test.ts @@ -5,6 +5,7 @@ import { resolveMattermostAccount } from "./accounts.js"; import type { MattermostClient } from "./client.js"; import { buildButtonAttachments, + computeInteractionCallbackUrl, createMattermostInteractionHandler, generateInteractionToken, getInteractionCallbackUrl, @@ -136,7 +137,9 @@ describe("callback URL registry", () => { describe("resolveInteractionCallbackUrl", () => { afterEach(() => { - setInteractionCallbackUrl("resolve-test", ""); + for (const accountId of ["cached", "default", "acct", "myaccount"]) { + setInteractionCallbackUrl(accountId, ""); + } }); it("prefers cached URL from registry", () => { @@ -144,6 +147,14 @@ describe("resolveInteractionCallbackUrl", () => { expect(resolveInteractionCallbackUrl("cached")).toBe("http://cached:1234/path"); }); + it("recomputes from config when bypassing the cache explicitly", () => { + setInteractionCallbackUrl("acct", "http://cached:1234/path"); + const url = computeInteractionCallbackUrl("acct", { + gateway: { port: 9999, customBindHost: "gateway.internal" }, + }); + expect(url).toBe("http://gateway.internal:9999/mattermost/interactions/acct"); + }); + it("uses interactions.callbackBaseUrl when configured", () => { const url = resolveInteractionCallbackUrl("default", { channels: { diff --git a/extensions/mattermost/src/mattermost/interactions.ts b/extensions/mattermost/src/mattermost/interactions.ts index 6685e194c7d..33415ae519c 100644 --- a/extensions/mattermost/src/mattermost/interactions.ts +++ b/extensions/mattermost/src/mattermost/interactions.ts @@ -67,17 +67,12 @@ function normalizeCallbackBaseUrl(baseUrl: string): string { /** * Resolve the interaction callback URL for an account. - * Prefers the in-memory registered URL (set by the gateway monitor). * Falls back to computing it from interactions.callbackBaseUrl or gateway host config. */ -export function resolveInteractionCallbackUrl( +export function computeInteractionCallbackUrl( accountId: string, cfg?: InteractionCallbackConfig, ): string { - const cached = callbackUrls.get(accountId); - if (cached) { - return cached; - } const path = resolveInteractionCallbackPath(accountId); // Prefer merged per-account config when available, but keep the top-level path for // callers/tests that still pass the root Mattermost config shape directly. @@ -101,6 +96,22 @@ export function resolveInteractionCallbackUrl( return `http://${host}:${port}${path}`; } +/** + * Resolve the interaction callback URL for an account. + * Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the + * monitor lifecycle can reuse the runtime-validated callback destination. + */ +export function resolveInteractionCallbackUrl( + accountId: string, + cfg?: InteractionCallbackConfig, +): string { + const cached = callbackUrls.get(accountId); + if (cached) { + return cached; + } + return computeInteractionCallbackUrl(accountId, cfg); +} + // ── HMAC token management ────────────────────────────────────────────── // Secret is derived from the bot token so it's stable across CLI and gateway processes. diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index 8546f0a0983..4ce11a6a003 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -44,8 +44,8 @@ import { type MattermostUser, } from "./client.js"; import { + computeInteractionCallbackUrl, createMattermostInteractionHandler, - resolveInteractionCallbackUrl, resolveInteractionCallbackPath, setInteractionCallbackUrl, setInteractionSecret, @@ -456,7 +456,9 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} // Register HTTP callback endpoint for interactive button clicks. // Mattermost POSTs to this URL when a user clicks a button action. const interactionPath = resolveInteractionCallbackPath(account.accountId); - const callbackUrl = resolveInteractionCallbackUrl(account.accountId, { + // Recompute from config on each monitor start so reconnects or config reloads can refresh the + // cached callback URL for downstream callers such as `message action=send`. + const callbackUrl = computeInteractionCallbackUrl(account.accountId, { gateway: cfg.gateway, interactions: account.config.interactions, });