Skip to content

Commit

Permalink
Add support for wildcard/unscoped channel event trigger definitions. …
Browse files Browse the repository at this point in the history
…Fixes #222.
  • Loading branch information
Filip Maj committed Apr 15, 2024
1 parent 0f1fee7 commit 6b116d5
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .github/maintainers_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ To create a new release:
7. Publish the release by clicking the "Publish release" button!
8. After a few minutes, the corresponding version will be available on
<https://deno.land/x/deno_slack_api>.
9. Don't forget to also bump this library's version in the deno-slack-sdk's `deps.ts`
file!
9. Don't forget to also bump this library's version in the deno-slack-sdk's
`deps.ts` file!

## Workflow

Expand Down
65 changes: 35 additions & 30 deletions src/typed-method-types/workflows/triggers/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,38 @@ type WorkspaceTypes = ObjectValueUnion<
>;

type ChannelEvents =
& (ChannelEvent | MetadataChannelEvent | MessagePostedEvent)
& {
/** @description The channel id's that this event listens on */
channel_ids: PopulatedArray<string>;
// deno-lint-ignore no-explicit-any
[otherOptions: string]: any;
};
& (ChannelEvent | MetadataChannelEvent | MessagePostedEvent) // controls `event_type` and `filter`
& (ChannelUnscopedEvent | ChannelScopedEvent); // controls event scoping: `channel_ids` and `all_resources`

/**
* Event that is unscoped (limited) to a specific channel
*/
type ChannelUnscopedEvent = {
/** @description If set to `true`, will trigger in all channels. `false` by default and mutually exclusive with `channel_ids`. */
all_resources: true;
/** @description The channel ids that this event listens on. Mutually exclusive with `all_resources`. */
channel_ids?: never;
};

/**
* Event that is scoped to specific channel ID(s)
*/
type ChannelScopedEvent = {
/** @description The channel ids that this event listens on. Mutually exclusive with `all_resources`. */
channel_ids: PopulatedArray<string>;
/** @description If set to `true`, will trigger in all channels. `false` by default and mutually exclusive with `channel_ids`. */
all_resources?: false;
};

type ChannelEvent = BaseEvent & {
/** @description The type of event */
event_type: Exclude<ChannelTypes, MessageMetadataTypes>;
};

type MetadataChannelEvent =
& BaseEvent
& {
/** @description The type of event */
event_type: Extract<ChannelTypes, MessageMetadataTypes>;
/** @description User defined description for the metadata event type */
metadata_event_type: string;
};
type MetadataChannelEvent = ChannelEvent & {
/** @description User defined description for the metadata event type */
metadata_event_type: string;
};

// The only event that currently requires a filter
type MessagePostedEvent =
Expand All @@ -87,26 +98,20 @@ type MessagePostedEvent =
event_type: MessagePostedEventType;
};

type WorkspaceEvents =
& BaseWorkspaceEvent
& {
/** @description The team id's that this event listens on */
team_ids?: PopulatedArray<string>;
// deno-lint-ignore no-explicit-any
[otherOptions: string]: any;
};

type BaseWorkspaceEvent = BaseEvent & {
type WorkspaceEvents = BaseEvent & {
/** @description The type of event */
event_type: WorkspaceTypes;
/** @description The team IDs that this event should listen on. Must be included when used on Enterprise Grid and working with workspace-based event triggers. */
team_ids?: PopulatedArray<string>;
};

type BaseEvent = {
// TODO: (breaking change) filter should not be optional here, but explicitly chosen for the events that accept it;
// could use similar technique as we do to manage messagemetadata-specific properties (above)
/** @description Defines the condition in which this event trigger should execute the workflow */
filter?: FilterType;
// deno-lint-ignore no-explicit-any
[otherOptions: string]: any;
};
} & Record<string, any>;

