Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 14 additions & 1 deletion src/commands/integrations/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -20,7 +21,8 @@ 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',
];

Expand All @@ -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",
Expand Down Expand Up @@ -63,6 +69,7 @@ export default class IntegrationsCreateCommand extends ControlBaseCommand {
"channel.presence",
"channel.lifecycle",
"presence.message",
"chat.message",
],
required: true,
}),
Expand All @@ -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: {
Expand Down Expand Up @@ -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}`,
);
Expand Down
3 changes: 3 additions & 0 deletions src/commands/integrations/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>, flags).replaceAll("\n", "\n ")}`,
Expand Down
4 changes: 4 additions & 0 deletions src/commands/integrations/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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 ")}`,
);
Expand Down
15 changes: 14 additions & 1 deletion src/commands/integrations/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?: {
Expand All @@ -24,7 +25,8 @@ 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",
];
Expand All @@ -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"],
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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" &&
Expand Down
2 changes: 2 additions & 0 deletions src/services/control-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,14 @@ export interface Rule {
channelFilter: string;
type: string;
};
chatRoomFilter: string;
target: unknown;
version: string;
}

// Define RuleData interface for rule creation and updates
export interface RuleData {
chatRoomFilter?: string;
requestMode: string;
ruleType: string;
source: {
Expand Down
95 changes: 95 additions & 0 deletions test/e2e/integrations/integrations-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
},
);

expect(createResult.exitCode).toBe(0);

Check failure on line 92 in test/e2e/integrations/integrations-e2e.test.ts

View workflow job for this annotation

GitHub Actions / e2e-cli

[e2e] test/e2e/integrations/integrations-e2e.test.ts > Integrations E2E Tests > should create, get, and delete an integration rule

AssertionError: expected 1 to be +0 // Object.is equality - Expected + Received - 0 + 1 ❯ test/e2e/integrations/integrations-e2e.test.ts:92:37

// Extract the rule ID from the result
const createLines = parseNdjsonLines(createResult.stdout);
Expand Down Expand Up @@ -123,4 +123,99 @@
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);

Check failure on line 157 in test/e2e/integrations/integrations-e2e.test.ts

View workflow job for this annotation

GitHub Actions / e2e-cli

[e2e] test/e2e/integrations/integrations-e2e.test.ts > Integrations E2E Tests > should create, get, list, and delete a chat-room-sourced integration rule

AssertionError: expected 1 to be +0 // Object.is equality - Expected + Received - 0 + 1 ❯ test/e2e/integrations/integrations-e2e.test.ts:157:37

const createLines = parseNdjsonLines(createResult.stdout);
const createRecord = createLines.find((r) => r.type === "result");
expect(createRecord).toBeDefined();

const createdRule = (createRecord?.rule ?? createRecord?.integration) as
| Record<string, unknown>
| 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<string, unknown>;
expect(fetchedRule).toHaveProperty("chatRoomFilter", "room:.*");
expect((fetchedRule.source as Record<string, unknown>).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<string, unknown>[]
).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);
},
);
});
1 change: 1 addition & 0 deletions test/fixtures/control-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface MockRule {
version: string;
created: number;
modified: number;
chatRoomFilter?: string;
source: { channelFilter: string; type: string };
target: Record<string, unknown>;
}
Expand Down
Loading
Loading