From f0ef330acf97eeb58235138a3125273eb3fa94ef Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 17 Jul 2026 16:51:28 +0100 Subject: [PATCH 1/4] feat(integrations): show chat room filter on rule get/list Rules created from Ably Chat rooms populate chatRoomFilter (top-level on the rule, alongside source) instead of source.channelFilter. Surface it in both human-readable and JSON output so `integrations get`/`list` don't silently omit the filter for chat-room-sourced rules. DX-1546 Co-Authored-By: Claude Sonnet 5 --- src/commands/integrations/get.ts | 3 + src/commands/integrations/list.ts | 4 ++ src/services/control-api.ts | 1 + test/fixtures/control-api.ts | 1 + test/unit/commands/integrations/get.test.ts | 69 ++++++++++++++++++++ test/unit/commands/integrations/list.test.ts | 60 +++++++++++++++++ 6 files changed, 138 insertions(+) diff --git a/src/commands/integrations/get.ts b/src/commands/integrations/get.ts index 222288b88..4f8b844e4 100644 --- a/src/commands/integrations/get.ts +++ b/src/commands/integrations/get.ts @@ -59,6 +59,9 @@ export default class IntegrationsGetCommand extends ControlBaseCommand { `${formatLabel("Source Channel Filter")} ${rule.source.channelFilter}`, ); } + if (rule.chatRoomFilter) { + this.log(`${formatLabel("Chat Room Filter")} ${rule.chatRoomFilter}`); + } this.log(`${formatLabel("Source Type")} ${rule.source.type}`); this.log( `${formatLabel("Target")} ${this.formatJsonOutput(structuredClone(rule.target) as Record, flags).replaceAll("\n", "\n ")}`, diff --git a/src/commands/integrations/list.ts b/src/commands/integrations/list.ts index 19451ab7e..f50291718 100644 --- a/src/commands/integrations/list.ts +++ b/src/commands/integrations/list.ts @@ -46,6 +46,7 @@ export default class IntegrationsListCommand extends ControlBaseCommand { hasMore, integrations: integrations.map((integration) => ({ appId: integration.appId, + chatRoomFilter: integration.chatRoomFilter || null, created: new Date(integration.created).toISOString(), id: integration.id, modified: new Date(integration.modified).toISOString(), @@ -80,6 +81,9 @@ export default class IntegrationsListCommand extends ControlBaseCommand { if (integration.source.channelFilter) { this.log(` Channel Filter: ${integration.source.channelFilter}`); } + if (integration.chatRoomFilter) { + this.log(` Chat Room Filter: ${integration.chatRoomFilter}`); + } this.log( ` Target: ${JSON.stringify(integration.target, null, 2).replaceAll("\n", "\n ")}`, ); diff --git a/src/services/control-api.ts b/src/services/control-api.ts index cca9438fc..e8801c335 100644 --- a/src/services/control-api.ts +++ b/src/services/control-api.ts @@ -95,6 +95,7 @@ export interface Rule { channelFilter: string; type: string; }; + chatRoomFilter: string; target: unknown; version: string; } diff --git a/test/fixtures/control-api.ts b/test/fixtures/control-api.ts index d2f50824c..ea64ca834 100644 --- a/test/fixtures/control-api.ts +++ b/test/fixtures/control-api.ts @@ -59,6 +59,7 @@ export interface MockRule { version: string; created: number; modified: number; + chatRoomFilter?: string; source: { channelFilter: string; type: string }; target: Record; } diff --git a/test/unit/commands/integrations/get.test.ts b/test/unit/commands/integrations/get.test.ts index 9ca31401e..7e3d98949 100644 --- a/test/unit/commands/integrations/get.test.ts +++ b/test/unit/commands/integrations/get.test.ts @@ -176,6 +176,75 @@ describe("integrations:get command", () => { expect(stdout).toContain("chat:*"); }); + it("should display chat room filter", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + const mockIntegration = { + id: mockRuleId, + appId, + ruleType: "http", + requestMode: "single", + chatRoomFilter: "room:*", + source: { + type: "room.message", + }, + target: { + url: "https://example.com/webhook", + format: "json", + enveloped: true, + }, + status: "enabled", + version: "1.0", + created: Date.now(), + modified: Date.now(), + }; + + nockControl() + .get(`/v1/apps/${appId}/rules/${mockRuleId}`) + .reply(200, mockIntegration); + + const { stdout } = await runCommand( + ["integrations:get", mockRuleId], + import.meta.url, + ); + + expect(stdout).toContain("Chat Room Filter"); + expect(stdout).toContain("room:*"); + }); + + it("should not display chat room filter when absent", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + const mockIntegration = { + id: mockRuleId, + appId, + ruleType: "http", + requestMode: "single", + source: { + channelFilter: "chat:*", + type: "channel.message", + }, + target: { + url: "https://example.com/webhook", + format: "json", + enveloped: true, + }, + status: "enabled", + version: "1.0", + created: Date.now(), + modified: Date.now(), + }; + + nockControl() + .get(`/v1/apps/${appId}/rules/${mockRuleId}`) + .reply(200, mockIntegration); + + const { stdout } = await runCommand( + ["integrations:get", mockRuleId], + import.meta.url, + ); + + expect(stdout).not.toContain("Chat Room Filter"); + }); + it("should display target information", async () => { const appId = getMockConfigManager().getCurrentAppId()!; const mockIntegration = { diff --git a/test/unit/commands/integrations/list.test.ts b/test/unit/commands/integrations/list.test.ts index a47e6d7f7..c3cb6862f 100644 --- a/test/unit/commands/integrations/list.test.ts +++ b/test/unit/commands/integrations/list.test.ts @@ -101,6 +101,66 @@ describe("integrations:list command", () => { expect(stdout).toContain("Source Type: channel.message"); expect(stdout).toContain("Channel Filter: chat:*"); }); + + it("should display chat room filter in human-readable output", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + const integrationsWithChatRoomFilter = [ + mockRule({ + id: "rule-003", + appId, + chatRoomFilter: "room:*", + source: { channelFilter: "", type: "room.message" }, + target: { url: "https://example.com/webhook", format: "json" }, + }), + ]; + nockControl() + .get(`/v1/apps/${appId}/rules`) + .reply(200, integrationsWithChatRoomFilter); + + const { stdout } = await runCommand( + ["integrations:list"], + import.meta.url, + ); + + expect(stdout).toContain("Chat Room Filter: room:*"); + }); + + it("should include chatRoomFilter in JSON output", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + const integrationsWithChatRoomFilter = [ + mockRule({ + id: "rule-003", + appId, + chatRoomFilter: "room:*", + source: { channelFilter: "", type: "room.message" }, + target: { url: "https://example.com/webhook", format: "json" }, + }), + ]; + nockControl() + .get(`/v1/apps/${appId}/rules`) + .reply(200, integrationsWithChatRoomFilter); + + const { stdout } = await runCommand( + ["integrations:list", "--json"], + import.meta.url, + ); + + const result = parseJsonOutput(stdout); + expect(result.integrations[0]).toHaveProperty("chatRoomFilter", "room:*"); + }); + + it("should output null chatRoomFilter in JSON when absent", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + nockControl().get(`/v1/apps/${appId}/rules`).reply(200, mockIntegrations); + + const { stdout } = await runCommand( + ["integrations:list", "--json"], + import.meta.url, + ); + + const result = parseJsonOutput(stdout); + expect(result.integrations[0]).toHaveProperty("chatRoomFilter", null); + }); }); standardFlagTests("integrations:list", import.meta.url, [ From f423e775a2f3bdf2d8b2b1f931db7e78e3e6e808 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 17 Jul 2026 17:08:13 +0100 Subject: [PATCH 2/4] feat(integrations): support setting chatRoomFilter via create/update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rules can be sourced from Ably Chat rooms (source-type chat.message), which requires setting chatRoomFilter rather than the channel-based source.channelFilter. `integrations create`/`update` had no way to set it, even though get/list already knew how to display it. Also corrects the example/test filter values to valid regex syntax (channelFilter/chatRoomFilter are regexps, not globs) — e.g. "room:.*" instead of "room:*". DX-1546 --- src/commands/integrations/create.ts | 13 +++ src/commands/integrations/update.ts | 13 +++ src/services/control-api.ts | 1 + .../unit/commands/integrations/create.test.ts | 88 +++++++++++++++++++ test/unit/commands/integrations/get.test.ts | 4 +- test/unit/commands/integrations/list.test.ts | 11 ++- .../unit/commands/integrations/update.test.ts | 47 ++++++++++ 7 files changed, 171 insertions(+), 6 deletions(-) diff --git a/src/commands/integrations/create.ts b/src/commands/integrations/create.ts index 09dfb4ad8..7e90d8c0f 100644 --- a/src/commands/integrations/create.ts +++ b/src/commands/integrations/create.ts @@ -5,6 +5,7 @@ import { formatLabel, formatResource } from "../../utils/output.js"; // Interface for basic integration data structure interface IntegrationData { + chatRoomFilter: string; requestMode: string; ruleType: string; // API property name source: { @@ -21,6 +22,7 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { static examples = [ '$ ably integrations create --rule-type "http" --source-type "channel.message" --target-url "https://example.com/webhook"', '$ ably integrations create --rule-type "amqp" --source-type "channel.message" --channel-filter "chat:*"', + '$ ably integrations create --rule-type "http" --source-type "chat.message" --chat-room-filter "room:.*" --target-url "https://example.com/webhook"', '$ ably integrations create --rule-type "http" --source-type "channel.message" --target-url "https://example.com/webhook" --json', ]; @@ -34,6 +36,10 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { description: "Channel filter pattern", required: false, }), + "chat-room-filter": Flags.string({ + description: "Chat room filter pattern", + required: false, + }), "request-mode": Flags.string({ default: "single", description: "Request mode for the integration", @@ -63,6 +69,7 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { "channel.presence", "channel.lifecycle", "presence.message", + "chat.message", ], required: true, }), @@ -87,6 +94,7 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { const controlApi = this.createControlApi(flags); // Prepare integration data const integrationData: IntegrationData = { + chatRoomFilter: flags["chat-room-filter"] || "", requestMode: flags["request-mode"], ruleType: flags["rule-type"], // API property name source: { @@ -169,6 +177,11 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { `${formatLabel("Source Channel Filter")} ${createdIntegration.source.channelFilter}`, ); } + if (createdIntegration.chatRoomFilter) { + this.log( + `${formatLabel("Chat Room Filter")} ${createdIntegration.chatRoomFilter}`, + ); + } this.log( `${formatLabel("Source Type")} ${createdIntegration.source.type}`, ); diff --git a/src/commands/integrations/update.ts b/src/commands/integrations/update.ts index 103512c70..e68b100d8 100644 --- a/src/commands/integrations/update.ts +++ b/src/commands/integrations/update.ts @@ -2,6 +2,7 @@ import { Args, Flags } from "@oclif/core"; import { ControlBaseCommand } from "../../control-base-command.js"; // Interface for rule update data structure (most fields optional) interface PartialRuleData { + chatRoomFilter?: string; requestMode?: string; ruleType?: string; // Usually shouldn't be updated, but kept for structure source?: { @@ -25,6 +26,7 @@ export default class IntegrationsUpdateCommand extends ControlBaseCommand { static examples = [ "$ ably integrations update rule123 --status disabled", '$ ably integrations update rule123 --channel-filter "chat:*"', + '$ ably integrations update rule123 --chat-room-filter "room:.*"', '$ ably integrations update rule123 --target-url "https://new-example.com/webhook"', "$ ably integrations update rule123 --status disabled --json", ]; @@ -39,6 +41,10 @@ export default class IntegrationsUpdateCommand extends ControlBaseCommand { description: "Channel filter pattern", required: false, }), + "chat-room-filter": Flags.string({ + description: "Chat room filter pattern", + required: false, + }), status: Flags.string({ description: "Status of the rule", options: ["enabled", "disabled"], @@ -96,6 +102,10 @@ export default class IntegrationsUpdateCommand extends ControlBaseCommand { updatePayload.source.channelFilter = flags["channel-filter"]; } + if (flags["chat-room-filter"]) { + updatePayload.chatRoomFilter = flags["chat-room-filter"]; + } + // Update target if it's an HTTP rule and target-url is provided if (existingRule.ruleType === "http" && flags["target-url"]) { // Ensure target exists before assigning to url @@ -130,6 +140,9 @@ export default class IntegrationsUpdateCommand extends ControlBaseCommand { `Source Channel Filter: ${updatedRule.source.channelFilter}`, ); } + if (updatedRule.chatRoomFilter) { + this.log(`Chat Room Filter: ${updatedRule.chatRoomFilter}`); + } this.log(`Source Type: ${updatedRule.source.type}`); if ( typeof updatedRule.target === "object" && diff --git a/src/services/control-api.ts b/src/services/control-api.ts index e8801c335..ec30432d7 100644 --- a/src/services/control-api.ts +++ b/src/services/control-api.ts @@ -102,6 +102,7 @@ export interface Rule { // Define RuleData interface for rule creation and updates export interface RuleData { + chatRoomFilter?: string; requestMode: string; ruleType: string; source: { diff --git a/test/unit/commands/integrations/create.test.ts b/test/unit/commands/integrations/create.test.ts index e6328440e..edd27183e 100644 --- a/test/unit/commands/integrations/create.test.ts +++ b/test/unit/commands/integrations/create.test.ts @@ -61,6 +61,46 @@ describe("integrations:create command", () => { expect(stdout).toContain("http"); }); + it("should display chat room filter in human-readable output", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + nockControl() + .post(`/v1/apps/${appId}/rules`) + .reply(201, { + id: mockRuleId, + appId, + ruleType: "http", + requestMode: "single", + chatRoomFilter: "room:.*", + source: { + channelFilter: "", + type: "chat.message", + }, + target: { + url: "https://example.com/webhook", + format: "json", + }, + status: "enabled", + }); + + const { stdout } = await runCommand( + [ + "integrations:create", + "--rule-type", + "http", + "--source-type", + "chat.message", + "--chat-room-filter", + "room:.*", + "--target-url", + "https://example.com/webhook", + ], + import.meta.url, + ); + + expect(stdout).toContain("Chat Room Filter"); + expect(stdout).toContain("room:.*"); + }); + it("should create an AMQP integration successfully", async () => { const appId = getMockConfigManager().getCurrentAppId()!; nockControl() @@ -435,6 +475,54 @@ describe("integrations:create command", () => { const source = integration.source as Record; expect(source.type).toBe("channel.lifecycle"); }); + + it("should accept chat.message source type with a chat room filter", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + nockControl() + .post(`/v1/apps/${appId}/rules`, (body: Record) => { + return body.chatRoomFilter === "room:.*"; + }) + .reply(201, { + id: mockRuleId, + appId, + ruleType: "http", + requestMode: "single", + chatRoomFilter: "room:.*", + source: { + channelFilter: "", + type: "chat.message", + }, + target: { + url: "https://example.com/webhook", + }, + status: "enabled", + created: 1640995200000, + modified: 1640995200000, + }); + + const { stdout } = await runCommand( + [ + "integrations:create", + "--rule-type", + "http", + "--source-type", + "chat.message", + "--chat-room-filter", + "room:.*", + "--target-url", + "https://example.com/webhook", + "--json", + ], + import.meta.url, + ); + + const result = parseNdjsonLines(stdout).find((r) => r.type === "result")!; + expect(result).toHaveProperty("success", true); + const integration = result.integration as Record; + expect(integration).toHaveProperty("chatRoomFilter", "room:.*"); + const source = integration.source as Record; + expect(source.type).toBe("chat.message"); + }); }); standardHelpTests("integrations:create", import.meta.url); diff --git a/test/unit/commands/integrations/get.test.ts b/test/unit/commands/integrations/get.test.ts index 7e3d98949..86f98a544 100644 --- a/test/unit/commands/integrations/get.test.ts +++ b/test/unit/commands/integrations/get.test.ts @@ -183,7 +183,7 @@ describe("integrations:get command", () => { appId, ruleType: "http", requestMode: "single", - chatRoomFilter: "room:*", + chatRoomFilter: "room:.*", source: { type: "room.message", }, @@ -208,7 +208,7 @@ describe("integrations:get command", () => { ); expect(stdout).toContain("Chat Room Filter"); - expect(stdout).toContain("room:*"); + expect(stdout).toContain("room:.*"); }); it("should not display chat room filter when absent", async () => { diff --git a/test/unit/commands/integrations/list.test.ts b/test/unit/commands/integrations/list.test.ts index c3cb6862f..06647c47e 100644 --- a/test/unit/commands/integrations/list.test.ts +++ b/test/unit/commands/integrations/list.test.ts @@ -108,7 +108,7 @@ describe("integrations:list command", () => { mockRule({ id: "rule-003", appId, - chatRoomFilter: "room:*", + chatRoomFilter: "room:.*", source: { channelFilter: "", type: "room.message" }, target: { url: "https://example.com/webhook", format: "json" }, }), @@ -122,7 +122,7 @@ describe("integrations:list command", () => { import.meta.url, ); - expect(stdout).toContain("Chat Room Filter: room:*"); + expect(stdout).toContain("Chat Room Filter: room:.*"); }); it("should include chatRoomFilter in JSON output", async () => { @@ -131,7 +131,7 @@ describe("integrations:list command", () => { mockRule({ id: "rule-003", appId, - chatRoomFilter: "room:*", + chatRoomFilter: "room:.*", source: { channelFilter: "", type: "room.message" }, target: { url: "https://example.com/webhook", format: "json" }, }), @@ -146,7 +146,10 @@ describe("integrations:list command", () => { ); const result = parseJsonOutput(stdout); - expect(result.integrations[0]).toHaveProperty("chatRoomFilter", "room:*"); + expect(result.integrations[0]).toHaveProperty( + "chatRoomFilter", + "room:.*", + ); }); it("should output null chatRoomFilter in JSON when absent", async () => { diff --git a/test/unit/commands/integrations/update.test.ts b/test/unit/commands/integrations/update.test.ts index 8229295a1..1edb5c75c 100644 --- a/test/unit/commands/integrations/update.test.ts +++ b/test/unit/commands/integrations/update.test.ts @@ -67,6 +67,53 @@ describe("integrations:update command", () => { expect(stdout).toContain(mockRuleId); }); + it("should update chat room filter", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + const mockIntegration = { + id: mockRuleId, + appId, + ruleType: "http", + requestMode: "single", + chatRoomFilter: "room:.*", + source: { + channelFilter: "", + type: "chat.message", + }, + target: { + url: "https://example.com/webhook", + format: "json", + enveloped: true, + }, + status: "enabled", + version: "1.0", + created: Date.now(), + modified: Date.now(), + }; + const updatedIntegration = { + ...mockIntegration, + chatRoomFilter: "rooms:.*", + }; + + nockControl() + .get(`/v1/apps/${appId}/rules/${mockRuleId}`) + .reply(200, mockIntegration); + + nockControl() + .patch( + `/v1/apps/${appId}/rules/${mockRuleId}`, + (body: Record) => body.chatRoomFilter === "rooms:.*", + ) + .reply(200, updatedIntegration); + + const { stdout, stderr } = await runCommand( + ["integrations:update", mockRuleId, "--chat-room-filter", "rooms:.*"], + import.meta.url, + ); + + expect(stderr).toContain("Integration rule updated."); + expect(stdout).toContain("Chat Room Filter: rooms:.*"); + }); + it("should update target URL for HTTP integrations", async () => { const appId = getMockConfigManager().getCurrentAppId()!; const mockIntegration = { From 519f4e1e9212d189d9aad1eafedfbcec65e7a5dc Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 17 Jul 2026 17:09:58 +0100 Subject: [PATCH 3/4] test(e2e): cover chat-room-sourced integration rule lifecycle Extends the Control API e2e suite to create/get/list/delete a rule with source-type chat.message and --chat-room-filter, verifying chatRoomFilter round-trips through the real API end to end. --- .../e2e/integrations/integrations-e2e.test.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/test/e2e/integrations/integrations-e2e.test.ts b/test/e2e/integrations/integrations-e2e.test.ts index 0a627e5ee..acb6d557c 100644 --- a/test/e2e/integrations/integrations-e2e.test.ts +++ b/test/e2e/integrations/integrations-e2e.test.ts @@ -123,4 +123,99 @@ describe.skipIf(SHOULD_SKIP_CONTROL_E2E)("Integrations E2E Tests", () => { expect(deleteResult.exitCode).toBe(0); }, ); + + it( + "should create, get, list, and delete a chat-room-sourced integration rule", + { timeout: 30000 }, + async () => { + setupTestFailureHandler( + "should create, get, list, and delete a chat-room-sourced integration rule", + ); + + // Create a rule sourced from a chat room rather than a channel + const createResult = await runCommand( + [ + "integrations", + "create", + "--app", + testAppId, + "--rule-type", + "http", + "--source-type", + "chat.message", + "--chat-room-filter", + "room:.*", + "--target-url", + "https://example.com/e2e-chat-room-webhook-test", + "--json", + ], + { + env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" }, + }, + ); + + expect(createResult.exitCode).toBe(0); + + const createLines = parseNdjsonLines(createResult.stdout); + const createRecord = createLines.find((r) => r.type === "result"); + expect(createRecord).toBeDefined(); + + const createdRule = (createRecord?.rule ?? createRecord?.integration) as + | Record + | undefined; + const ruleId = (createdRule?.id ?? createdRule?.ruleId ?? "") as string; + expect(ruleId).toBeTruthy(); + expect(createdRule).toHaveProperty("chatRoomFilter", "room:.*"); + + // Get the rule and confirm chatRoomFilter round-trips + const getResult = await runCommand( + ["integrations", "get", ruleId, "--app", testAppId, "--json"], + { + env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" }, + }, + ); + + expect(getResult.exitCode).toBe(0); + + const getLines = parseNdjsonLines(getResult.stdout); + const getRecord = getLines.find((r) => r.type === "result"); + expect(getRecord).toBeDefined(); + + const fetchedRule = getRecord?.rule as Record; + expect(fetchedRule).toHaveProperty("chatRoomFilter", "room:.*"); + expect((fetchedRule.source as Record).type).toBe( + "chat.message", + ); + + // List rules and confirm the chat-room rule appears with its filter + const listResult = await runCommand( + ["integrations", "list", "--app", testAppId, "--json"], + { + env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" }, + }, + ); + + expect(listResult.exitCode).toBe(0); + + const listRecords = parseNdjsonLines(listResult.stdout); + const listRecord = listRecords.find((r) => r.type === "result"); + expect(listRecord).toBeDefined(); + + const listedRule = ( + listRecord!.integrations as Record[] + ).find((rule) => rule.id === ruleId); + expect(listedRule).toBeDefined(); + expect(listedRule).toHaveProperty("chatRoomFilter", "room:.*"); + + // Delete the integration rule + const deleteResult = await runCommand( + ["integrations", "delete", ruleId, "--app", testAppId, "--force"], + { + env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" }, + }, + ); + + expect(deleteResult.exitCode).toBe(0); + }, + ); }); From 522feb4a1da81a9df48916c27b9ebd730ef7084c Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 17 Jul 2026 17:24:12 +0100 Subject: [PATCH 4/4] fix(integrations): use valid regex syntax in channelFilter examples/tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit channelFilter is a regexp, not a glob, so "chat:*" is invalid — the trailing * has nothing to repeat. Switch examples and test fixtures to "chat:.*" (and similarly for other filter values used in tests). --- src/commands/integrations/create.ts | 2 +- src/commands/integrations/update.ts | 2 +- .../unit/commands/integrations/create.test.ts | 14 ++++++------- .../unit/commands/integrations/delete.test.ts | 10 +++++----- test/unit/commands/integrations/get.test.ts | 16 +++++++-------- test/unit/commands/integrations/list.test.ts | 4 ++-- .../unit/commands/integrations/update.test.ts | 20 +++++++++---------- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/commands/integrations/create.ts b/src/commands/integrations/create.ts index 7e90d8c0f..77f762b2d 100644 --- a/src/commands/integrations/create.ts +++ b/src/commands/integrations/create.ts @@ -21,7 +21,7 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand { static examples = [ '$ ably integrations create --rule-type "http" --source-type "channel.message" --target-url "https://example.com/webhook"', - '$ ably integrations create --rule-type "amqp" --source-type "channel.message" --channel-filter "chat:*"', + '$ ably integrations create --rule-type "amqp" --source-type "channel.message" --channel-filter "chat:.*"', '$ ably integrations create --rule-type "http" --source-type "chat.message" --chat-room-filter "room:.*" --target-url "https://example.com/webhook"', '$ ably integrations create --rule-type "http" --source-type "channel.message" --target-url "https://example.com/webhook" --json', ]; diff --git a/src/commands/integrations/update.ts b/src/commands/integrations/update.ts index e68b100d8..e0ac60b49 100644 --- a/src/commands/integrations/update.ts +++ b/src/commands/integrations/update.ts @@ -25,7 +25,7 @@ export default class IntegrationsUpdateCommand extends ControlBaseCommand { static examples = [ "$ ably integrations update rule123 --status disabled", - '$ ably integrations update rule123 --channel-filter "chat:*"', + '$ ably integrations update rule123 --channel-filter "chat:.*"', '$ ably integrations update rule123 --chat-room-filter "room:.*"', '$ ably integrations update rule123 --target-url "https://new-example.com/webhook"', "$ ably integrations update rule123 --status disabled --json", diff --git a/test/unit/commands/integrations/create.test.ts b/test/unit/commands/integrations/create.test.ts index edd27183e..64199dd12 100644 --- a/test/unit/commands/integrations/create.test.ts +++ b/test/unit/commands/integrations/create.test.ts @@ -31,7 +31,7 @@ describe("integrations:create command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -49,7 +49,7 @@ describe("integrations:create command", () => { "--source-type", "channel.message", "--channel-filter", - "chat:*", + "chat:.*", "--target-url", "https://example.com/webhook", ], @@ -239,7 +239,7 @@ describe("integrations:create command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -259,7 +259,7 @@ describe("integrations:create command", () => { "--source-type", "channel.message", "--channel-filter", - "chat:*", + "chat:.*", "--target-url", "https://example.com/webhook", "--json", @@ -292,7 +292,7 @@ describe("integrations:create command", () => { "--source-type", "channel.message", "--channel-filter", - "chat:*", + "chat:.*", "--target-url", "https://example.com/webhook", ], @@ -314,7 +314,7 @@ describe("integrations:create command", () => { "--source-type", "channel.message", "--channel-filter", - "chat:*", + "chat:.*", "--target-url", "https://example.com/webhook", ], @@ -365,7 +365,7 @@ describe("integrations:create command", () => { "--source-type", "channel.message", "--channel-filter", - "chat:*", + "chat:.*", "--target-url", "https://example.com/webhook", ], diff --git a/test/unit/commands/integrations/delete.test.ts b/test/unit/commands/integrations/delete.test.ts index b4dc804c4..a88ec5bc2 100644 --- a/test/unit/commands/integrations/delete.test.ts +++ b/test/unit/commands/integrations/delete.test.ts @@ -27,7 +27,7 @@ describe("integrations:delete command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -66,7 +66,7 @@ describe("integrations:delete command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -145,7 +145,7 @@ describe("integrations:delete command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -196,7 +196,7 @@ describe("integrations:delete command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -234,7 +234,7 @@ describe("integrations:delete command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { diff --git a/test/unit/commands/integrations/get.test.ts b/test/unit/commands/integrations/get.test.ts index 86f98a544..da2c181d4 100644 --- a/test/unit/commands/integrations/get.test.ts +++ b/test/unit/commands/integrations/get.test.ts @@ -28,7 +28,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -66,7 +66,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -112,7 +112,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -150,7 +150,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -173,7 +173,7 @@ describe("integrations:get command", () => { import.meta.url, ); - expect(stdout).toContain("chat:*"); + expect(stdout).toContain("chat:.*"); }); it("should display chat room filter", async () => { @@ -219,7 +219,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -253,7 +253,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -339,7 +339,7 @@ describe("integrations:get command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { diff --git a/test/unit/commands/integrations/list.test.ts b/test/unit/commands/integrations/list.test.ts index 06647c47e..87eb20393 100644 --- a/test/unit/commands/integrations/list.test.ts +++ b/test/unit/commands/integrations/list.test.ts @@ -19,7 +19,7 @@ describe("integrations:list command", () => { mockRule({ id: "rule-001", appId: "app-123", - source: { channelFilter: "chat:*", type: "channel.message" }, + source: { channelFilter: "chat:.*", type: "channel.message" }, target: { url: "https://example.com/webhook", format: "json" }, }), mockRule({ @@ -99,7 +99,7 @@ describe("integrations:list command", () => { expect(stdout).toContain("Integration ID: rule-001"); expect(stdout).toContain("Request Mode: single"); expect(stdout).toContain("Source Type: channel.message"); - expect(stdout).toContain("Channel Filter: chat:*"); + expect(stdout).toContain("Channel Filter: chat:.*"); }); it("should display chat room filter in human-readable output", async () => { diff --git a/test/unit/commands/integrations/update.test.ts b/test/unit/commands/integrations/update.test.ts index 1edb5c75c..e5d6f1bac 100644 --- a/test/unit/commands/integrations/update.test.ts +++ b/test/unit/commands/integrations/update.test.ts @@ -27,7 +27,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -44,7 +44,7 @@ describe("integrations:update command", () => { ...mockIntegration, source: { ...mockIntegration.source, - channelFilter: "messages:*", + channelFilter: "messages:.*", }, }; @@ -59,7 +59,7 @@ describe("integrations:update command", () => { .reply(200, updatedIntegration); const { stdout, stderr } = await runCommand( - ["integrations:update", mockRuleId, "--channel-filter", "messages:*"], + ["integrations:update", mockRuleId, "--channel-filter", "messages:.*"], import.meta.url, ); @@ -122,7 +122,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -168,7 +168,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -236,7 +236,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -344,7 +344,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -397,7 +397,7 @@ describe("integrations:update command", () => { ruleType: "http", requestMode: "single", source: { - channelFilter: "chat:*", + channelFilter: "chat:.*", type: "channel.message", }, target: { @@ -414,7 +414,7 @@ describe("integrations:update command", () => { ...mockIntegration, source: { ...mockIntegration.source, - channelFilter: "new:*", + channelFilter: "new:.*", }, }; @@ -446,7 +446,7 @@ describe("integrations:update command", () => { "--app", appId, "--channel-filter", - "new:*", + "new:.*", ], import.meta.url, );