export type EventTrigger<WorkflowDefinition extends WorkflowSchema> =
& BaseTrigger<WorkflowDefinition>
Expand Down Expand Up @@ -138,6 +143,6 @@ export type EventTriggerResponseObject<
* @description The type of event specified for the event trigger
*/
event_type?: string;
// deno-lint-ignore no-explicit-any
[otherOptions: string]: any;
};
}
// deno-lint-ignore no-explicit-any
& Record<string, any>;
188 changes: 152 additions & 36 deletions src/typed-method-types/workflows/triggers/tests/event_test.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,166 @@
import { assertEquals } from "../../../../dev_deps.ts";
import { TriggerTypes } from "../mod.ts";
import { SlackAPI } from "../../../../mod.ts";
import { SlackAPI, TriggerEventTypes, TriggerTypes } from "../../../../mod.ts";
import * as mf from "https://deno.land/x/[email protected]/mod.ts";
import { event_response } from "./fixtures/sample_responses.ts";
import { ExampleWorkflow } from "./fixtures/workflows.ts";
import { EventTrigger } from "../event.ts";

Deno.test("Event triggers can set the type using the string", () => {
const event: EventTrigger<ExampleWorkflow> = {
type: "event",
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: "slack#/events/reaction_added",
channel_ids: ["C013ZG3K41Z"],
Deno.test("Event trigger type tests", async (t) => {
await t.step("Event triggers can set the type using the string", () => {
const event: EventTrigger<ExampleWorkflow> = {
type: "event",
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: "slack#/events/reaction_added",
channel_ids: ["C013ZG3K41Z"],
},
};
assertEquals(event.type, TriggerTypes.Event);
});

await t.step(
"Event triggers can set the type using the TriggerTypes object",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: "slack#/events/reaction_added",
channel_ids: ["C013ZG3K41Z"],
},
};
assertEquals(event.type, TriggerTypes.Event);
},
};
assertEquals(event.type, TriggerTypes.Event);
});
);

Deno.test("Event triggers can set the type using the TriggerTypes object", () => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: "slack#/events/reaction_added",
channel_ids: ["C013ZG3K41Z"],
await t.step(
"shared_channel_invite_* event triggers do not require channel_ids",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: TriggerEventTypes.SharedChannelInviteAccepted,
},
};
assertEquals(event.type, TriggerTypes.Event);
},
};
assertEquals(event.type, TriggerTypes.Event);
});
);

await t.step(
"can define a channel-scoped event with `channel_ids`",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: TriggerEventTypes.ReactionAdded,
channel_ids: ["C1234"],
},
};
const _event2: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: TriggerEventTypes.ReactionAdded,
channel_ids: ["C1234"],
all_resources: false,
},
};
assertEquals(event.type, TriggerTypes.Event);
},
);

Deno.test("shared_channel_invite_* event triggers do not require channel_ids", () => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: "slack#/events/shared_channel_invite_accepted",
await t.step(
"can define a channel-unscoped event with `all_resources`",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: TriggerEventTypes.ReactionAdded,
all_resources: true,
},
};
assertEquals(event.type, TriggerTypes.Event);
},
);

await t.step(
"channel event types must provide one of `all_resources:true` or `channel_ids`",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
// @ts-expect-error requires one of `all_resources:true` or `channel_ids`
event: {
event_type: TriggerEventTypes.ReactionAdded,
},
};
const _event2: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
// @ts-expect-error requires one of `all_resources:true` or `channel_ids`
event: {
event_type: TriggerEventTypes.ReactionAdded,
all_resources: false,
},
};
assertEquals(event.type, TriggerTypes.Event);
},
);

await t.step(
"channel event types must not provide both `all_resources:true` and `channel_ids`",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
// @ts-expect-error cannot provide both `all_resources` and `channel_ids`
event: {
event_type: TriggerEventTypes.ReactionAdded,
all_resources: true,
channel_ids: ["C1234"],
},
};
assertEquals(event.type, TriggerTypes.Event);
},
);

await t.step(
"can define a workspace-scoped event with `team_ids`",
() => {
const event: EventTrigger<ExampleWorkflow> = {
type: TriggerTypes.Event,
name: "test",
workflow: "#/workflows/example",
inputs: {},
event: {
event_type: TriggerEventTypes.UserJoinedTeam,
team_ids: ["T1234"],
},
};
assertEquals(event.type, TriggerTypes.Event);
},
};
assertEquals(event.type, TriggerTypes.Event);
);
});

Deno.test("Mock call for event", async (t) => {
Expand Down

0 comments on commit 6b116d5

Please sign in to comment.