@notionhq/apps 0.0.42 → 0.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/connections.d.ts +6 -2
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +9 -0
- package/dist/providers.generated.d.ts +2 -6
- package/dist/providers.generated.d.ts.map +1 -1
- package/dist/providers.generated.js +2 -9
- package/dist/slack.generated.d.ts +178 -0
- package/dist/slack.generated.d.ts.map +1 -1
- package/dist/slack.generated.js +12 -0
- package/package.json +41 -1
- package/skills/connections/SKILL.md +2 -1
- package/src/connections.test.ts +100 -6
- package/src/connections.ts +22 -0
- package/src/providers.generated.ts +8 -11
- package/src/slack.generated.ts +205 -0
- package/src/workflow-connections-types.test.ts +4 -2
- package/src/workflow-types.test.ts +9 -5
- package/src/workflow.test.ts +32 -7
- package/dist/outlook.generated.d.ts +0 -224
- package/dist/outlook.generated.d.ts.map +0 -1
- package/dist/outlook.generated.js +0 -24
- package/src/outlook.generated.ts +0 -249
package/src/connections.test.ts
CHANGED
|
@@ -13,15 +13,59 @@ afterEach(() => {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
describe("connection requirements", () => {
|
|
16
|
+
it("rejects Calendar declarations that omit explicit access", () => {
|
|
17
|
+
expect(() => normalizeConnectionRequirements({ work: { type: "calendar" } })).toThrow(
|
|
18
|
+
"scope and readTeammatesCalendars",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
it.each([
|
|
22
|
+
{ scope: "read" },
|
|
23
|
+
{ readTeammatesCalendars: false },
|
|
24
|
+
{ scope: "write", readTeammatesCalendars: false },
|
|
25
|
+
{ scope: "read", readTeammatesCalendars: "false" },
|
|
26
|
+
])("rejects invalid Calendar options: %j", (calendar) => {
|
|
27
|
+
expect(() =>
|
|
28
|
+
Reflect.apply(normalizeConnectionRequirements, undefined, [
|
|
29
|
+
{ work: { type: "calendar", calendar } },
|
|
30
|
+
]),
|
|
31
|
+
).toThrow("scope and readTeammatesCalendars");
|
|
32
|
+
});
|
|
33
|
+
it("rejects Calendar options on other providers", () => {
|
|
34
|
+
expect(() =>
|
|
35
|
+
normalizeConnectionRequirements({
|
|
36
|
+
work: { type: "mail", calendar: { scope: "read", readTeammatesCalendars: false } },
|
|
37
|
+
}),
|
|
38
|
+
).toThrow("only valid on Calendar");
|
|
39
|
+
});
|
|
40
|
+
it("preserves explicit write and teammate choices in the manifest", () => {
|
|
41
|
+
const connection = connections.calendar({
|
|
42
|
+
scope: "read_write",
|
|
43
|
+
readTeammatesCalendars: true,
|
|
44
|
+
});
|
|
45
|
+
const normalized = normalizeConnectionRequirements({ work: connection });
|
|
46
|
+
if (!connection.calendar) throw new Error("Missing Calendar options");
|
|
47
|
+
connection.calendar.readTeammatesCalendars = false;
|
|
48
|
+
expect(normalized).toEqual([
|
|
49
|
+
{
|
|
50
|
+
type: "calendar",
|
|
51
|
+
key: "work",
|
|
52
|
+
calendar: { scope: "read_write", readTeammatesCalendars: true },
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
16
56
|
it("normalizes object keys into manifest requirements", () => {
|
|
17
57
|
expect(
|
|
18
58
|
normalizeConnectionRequirements({
|
|
19
|
-
work: connections.calendar(),
|
|
59
|
+
work: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
20
60
|
support: connections.slack(),
|
|
21
61
|
team: connections.slack(),
|
|
22
62
|
}),
|
|
23
63
|
).toEqual([
|
|
24
|
-
{
|
|
64
|
+
{
|
|
65
|
+
type: "calendar",
|
|
66
|
+
key: "work",
|
|
67
|
+
calendar: { scope: "read", readTeammatesCalendars: false },
|
|
68
|
+
},
|
|
25
69
|
{ type: "slack", key: "support" },
|
|
26
70
|
{ type: "slack", key: "team" },
|
|
27
71
|
]);
|
|
@@ -44,10 +88,14 @@ describe("connection requirements", () => {
|
|
|
44
88
|
Reflect.apply(normalizeConnectionRequirements, undefined, [[connections.slack()]]),
|
|
45
89
|
).toThrow("must be an object");
|
|
46
90
|
const inherited = { support: connections.slack() };
|
|
47
|
-
Object.setPrototypeOf(inherited, {
|
|
91
|
+
Object.setPrototypeOf(inherited, {
|
|
92
|
+
hidden: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
93
|
+
});
|
|
48
94
|
expect(() => normalizeConnectionRequirements(inherited)).toThrow("only own");
|
|
49
95
|
const hidden = { support: connections.slack() };
|
|
50
|
-
Object.defineProperty(hidden, "hidden", {
|
|
96
|
+
Object.defineProperty(hidden, "hidden", {
|
|
97
|
+
value: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
98
|
+
});
|
|
51
99
|
expect(() => normalizeConnectionRequirements(hidden)).toThrow("enumerable strings");
|
|
52
100
|
const symbolic = { [Symbol("support")]: connections.slack() };
|
|
53
101
|
expect(() => normalizeConnectionRequirements(symbolic)).toThrow("enumerable strings");
|
|
@@ -120,7 +168,7 @@ describe("workflow connection runtime", () => {
|
|
|
120
168
|
const declarations = { support: connections.slack(), team: connections.slack() };
|
|
121
169
|
const runtime = createWorkflowConnections(notion, declarations);
|
|
122
170
|
Object.assign(declarations, {
|
|
123
|
-
support: connections.calendar(),
|
|
171
|
+
support: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
124
172
|
injected: connections.slack(),
|
|
125
173
|
});
|
|
126
174
|
await runtime.support.findUserByEmail({ email: "support@example.com" });
|
|
@@ -194,6 +242,52 @@ describe("workflow connection runtime", () => {
|
|
|
194
242
|
]);
|
|
195
243
|
});
|
|
196
244
|
|
|
245
|
+
it("routes Slack channel writes through their configured connection", async () => {
|
|
246
|
+
vi.stubEnv(
|
|
247
|
+
"NOTION_CONNECTION_BINDINGS",
|
|
248
|
+
'[{"key":"support","type":"slack","id":"support-id"}]',
|
|
249
|
+
);
|
|
250
|
+
const notion = new Client({ auth: "test" });
|
|
251
|
+
const request = vi.spyOn(notion, "request").mockResolvedValue({ result: {} });
|
|
252
|
+
const slack = createWorkflowConnections(notion, {
|
|
253
|
+
support: connections.slack(),
|
|
254
|
+
}).support;
|
|
255
|
+
|
|
256
|
+
await slack.createThreadInChannel({ channelId: "C123", message: "New update" });
|
|
257
|
+
await slack.replyInThread({
|
|
258
|
+
prefixedThreadUri: "slackMessage://example.slack.com/C123/123/123",
|
|
259
|
+
message: "Reply",
|
|
260
|
+
});
|
|
261
|
+
await slack.updateMessage({
|
|
262
|
+
prefixedMessageUri: "slackMessage://example.slack.com/C123/123/123",
|
|
263
|
+
message: "Edited update",
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(request.mock.calls.map(([input]) => input.body)).toEqual([
|
|
267
|
+
{
|
|
268
|
+
connection: "support-id",
|
|
269
|
+
action: "createThreadInChannel",
|
|
270
|
+
args: { channelId: "C123", message: "New update" },
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
connection: "support-id",
|
|
274
|
+
action: "replyInThread",
|
|
275
|
+
args: {
|
|
276
|
+
prefixedThreadUri: "slackMessage://example.slack.com/C123/123/123",
|
|
277
|
+
message: "Reply",
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
connection: "support-id",
|
|
282
|
+
action: "updateMessage",
|
|
283
|
+
args: {
|
|
284
|
+
prefixedMessageUri: "slackMessage://example.slack.com/C123/123/123",
|
|
285
|
+
message: "Edited update",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
]);
|
|
289
|
+
});
|
|
290
|
+
|
|
197
291
|
it("does not parse missing or malformed bindings until a declared client is used", () => {
|
|
198
292
|
vi.stubEnv("NOTION_CONNECTION_BINDINGS", "not json");
|
|
199
293
|
const runtime = createWorkflowConnections(new Client(), { support: connections.slack() });
|
|
@@ -263,7 +357,7 @@ describe("workflow connection runtime", () => {
|
|
|
263
357
|
const notion = new Client({ auth: "test" });
|
|
264
358
|
const request = vi.spyOn(notion, "request").mockResolvedValue({ result: { accounts: [] } });
|
|
265
359
|
const result = await createWorkflowConnections(notion, {
|
|
266
|
-
calendar: connections.calendar(),
|
|
360
|
+
calendar: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
267
361
|
}).calendar.listCalendars({});
|
|
268
362
|
expect(result.accounts).toEqual([]);
|
|
269
363
|
expect(request).toHaveBeenCalledWith({
|
package/src/connections.ts
CHANGED
|
@@ -13,6 +13,12 @@ import type { ConnectionActionInvoker } from "./connection-actions.js";
|
|
|
13
13
|
export type WorkflowConnection<TProvider extends string = string> = {
|
|
14
14
|
type: TProvider;
|
|
15
15
|
oauth?: OAuthConfiguration;
|
|
16
|
+
calendar?: CalendarConnectionOptions;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type CalendarConnectionOptions = {
|
|
20
|
+
scope: "read" | "read_write";
|
|
21
|
+
readTeammatesCalendars: boolean;
|
|
16
22
|
};
|
|
17
23
|
|
|
18
24
|
export type WorkflowConnectionDeclarations = Readonly<Record<string, WorkflowConnection>>;
|
|
@@ -94,6 +100,19 @@ export function normalizeConnectionRequirements(
|
|
|
94
100
|
"Declare a connection key as its workflow object property, not in the provider options.",
|
|
95
101
|
);
|
|
96
102
|
|
|
103
|
+
if (requirement.type === "calendar") {
|
|
104
|
+
const options = requirement.calendar;
|
|
105
|
+
if (
|
|
106
|
+
!options ||
|
|
107
|
+
!["read", "read_write"].includes(options.scope) ||
|
|
108
|
+
typeof options.readTeammatesCalendars !== "boolean"
|
|
109
|
+
) {
|
|
110
|
+
throw new Error("Calendar connections require scope and readTeammatesCalendars.");
|
|
111
|
+
}
|
|
112
|
+
} else if (requirement.calendar !== undefined) {
|
|
113
|
+
throw new Error("Calendar options are only valid on Calendar connections.");
|
|
114
|
+
}
|
|
115
|
+
|
|
97
116
|
if (requirement.type === "oauth") {
|
|
98
117
|
if (!requirement.oauth)
|
|
99
118
|
throw new Error("OAuth connections require provider configuration.");
|
|
@@ -105,6 +124,9 @@ export function normalizeConnectionRequirements(
|
|
|
105
124
|
return {
|
|
106
125
|
type: requirement.type,
|
|
107
126
|
key,
|
|
127
|
+
...(requirement.calendar === undefined
|
|
128
|
+
? {}
|
|
129
|
+
: { calendar: { ...requirement.calendar } }),
|
|
108
130
|
...(requirement.oauth === undefined
|
|
109
131
|
? {}
|
|
110
132
|
: { oauth: structuredClone(requirement.oauth) }),
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Generated by notion tool-core codegen-script-types --connections. Do not edit.
|
|
2
2
|
import type { ConnectionActionInvoker } from "./connection-actions.js"
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
WorkflowConnection,
|
|
5
|
+
CalendarConnectionOptions,
|
|
6
|
+
} from "./connections.js"
|
|
4
7
|
import { CalendarConnectionClient } from "./calendar.generated.js"
|
|
5
8
|
import { MailConnectionClient } from "./mail.generated.js"
|
|
6
9
|
import { SlackConnectionClient } from "./slack.generated.js"
|
|
@@ -10,7 +13,6 @@ import { CursorConnectionClient } from "./cursor.generated.js"
|
|
|
10
13
|
import { BoxConnectionClient } from "./box.generated.js"
|
|
11
14
|
import { ConfluenceConnectionClient } from "./confluence.generated.js"
|
|
12
15
|
import { GoogleDriveConnectionClient } from "./googleDrive.generated.js"
|
|
13
|
-
import { OutlookConnectionClient } from "./outlook.generated.js"
|
|
14
16
|
import { SalesforceConnectionClient } from "./salesforce.generated.js"
|
|
15
17
|
export const connections = {
|
|
16
18
|
calendar,
|
|
@@ -22,7 +24,6 @@ export const connections = {
|
|
|
22
24
|
box,
|
|
23
25
|
confluence,
|
|
24
26
|
googleDrive,
|
|
25
|
-
outlook,
|
|
26
27
|
salesforce,
|
|
27
28
|
}
|
|
28
29
|
export type ConnectionClients = {
|
|
@@ -35,7 +36,6 @@ export type ConnectionClients = {
|
|
|
35
36
|
box: BoxConnectionClient
|
|
36
37
|
confluence: ConfluenceConnectionClient
|
|
37
38
|
googleDrive: GoogleDriveConnectionClient
|
|
38
|
-
outlook: OutlookConnectionClient
|
|
39
39
|
salesforce: SalesforceConnectionClient
|
|
40
40
|
}
|
|
41
41
|
export function createConnectionClient(
|
|
@@ -61,16 +61,16 @@ export function createConnectionClient(
|
|
|
61
61
|
return new ConfluenceConnectionClient(invoker)
|
|
62
62
|
case "googleDrive":
|
|
63
63
|
return new GoogleDriveConnectionClient(invoker)
|
|
64
|
-
case "outlook":
|
|
65
|
-
return new OutlookConnectionClient(invoker)
|
|
66
64
|
case "salesforce":
|
|
67
65
|
return new SalesforceConnectionClient(invoker)
|
|
68
66
|
default:
|
|
69
67
|
throw new Error("Unsupported connection provider: " + provider)
|
|
70
68
|
}
|
|
71
69
|
}
|
|
72
|
-
function calendar(
|
|
73
|
-
|
|
70
|
+
function calendar(
|
|
71
|
+
options: CalendarConnectionOptions,
|
|
72
|
+
): WorkflowConnection<"calendar"> {
|
|
73
|
+
return { type: "calendar", calendar: { ...options } }
|
|
74
74
|
}
|
|
75
75
|
function mail(): WorkflowConnection<"mail"> {
|
|
76
76
|
return { type: "mail" }
|
|
@@ -96,9 +96,6 @@ function confluence(): WorkflowConnection<"confluence"> {
|
|
|
96
96
|
function googleDrive(): WorkflowConnection<"googleDrive"> {
|
|
97
97
|
return { type: "googleDrive" }
|
|
98
98
|
}
|
|
99
|
-
function outlook(): WorkflowConnection<"outlook"> {
|
|
100
|
-
return { type: "outlook" }
|
|
101
|
-
}
|
|
102
99
|
function salesforce(): WorkflowConnection<"salesforce"> {
|
|
103
100
|
return { type: "salesforce" }
|
|
104
101
|
}
|
package/src/slack.generated.ts
CHANGED
|
@@ -27,6 +27,96 @@ export type AddReactionToMessageScriptOutput = {
|
|
|
27
27
|
message?: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Script input for the "Create a new thread in a Slack channel" effect (`slack.createThreadInChannel`),
|
|
32
|
+
* derived from its Tool Core definition's call_function input projection.
|
|
33
|
+
*
|
|
34
|
+
* Create a new thread in a Slack channel
|
|
35
|
+
*/
|
|
36
|
+
export type CreateThreadInChannelScriptInput =
|
|
37
|
+
| {
|
|
38
|
+
/**
|
|
39
|
+
* The Slack ID of the channel to send the message in.
|
|
40
|
+
*/
|
|
41
|
+
channelId: string
|
|
42
|
+
/**
|
|
43
|
+
* The file IDs of any files attached to the message.
|
|
44
|
+
*/
|
|
45
|
+
attachedFileIds?: Array<string>
|
|
46
|
+
/**
|
|
47
|
+
* Raw Slack user ID that should be the only user able to see the message. Omit for a normal channel message.
|
|
48
|
+
*/
|
|
49
|
+
ephemeralUserId?: string
|
|
50
|
+
/**
|
|
51
|
+
* Message body in standard Markdown. Never use Notion rich-text XML such as <mention>; write Notion references as Markdown links ([label](https://example.com)). Use Slack mention tokens only when a verified Slack ID is available.
|
|
52
|
+
*/
|
|
53
|
+
message: string
|
|
54
|
+
/**
|
|
55
|
+
* Whether to unfurl links in Slack. Defaults to true.
|
|
56
|
+
*/
|
|
57
|
+
unfurlLinks?: boolean
|
|
58
|
+
/**
|
|
59
|
+
* A confirmation token for sending messages with URLs that require user approval. Do NOT provide unless specifically instructed or unless an error provides the token.
|
|
60
|
+
*/
|
|
61
|
+
userConfirmationToken?: string
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
/**
|
|
65
|
+
* The name of the channel without the # symbol. For example, "engineering".
|
|
66
|
+
*/
|
|
67
|
+
channelName: string
|
|
68
|
+
/**
|
|
69
|
+
* The file IDs of any files attached to the message.
|
|
70
|
+
*/
|
|
71
|
+
attachedFileIds?: Array<string>
|
|
72
|
+
/**
|
|
73
|
+
* Raw Slack user ID that should be the only user able to see the message. Omit for a normal channel message.
|
|
74
|
+
*/
|
|
75
|
+
ephemeralUserId?: string
|
|
76
|
+
/**
|
|
77
|
+
* Message body in standard Markdown. Never use Notion rich-text XML such as <mention>; write Notion references as Markdown links ([label](https://example.com)). Use Slack mention tokens only when a verified Slack ID is available.
|
|
78
|
+
*/
|
|
79
|
+
message: string
|
|
80
|
+
/**
|
|
81
|
+
* Whether to unfurl links in Slack. Defaults to true.
|
|
82
|
+
*/
|
|
83
|
+
unfurlLinks?: boolean
|
|
84
|
+
/**
|
|
85
|
+
* A confirmation token for sending messages with URLs that require user approval. Do NOT provide unless specifically instructed or unless an error provides the token.
|
|
86
|
+
*/
|
|
87
|
+
userConfirmationToken?: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Canonical Tool Core output for the "Create a new thread in a Slack channel" effect (`slack.createThreadInChannel`).
|
|
92
|
+
* This is the definition's output schema; the effect's script surface may
|
|
93
|
+
* re-encode it before returning.
|
|
94
|
+
*/
|
|
95
|
+
export type CreateThreadInChannelScriptOutput = {
|
|
96
|
+
channelId: string
|
|
97
|
+
channelUri: string
|
|
98
|
+
from:
|
|
99
|
+
| {
|
|
100
|
+
handle: string
|
|
101
|
+
id: string
|
|
102
|
+
type: "user"
|
|
103
|
+
uri: string
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
type: "bot"
|
|
107
|
+
}
|
|
108
|
+
id: string
|
|
109
|
+
message: string
|
|
110
|
+
threadId: string
|
|
111
|
+
timestamp: string
|
|
112
|
+
uri: string
|
|
113
|
+
url: string
|
|
114
|
+
urls: Array<string>
|
|
115
|
+
channel?: string
|
|
116
|
+
fileIds?: Array<string>
|
|
117
|
+
files?: Array<string>
|
|
118
|
+
}
|
|
119
|
+
|
|
30
120
|
/**
|
|
31
121
|
* Script input for the "Find a Slack user by email" effect (`slack.findUserByEmail`),
|
|
32
122
|
* derived from its Tool Core definition's call_function input projection.
|
|
@@ -428,6 +518,52 @@ export type RemoveReactionFromMessageScriptOutput = {
|
|
|
428
518
|
message?: string
|
|
429
519
|
}
|
|
430
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Script input for the "Reply to Slack thread" effect (`slack.replyInThread`),
|
|
523
|
+
* derived from its Tool Core definition's call_function input projection.
|
|
524
|
+
*
|
|
525
|
+
* Reply in a Slack thread
|
|
526
|
+
*/
|
|
527
|
+
export type ReplyInThreadScriptInput = {
|
|
528
|
+
/**
|
|
529
|
+
* The file IDs of any files attached to the message.
|
|
530
|
+
*/
|
|
531
|
+
attachedFileIds?: Array<string>
|
|
532
|
+
/**
|
|
533
|
+
* Raw Slack user ID that should be the only user able to see the reply. Omit for a normal thread reply.
|
|
534
|
+
*/
|
|
535
|
+
ephemeralUserId?: string
|
|
536
|
+
/**
|
|
537
|
+
* The thread URI to reply to. If there is a Slack message trigger, use the thread-id attribute from the trigger message.
|
|
538
|
+
*/
|
|
539
|
+
prefixedThreadUri: string
|
|
540
|
+
/**
|
|
541
|
+
* The Slack markdown message to post in the thread.
|
|
542
|
+
*/
|
|
543
|
+
message: string
|
|
544
|
+
/**
|
|
545
|
+
* Whether to unfurl links in Slack. Defaults to true.
|
|
546
|
+
*/
|
|
547
|
+
unfurlLinks?: boolean
|
|
548
|
+
/**
|
|
549
|
+
* A confirmation token for sending messages with URLs that require user approval. Do NOT provide unless specifically instructed or unless an error provides the token.
|
|
550
|
+
*/
|
|
551
|
+
userConfirmationToken?: string
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Canonical Tool Core output for the "Reply to Slack thread" effect (`slack.replyInThread`).
|
|
556
|
+
* This is the definition's output schema; the effect's script surface may
|
|
557
|
+
* re-encode it before returning.
|
|
558
|
+
*/
|
|
559
|
+
export type ReplyInThreadScriptOutput = {
|
|
560
|
+
message: string
|
|
561
|
+
url: string
|
|
562
|
+
channelId: string
|
|
563
|
+
threadId: string
|
|
564
|
+
slackMessageId: string
|
|
565
|
+
}
|
|
566
|
+
|
|
431
567
|
/**
|
|
432
568
|
* Script input for the "Search Slack messages" effect (`slack.search`),
|
|
433
569
|
* derived from its Tool Core definition's call_function input projection.
|
|
@@ -478,6 +614,57 @@ export type SearchScriptOutput = {
|
|
|
478
614
|
}>
|
|
479
615
|
}
|
|
480
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Script input for the "Update a Slack message" effect (`slack.updateMessage`),
|
|
619
|
+
* derived from its Tool Core definition's call_function input projection.
|
|
620
|
+
*
|
|
621
|
+
* Update a Slack message
|
|
622
|
+
*/
|
|
623
|
+
export type UpdateMessageScriptInput = {
|
|
624
|
+
/**
|
|
625
|
+
* The URI of the Slack message
|
|
626
|
+
*/
|
|
627
|
+
prefixedMessageUri: string
|
|
628
|
+
/**
|
|
629
|
+
* Message body in standard Markdown. Never use Notion rich-text XML such as <mention>; write Notion references as Markdown links ([label](https://example.com)). Use Slack mention tokens only when a verified Slack ID is available.
|
|
630
|
+
*/
|
|
631
|
+
message: string
|
|
632
|
+
/**
|
|
633
|
+
* Whether to unfurl links in Slack. Defaults to true.
|
|
634
|
+
*/
|
|
635
|
+
unfurlLinks?: boolean
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Canonical Tool Core output for the "Update a Slack message" effect (`slack.updateMessage`).
|
|
640
|
+
* This is the definition's output schema; the effect's script surface may
|
|
641
|
+
* re-encode it before returning.
|
|
642
|
+
*/
|
|
643
|
+
export type UpdateMessageScriptOutput = {
|
|
644
|
+
channelId: string
|
|
645
|
+
channelUri: string
|
|
646
|
+
from:
|
|
647
|
+
| {
|
|
648
|
+
handle: string
|
|
649
|
+
id: string
|
|
650
|
+
type: "user"
|
|
651
|
+
uri: string
|
|
652
|
+
}
|
|
653
|
+
| {
|
|
654
|
+
type: "bot"
|
|
655
|
+
}
|
|
656
|
+
id: string
|
|
657
|
+
message: string
|
|
658
|
+
threadId: string
|
|
659
|
+
timestamp: string
|
|
660
|
+
uri: string
|
|
661
|
+
url: string
|
|
662
|
+
urls: Array<string>
|
|
663
|
+
channel?: string
|
|
664
|
+
fileIds?: Array<string>
|
|
665
|
+
files?: Array<string>
|
|
666
|
+
}
|
|
667
|
+
|
|
481
668
|
/**
|
|
482
669
|
* Script input for the "View Slack file URL" effect (`slack.viewFileUrl`),
|
|
483
670
|
* derived from its Tool Core definition's call_function input projection.
|
|
@@ -512,6 +699,14 @@ export class SlackConnectionClient {
|
|
|
512
699
|
args,
|
|
513
700
|
)
|
|
514
701
|
}
|
|
702
|
+
createThreadInChannel(
|
|
703
|
+
args: CreateThreadInChannelScriptInput,
|
|
704
|
+
): Promise<CreateThreadInChannelScriptOutput> {
|
|
705
|
+
return this.invoke<CreateThreadInChannelScriptOutput>(
|
|
706
|
+
"createThreadInChannel",
|
|
707
|
+
args,
|
|
708
|
+
)
|
|
709
|
+
}
|
|
515
710
|
findUserByEmail(
|
|
516
711
|
args: FindUserByEmailScriptInput,
|
|
517
712
|
): Promise<FindUserByEmailScriptOutput> {
|
|
@@ -557,9 +752,19 @@ export class SlackConnectionClient {
|
|
|
557
752
|
args,
|
|
558
753
|
)
|
|
559
754
|
}
|
|
755
|
+
replyInThread(
|
|
756
|
+
args: ReplyInThreadScriptInput,
|
|
757
|
+
): Promise<ReplyInThreadScriptOutput> {
|
|
758
|
+
return this.invoke<ReplyInThreadScriptOutput>("replyInThread", args)
|
|
759
|
+
}
|
|
560
760
|
search(args: SearchScriptInput): Promise<SearchScriptOutput> {
|
|
561
761
|
return this.invoke<SearchScriptOutput>("search", args)
|
|
562
762
|
}
|
|
763
|
+
updateMessage(
|
|
764
|
+
args: UpdateMessageScriptInput,
|
|
765
|
+
): Promise<UpdateMessageScriptOutput> {
|
|
766
|
+
return this.invoke<UpdateMessageScriptOutput>("updateMessage", args)
|
|
767
|
+
}
|
|
563
768
|
viewFileUrl(args: ViewFileUrlScriptInput): Promise<ViewFileUrlScriptOutput> {
|
|
564
769
|
return this.invoke<ViewFileUrlScriptOutput>("viewFileUrl", args)
|
|
565
770
|
}
|
|
@@ -20,7 +20,9 @@ it("infers only the declared provider clients", () => {
|
|
|
20
20
|
name: "Calendar",
|
|
21
21
|
description: "Calendar only",
|
|
22
22
|
triggers: [events.notionPageCreated()],
|
|
23
|
-
connections: {
|
|
23
|
+
connections: {
|
|
24
|
+
work: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
25
|
+
},
|
|
24
26
|
handler(_event, context) {
|
|
25
27
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<"work">();
|
|
26
28
|
expectTypeOf(context.connections.work.listCalendars).toBeFunction();
|
|
@@ -30,7 +32,7 @@ it("infers only the declared provider clients", () => {
|
|
|
30
32
|
},
|
|
31
33
|
});
|
|
32
34
|
const requirements = {
|
|
33
|
-
calendar: connections.calendar(),
|
|
35
|
+
calendar: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
34
36
|
support: connections.slack(),
|
|
35
37
|
team: connections.slack(),
|
|
36
38
|
};
|
|
@@ -71,7 +71,8 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
71
71
|
{
|
|
72
72
|
name: "valid",
|
|
73
73
|
valid: true,
|
|
74
|
-
connections:
|
|
74
|
+
connections:
|
|
75
|
+
"{ support: connections.slack(), calendar: connections.calendar({ scope: 'read', readTeammatesCalendars: false }) }",
|
|
75
76
|
trigger:
|
|
76
77
|
'events.slackMessage({ connectionKey: "support" }), events.calendarEventCreated({ connectionKey: "calendar" })',
|
|
77
78
|
},
|
|
@@ -102,7 +103,8 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
102
103
|
{
|
|
103
104
|
name: "wrong_provider",
|
|
104
105
|
valid: false,
|
|
105
|
-
connections:
|
|
106
|
+
connections:
|
|
107
|
+
"{ support: connections.calendar({ scope: 'read', readTeammatesCalendars: false }) }",
|
|
106
108
|
trigger: 'events.slackMessage({ connectionKey: "support" })',
|
|
107
109
|
},
|
|
108
110
|
{
|
|
@@ -120,13 +122,15 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
120
122
|
{
|
|
121
123
|
name: "global_invalid",
|
|
122
124
|
valid: false,
|
|
123
|
-
connections:
|
|
125
|
+
connections:
|
|
126
|
+
"{ support: connections.calendar({ scope: 'read', readTeammatesCalendars: false }) }",
|
|
124
127
|
trigger: 'globalTriggers.slackMessage({ connectionKey: "support" })',
|
|
125
128
|
},
|
|
126
129
|
{
|
|
127
130
|
name: "literal_invalid",
|
|
128
131
|
valid: false,
|
|
129
|
-
connections:
|
|
132
|
+
connections:
|
|
133
|
+
"{ support: connections.calendar({ scope: 'read', readTeammatesCalendars: false }) }",
|
|
130
134
|
trigger: '{ type: "slack.message", connectionKey: "support" }',
|
|
131
135
|
},
|
|
132
136
|
{
|
|
@@ -170,7 +174,7 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
170
174
|
name: "keyed_clients",
|
|
171
175
|
valid: true,
|
|
172
176
|
connections:
|
|
173
|
-
"{ support: connections.slack(), team: connections.slack(), work: connections.calendar() }",
|
|
177
|
+
"{ support: connections.slack(), team: connections.slack(), work: connections.calendar({ scope: 'read', readTeammatesCalendars: false }) }",
|
|
174
178
|
trigger: "events.notionPageCreated()",
|
|
175
179
|
handler:
|
|
176
180
|
'(_event, ctx) => { ctx.connections.support.findUserByEmail({ email: "a@example.com" }); ctx.connections.team.findUserByEmail({ email: "b@example.com" }); ctx.connections.work.listCalendars({}); }',
|
package/src/workflow.test.ts
CHANGED
|
@@ -1702,11 +1702,19 @@ describe("workflow", () => {
|
|
|
1702
1702
|
name: "Schedule Follow-up",
|
|
1703
1703
|
description: "Schedules a follow-up after a page is created",
|
|
1704
1704
|
triggers: [events.notionPageCreated()],
|
|
1705
|
-
connections: {
|
|
1705
|
+
connections: {
|
|
1706
|
+
calendar: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
1707
|
+
},
|
|
1706
1708
|
handler: () => {},
|
|
1707
1709
|
});
|
|
1708
1710
|
|
|
1709
|
-
expect(definition.config.connections).toEqual([
|
|
1711
|
+
expect(definition.config.connections).toEqual([
|
|
1712
|
+
{
|
|
1713
|
+
type: "calendar",
|
|
1714
|
+
key: "calendar",
|
|
1715
|
+
calendar: { scope: "read", readTeammatesCalendars: false },
|
|
1716
|
+
},
|
|
1717
|
+
]);
|
|
1710
1718
|
});
|
|
1711
1719
|
|
|
1712
1720
|
it("keeps declaration keys stable after the input object changes", async () => {
|
|
@@ -1721,14 +1729,19 @@ describe("workflow", () => {
|
|
|
1721
1729
|
seen.push(Object.keys(context.connections));
|
|
1722
1730
|
},
|
|
1723
1731
|
});
|
|
1724
|
-
Object.assign(declarations, {
|
|
1732
|
+
Object.assign(declarations, {
|
|
1733
|
+
support: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
1734
|
+
team: connections.slack(),
|
|
1735
|
+
});
|
|
1725
1736
|
expect(definition.config.connections).toEqual([{ type: "slack", key: "support" }]);
|
|
1726
1737
|
await definition.handler(event, { concreteOutput: true });
|
|
1727
1738
|
expect(seen).toEqual([["support"]]);
|
|
1728
1739
|
});
|
|
1729
1740
|
it("rejects inherited declaration objects before cloning", () => {
|
|
1730
1741
|
const declarations = { support: connections.slack() };
|
|
1731
|
-
Object.setPrototypeOf(declarations, {
|
|
1742
|
+
Object.setPrototypeOf(declarations, {
|
|
1743
|
+
extra: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
1744
|
+
});
|
|
1732
1745
|
expect(() =>
|
|
1733
1746
|
workflow({
|
|
1734
1747
|
name: "Inherited keys",
|
|
@@ -1746,7 +1759,12 @@ describe("workflow", () => {
|
|
|
1746
1759
|
name: "Schedule Follow-up",
|
|
1747
1760
|
description: "Schedules a follow-up after a page is created",
|
|
1748
1761
|
triggers: [events.notionPageCreated()],
|
|
1749
|
-
connections: {
|
|
1762
|
+
connections: {
|
|
1763
|
+
"bad/key": connections.calendar({
|
|
1764
|
+
scope: "read",
|
|
1765
|
+
readTeammatesCalendars: false,
|
|
1766
|
+
}),
|
|
1767
|
+
},
|
|
1750
1768
|
handler: () => {},
|
|
1751
1769
|
}),
|
|
1752
1770
|
).toThrowError("Invalid workflow connection key: bad/key");
|
|
@@ -2011,7 +2029,10 @@ describe("connection-bound triggers", () => {
|
|
|
2011
2029
|
const definition = workflow({
|
|
2012
2030
|
name: "Typed triggers",
|
|
2013
2031
|
description: "Uses only declared provider keys",
|
|
2014
|
-
connections: {
|
|
2032
|
+
connections: {
|
|
2033
|
+
support: connections.slack(),
|
|
2034
|
+
calendar: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
2035
|
+
},
|
|
2015
2036
|
triggers: ({ events }) => {
|
|
2016
2037
|
calls += 1;
|
|
2017
2038
|
expectTypeOf(events.slackMessage)
|
|
@@ -2060,7 +2081,11 @@ describe("connection-bound triggers", () => {
|
|
|
2060
2081
|
});
|
|
2061
2082
|
it.each([
|
|
2062
2083
|
{ requirements: {} },
|
|
2063
|
-
{
|
|
2084
|
+
{
|
|
2085
|
+
requirements: {
|
|
2086
|
+
support: connections.calendar({ scope: "read", readTeammatesCalendars: false }),
|
|
2087
|
+
},
|
|
2088
|
+
},
|
|
2064
2089
|
{ requirements: { other: connections.slack() } },
|
|
2065
2090
|
])("rejects missing or incompatible declarations: %j", ({ requirements }) => {
|
|
2066
2091
|
expect(() =>
|