@headless-lms/core 0.2.0 → 0.3.0
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/dist/assets/events.d.ts +39 -21
- package/dist/assets/events.js +1 -1
- package/dist/assets/events.js.map +1 -1
- package/dist/assets/file-urls.d.ts +20 -0
- package/dist/assets/file-urls.js +25 -0
- package/dist/assets/file-urls.js.map +1 -0
- package/dist/assets/index.d.ts +5 -4
- package/dist/assets/index.js +3 -2
- package/dist/assets/model.d.ts +2 -2
- package/dist/assets/ports.d.ts +45 -15
- package/dist/assets/service.d.ts +39 -7
- package/dist/assets/service.js +291 -38
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.d.ts +13 -24
- package/dist/automations/actions.js +168 -43
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +2 -1
- package/dist/automations/catalog.js +229 -13
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.d.ts +14 -0
- package/dist/automations/community-derivations.js +439 -0
- package/dist/automations/community-derivations.js.map +1 -0
- package/dist/automations/community-mail.d.ts +112 -0
- package/dist/automations/community-mail.js +90 -0
- package/dist/automations/community-mail.js.map +1 -0
- package/dist/automations/community-notifications.d.ts +16 -0
- package/dist/automations/community-notifications.js +84 -0
- package/dist/automations/community-notifications.js.map +1 -0
- package/dist/automations/events.d.ts +28 -0
- package/dist/automations/index.d.ts +4 -2
- package/dist/automations/index.js +3 -2
- package/dist/automations/model.d.ts +10 -3
- package/dist/automations/model.js +15 -2
- package/dist/automations/model.js.map +1 -1
- package/dist/automations/ports.d.ts +5 -1
- package/dist/automations/schedule.d.ts +6 -0
- package/dist/automations/schedule.js +41 -0
- package/dist/automations/schedule.js.map +1 -0
- package/dist/automations/seeds.d.ts +17 -0
- package/dist/automations/seeds.js +235 -0
- package/dist/automations/seeds.js.map +1 -0
- package/dist/automations/service.d.ts +22 -3
- package/dist/automations/service.js +306 -43
- package/dist/automations/service.js.map +1 -1
- package/dist/content/events.d.ts +631 -19
- package/dist/content/events.js +44 -1
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +4 -4
- package/dist/content/model.d.ts +2 -2
- package/dist/content/ports.d.ts +71 -18
- package/dist/content/service.d.ts +57 -18
- package/dist/content/service.js +502 -23
- package/dist/content/service.js.map +1 -1
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/authors.d.ts +10 -0
- package/dist/discussion/authors.js +23 -0
- package/dist/discussion/authors.js.map +1 -0
- package/dist/discussion/chats.d.ts +104 -0
- package/dist/discussion/chats.js +798 -0
- package/dist/discussion/chats.js.map +1 -0
- package/dist/discussion/events.d.ts +619 -56
- package/dist/discussion/events.js +106 -3
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +7 -3
- package/dist/discussion/index.js +4 -1
- package/dist/discussion/ports.d.ts +288 -27
- package/dist/discussion/ports.js +6 -1
- package/dist/discussion/ports.js.map +1 -0
- package/dist/discussion/posts.d.ts +103 -0
- package/dist/discussion/posts.js +723 -0
- package/dist/discussion/posts.js.map +1 -0
- package/dist/discussion/service.d.ts +32 -10
- package/dist/discussion/service.js +293 -78
- package/dist/discussion/service.js.map +1 -1
- package/dist/entitlements/events.d.ts +178 -11
- package/dist/entitlements/events.js +28 -1
- package/dist/entitlements/events.js.map +1 -1
- package/dist/entitlements/index.d.ts +5 -4
- package/dist/entitlements/index.js +2 -1
- package/dist/entitlements/model.d.ts +3 -2
- package/dist/entitlements/ports.d.ts +124 -11
- package/dist/entitlements/rules.d.ts +12 -0
- package/dist/entitlements/rules.js +17 -0
- package/dist/entitlements/rules.js.map +1 -0
- package/dist/entitlements/service.d.ts +54 -3
- package/dist/entitlements/service.js +472 -2
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/access.d.ts +6 -0
- package/dist/identity/access.js +7 -0
- package/dist/identity/access.js.map +1 -0
- package/dist/identity/index.d.ts +6 -4
- package/dist/identity/index.js +3 -1
- package/dist/identity/model.d.ts +2 -1
- package/dist/identity/notifications.d.ts +27 -0
- package/dist/identity/notifications.js +101 -0
- package/dist/identity/notifications.js.map +1 -0
- package/dist/identity/ports.d.ts +73 -3
- package/dist/identity/service.d.ts +14 -3
- package/dist/identity/service.js +62 -5
- package/dist/identity/service.js.map +1 -1
- package/dist/integrations/model.js +1 -1
- package/dist/integrations/model.js.map +1 -1
- package/dist/integrations/service.js +2 -2
- package/dist/integrations/service.js.map +1 -1
- package/dist/organizations/access-scopes.d.ts +19 -0
- package/dist/organizations/access-scopes.js +89 -0
- package/dist/organizations/access-scopes.js.map +1 -0
- package/dist/organizations/events.d.ts +74 -32
- package/dist/organizations/events.js +6 -0
- package/dist/organizations/events.js.map +1 -1
- package/dist/organizations/index.d.ts +7 -6
- package/dist/organizations/index.js +4 -3
- package/dist/organizations/members.d.ts +8 -5
- package/dist/organizations/members.js.map +1 -1
- package/dist/organizations/ports.d.ts +14 -11
- package/dist/organizations/roles.d.ts +6 -10
- package/dist/organizations/roles.js +19 -7
- package/dist/organizations/roles.js.map +1 -1
- package/dist/organizations/service.d.ts +20 -11
- package/dist/organizations/service.js +157 -47
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/types.d.ts +1 -10
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/ports.d.ts +6 -1
- package/dist/progress/service.d.ts +3 -1
- package/dist/progress/service.js +6 -2
- package/dist/progress/service.js.map +1 -1
- package/dist/reporting/admin/index.d.ts +4 -0
- package/dist/reporting/admin/index.js +2 -0
- package/dist/reporting/admin/model.d.ts +108 -0
- package/dist/reporting/admin/ports.d.ts +27 -0
- package/dist/reporting/admin/service.d.ts +22 -0
- package/dist/reporting/admin/service.js +38 -0
- package/dist/reporting/admin/service.js.map +1 -0
- package/dist/reporting/member/index.d.ts +5 -0
- package/dist/reporting/member/index.js +2 -0
- package/dist/reporting/member/model.d.ts +124 -0
- package/dist/reporting/member/ports.d.ts +45 -0
- package/dist/reporting/member/service.d.ts +73 -0
- package/dist/reporting/member/service.js +286 -0
- package/dist/reporting/member/service.js.map +1 -0
- package/dist/shared/actor.d.ts +9 -0
- package/dist/shared/errors.d.ts +7 -1
- package/dist/shared/errors.js +10 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +11 -0
- package/dist/shared/id.js +12 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/mailer.d.ts +4 -1
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/network.d.ts +10 -0
- package/dist/shared/network.js +211 -0
- package/dist/shared/network.js.map +1 -0
- package/dist/types/assets.d.ts +2 -2
- package/dist/types/automations.d.ts +10 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +11 -3
- package/dist/types/email-templates.d.ts +121 -0
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +48 -2
- package/dist/types/index.d.ts +9 -9
- package/dist/types/ports.d.ts +6 -0
- package/dist/types/schemas/assets.d.ts +65 -37
- package/dist/types/schemas/assets.js +17 -12
- package/dist/types/schemas/assets.js.map +1 -1
- package/dist/types/schemas/automations.d.ts +18 -1
- package/dist/types/schemas/automations.js +15 -6
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +335 -3
- package/dist/types/schemas/content.js +139 -3
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +370 -3
- package/dist/types/schemas/discussion.js +199 -4
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/entitlements.d.ts +36 -2
- package/dist/types/schemas/entitlements.js +23 -1
- package/dist/types/schemas/entitlements.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +111 -1
- package/dist/types/schemas/identity.js +55 -2
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +8 -8
- package/dist/types/schemas/index.js +8 -8
- package/dist/types/schemas/organizations.d.ts +8 -0
- package/dist/types/schemas/organizations.js +2 -0
- package/dist/types/schemas/organizations.js.map +1 -1
- package/dist/types/schemas/shared.d.ts +3 -1
- package/dist/types/schemas/shared.js +5 -1
- package/dist/types/schemas/shared.js.map +1 -1
- package/package.json +2 -2
- package/dist/reporting/courses/index.d.ts +0 -4
- package/dist/reporting/courses/index.js +0 -2
- package/dist/reporting/courses/model.d.ts +0 -24
- package/dist/reporting/courses/ports.d.ts +0 -13
- package/dist/reporting/courses/service.d.ts +0 -19
- package/dist/reporting/courses/service.js +0 -20
- package/dist/reporting/courses/service.js.map +0 -1
- package/dist/reporting/dashboard/index.d.ts +0 -4
- package/dist/reporting/dashboard/index.js +0 -2
- package/dist/reporting/dashboard/model.d.ts +0 -14
- package/dist/reporting/dashboard/ports.d.ts +0 -13
- package/dist/reporting/dashboard/service.d.ts +0 -19
- package/dist/reporting/dashboard/service.js +0 -20
- package/dist/reporting/dashboard/service.js.map +0 -1
- package/dist/reporting/learn/index.d.ts +0 -5
- package/dist/reporting/learn/index.js +0 -2
- package/dist/reporting/learn/model.d.ts +0 -23
- package/dist/reporting/learn/ports.d.ts +0 -43
- package/dist/reporting/learn/ports.js +0 -1
- package/dist/reporting/learn/service.d.ts +0 -49
- package/dist/reporting/learn/service.js +0 -116
- package/dist/reporting/learn/service.js.map +0 -1
- package/dist/reporting/students/index.d.ts +0 -4
- package/dist/reporting/students/index.js +0 -2
- package/dist/reporting/students/model.d.ts +0 -51
- package/dist/reporting/students/model.js +0 -1
- package/dist/reporting/students/ports.d.ts +0 -15
- package/dist/reporting/students/ports.js +0 -1
- package/dist/reporting/students/service.d.ts +0 -20
- package/dist/reporting/students/service.js +0 -23
- package/dist/reporting/students/service.js.map +0 -1
- /package/dist/reporting/{courses → admin}/model.js +0 -0
- /package/dist/reporting/{courses → admin}/ports.js +0 -0
- /package/dist/reporting/{dashboard → member}/model.js +0 -0
- /package/dist/reporting/{dashboard → member}/ports.js +0 -0
- /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
|
@@ -1,75 +1,189 @@
|
|
|
1
|
+
import { notificationKindSchema } from "../types/schemas/identity.js";
|
|
1
2
|
import { deliverWebhookInputSchema } from "../types/schemas/automations.js";
|
|
3
|
+
import { noopLogger } from "../shared/logger.js";
|
|
2
4
|
import { entitlementEvents } from "../entitlements/events.js";
|
|
3
|
-
import {
|
|
5
|
+
import { ActionFailedError } from "./model.js";
|
|
6
|
+
import { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, publicDataSchema } from "./catalog.js";
|
|
4
7
|
import { webhookHeaders } from "./webhook-signing.js";
|
|
8
|
+
import { address, firedAtOf } from "./community-mail.js";
|
|
9
|
+
import { COMMUNITY_DERIVATIONS } from "./community-derivations.js";
|
|
10
|
+
import { NOTIFICATION_PLACEMENTS } from "./community-notifications.js";
|
|
5
11
|
//#region src/automations/actions.ts
|
|
6
12
|
const SEND_EMAIL_DERIVATIONS = {
|
|
7
13
|
accessGranted: {
|
|
8
|
-
|
|
9
|
-
derive: async (event,
|
|
14
|
+
triggers: ["entitlement.created"],
|
|
15
|
+
derive: async (event, deps) => {
|
|
10
16
|
const result = entitlementEvents.entitlementCreated.safeParse(event);
|
|
11
17
|
if (!result.success) return;
|
|
12
18
|
const entitlement = result.data.data;
|
|
13
|
-
if (!entitlement.contentId) return;
|
|
14
|
-
const
|
|
15
|
-
if (!
|
|
16
|
-
return {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
contentType: content.type
|
|
22
|
-
}
|
|
23
|
-
};
|
|
19
|
+
if (!entitlement.contentId) return [];
|
|
20
|
+
const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);
|
|
21
|
+
if (!content) return;
|
|
22
|
+
return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {
|
|
23
|
+
contentTitle: content.title,
|
|
24
|
+
contentId: content.id,
|
|
25
|
+
contentType: content.type
|
|
26
|
+
});
|
|
24
27
|
}
|
|
25
28
|
},
|
|
26
29
|
accessRevoked: {
|
|
27
|
-
|
|
28
|
-
derive: async (event,
|
|
30
|
+
triggers: ["entitlement.deleted"],
|
|
31
|
+
derive: async (event, deps) => {
|
|
29
32
|
const result = entitlementEvents.entitlementDeleted.safeParse(event);
|
|
30
33
|
if (!result.success) return;
|
|
31
34
|
const entitlement = result.data.data;
|
|
32
|
-
if (!entitlement.contentId) return;
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
35
|
-
return {
|
|
36
|
-
to,
|
|
37
|
-
params: { contentTitle: content.title }
|
|
38
|
-
};
|
|
35
|
+
if (!entitlement.contentId) return [];
|
|
36
|
+
const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);
|
|
37
|
+
if (!content) return;
|
|
38
|
+
return address(deps, event, entitlement.orgId, [entitlement.orgUserId], { contentTitle: content.title });
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
},
|
|
41
|
+
...COMMUNITY_DERIVATIONS
|
|
41
42
|
};
|
|
43
|
+
function reachedNoOne() {
|
|
44
|
+
return {
|
|
45
|
+
recipients: [],
|
|
46
|
+
failed: []
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function assertSomeDelivered(action, outcome) {
|
|
50
|
+
if (outcome.recipients.length === 0 && outcome.failed.length > 0) throw new ActionFailedError(`${action}: delivery failed for all ${outcome.failed.length} recipients`);
|
|
51
|
+
return outcome;
|
|
52
|
+
}
|
|
42
53
|
function isEmailTemplateId(value) {
|
|
43
54
|
return typeof value === "string" && value in ALL_EMAIL_TEMPLATE_IDS;
|
|
44
55
|
}
|
|
45
|
-
/** Throws on any failure — the engine owns retry policy and failure bookkeeping. */
|
|
56
|
+
/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */
|
|
46
57
|
async function executeAction(action, event, deps) {
|
|
47
58
|
switch (action.type) {
|
|
48
59
|
case "sendEmail": {
|
|
49
60
|
const template = action.input["template"];
|
|
50
|
-
if (!isEmailTemplateId(template)) throw new
|
|
61
|
+
if (!isEmailTemplateId(template)) throw new ActionFailedError(`sendEmail: unknown template "${String(template)}"`);
|
|
51
62
|
const derivation = SEND_EMAIL_DERIVATIONS[template];
|
|
52
|
-
if (!derivation || derivation.
|
|
53
|
-
const derived = await derivation.derive(event,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
if (!derivation || !derivation.triggers.includes(event.type)) throw new ActionFailedError(`sendEmail: template "${template}" cannot be derived from event "${event.type}"`);
|
|
64
|
+
const derived = await derivation.derive(event, {
|
|
65
|
+
...deps,
|
|
66
|
+
actionInput: action.input
|
|
67
|
+
});
|
|
68
|
+
if (!derived) throw new ActionFailedError(`sendEmail: event "${event.type}" is missing the data required to derive template "${template}"`);
|
|
69
|
+
const outcome = reachedNoOne();
|
|
70
|
+
for (const recipient of derived) (await send(template, recipient, event, deps) ? outcome.recipients : outcome.failed).push(recipient.orgUserId);
|
|
71
|
+
return assertSomeDelivered("sendEmail", outcome);
|
|
57
72
|
}
|
|
73
|
+
case NOTIFY_ACTION: {
|
|
74
|
+
const kind = notificationKindSchema.safeParse(action.input["kind"]);
|
|
75
|
+
if (!kind.success) throw new ActionFailedError(`${NOTIFY_ACTION}: unknown kind "${String(action.input["kind"])}"`);
|
|
76
|
+
return notify(kind.data, event, {
|
|
77
|
+
...deps,
|
|
78
|
+
actionInput: action.input
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
case "grantAccess":
|
|
82
|
+
await grantAccess(action, event, deps);
|
|
83
|
+
return reachedNoOne();
|
|
84
|
+
case "publishDuePosts":
|
|
85
|
+
await deps.scheduledWork.publishDuePosts(event.orgId, firedAtOf(event));
|
|
86
|
+
return reachedNoOne();
|
|
87
|
+
case "expireDueGrants":
|
|
88
|
+
await deps.scheduledWork.expireDueGrants(event.orgId, firedAtOf(event));
|
|
89
|
+
return reachedNoOne();
|
|
90
|
+
case "sendDigest":
|
|
91
|
+
await deps.scheduledWork.sendDigest(event.orgId, firedAtOf(event));
|
|
92
|
+
return reachedNoOne();
|
|
93
|
+
case "removeUnattachedUploads":
|
|
94
|
+
await deps.scheduledWork.removeUnattachedUploads(event.orgId, firedAtOf(event));
|
|
95
|
+
return reachedNoOne();
|
|
58
96
|
case DELIVER_WEBHOOK_ACTION:
|
|
59
97
|
await deliverWebhook(action, event, deps);
|
|
60
|
-
return;
|
|
61
|
-
default: throw new
|
|
98
|
+
return reachedNoOne();
|
|
99
|
+
default: throw new ActionFailedError(`unknown automation action type "${action.type}"`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function send(template, recipient, event, deps) {
|
|
103
|
+
try {
|
|
104
|
+
await deps.mailer.send(recipient.to, template, recipient.params);
|
|
105
|
+
return true;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
(deps.logger ?? noopLogger).warn("automation email not delivered", {
|
|
108
|
+
orgId: event.orgId,
|
|
109
|
+
automationId: deps.automationId,
|
|
110
|
+
trigger: event.type,
|
|
111
|
+
template,
|
|
112
|
+
orgUserId: recipient.orgUserId,
|
|
113
|
+
err
|
|
114
|
+
});
|
|
115
|
+
return false;
|
|
62
116
|
}
|
|
63
117
|
}
|
|
118
|
+
async function notify(kind, event, deps) {
|
|
119
|
+
const derivation = SEND_EMAIL_DERIVATIONS[kind];
|
|
120
|
+
if (!derivation || !derivation.triggers.includes(event.type)) throw new ActionFailedError(`${NOTIFY_ACTION}: kind "${kind}" cannot be derived from event "${event.type}"`);
|
|
121
|
+
const recipients = await derivation.derive(event, deps);
|
|
122
|
+
if (!recipients) throw new ActionFailedError(`${NOTIFY_ACTION}: event "${event.type}" is missing the data required to derive kind "${kind}"`);
|
|
123
|
+
const preferences = await deps.desk.preferencesFor(event.orgId, recipients.map((recipient) => recipient.userId), kind);
|
|
124
|
+
const placement = NOTIFICATION_PLACEMENTS[kind];
|
|
125
|
+
const notifications = [];
|
|
126
|
+
const outcome = reachedNoOne();
|
|
127
|
+
for (const recipient of recipients) {
|
|
128
|
+
const preference = preferences[recipient.userId];
|
|
129
|
+
const email = preference?.email !== false;
|
|
130
|
+
const inApp = preference?.inApp !== false;
|
|
131
|
+
const emailed = email && await send(kind, recipient, event, deps);
|
|
132
|
+
let placed = false;
|
|
133
|
+
if (inApp) try {
|
|
134
|
+
const placements = await placement.place(event, recipient, deps);
|
|
135
|
+
notifications.push(...placements.map((spot) => ({
|
|
136
|
+
...spot,
|
|
137
|
+
userId: recipient.userId,
|
|
138
|
+
kind
|
|
139
|
+
})));
|
|
140
|
+
placed = true;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
(deps.logger ?? noopLogger).warn("automation notification not placed", {
|
|
143
|
+
orgId: event.orgId,
|
|
144
|
+
automationId: deps.automationId,
|
|
145
|
+
trigger: event.type,
|
|
146
|
+
kind,
|
|
147
|
+
orgUserId: recipient.orgUserId,
|
|
148
|
+
err
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (emailed || placed) outcome.recipients.push(recipient.orgUserId);
|
|
152
|
+
if (email && !emailed || inApp && !placed) outcome.failed.push(recipient.orgUserId);
|
|
153
|
+
}
|
|
154
|
+
await deps.desk.notify(event.orgId, notifications);
|
|
155
|
+
return assertSomeDelivered(NOTIFY_ACTION, outcome);
|
|
156
|
+
}
|
|
157
|
+
function grantRecipient(event) {
|
|
158
|
+
if (typeof event.data !== "object" || event.data === null) return;
|
|
159
|
+
const data = event.data;
|
|
160
|
+
if (event.type === "organization.student.created" && typeof data.id === "string") return data.id;
|
|
161
|
+
if (typeof data.orgUserId === "string") return data.orgUserId;
|
|
162
|
+
}
|
|
163
|
+
async function grantAccess(action, event, deps) {
|
|
164
|
+
const contentId = typeof action.input.contentId === "string" ? action.input.contentId : null;
|
|
165
|
+
const bundleId = typeof action.input.bundleId === "string" ? action.input.bundleId : null;
|
|
166
|
+
if (contentId == null === (bundleId == null)) throw new ActionFailedError("grantAccess: provide exactly one of contentId or bundleId");
|
|
167
|
+
const orgUserId = grantRecipient(event);
|
|
168
|
+
if (!orgUserId) throw new ActionFailedError(`grantAccess: event "${event.type}" has no recipient`);
|
|
169
|
+
const days = action.input.expiresInDays;
|
|
170
|
+
const expiresAt = typeof days === "number" && Number.isFinite(days) ? new Date(Date.now() + days * 864e5) : null;
|
|
171
|
+
await deps.grants.grant(event.orgId, {
|
|
172
|
+
orgUserId,
|
|
173
|
+
contentId,
|
|
174
|
+
bundleId,
|
|
175
|
+
expiresAt
|
|
176
|
+
});
|
|
177
|
+
}
|
|
64
178
|
async function deliverWebhook(action, event, deps) {
|
|
65
179
|
const input = deliverWebhookInputSchema.safeParse(action.input);
|
|
66
|
-
if (!input.success) throw new
|
|
180
|
+
if (!input.success) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);
|
|
67
181
|
const publicData = publicDataSchema(event.type);
|
|
68
|
-
if (!publicData) throw new
|
|
182
|
+
if (!publicData) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: event "${event.type}" is not published`);
|
|
69
183
|
const data = publicData.safeParse(event.data);
|
|
70
|
-
if (!data.success) throw new
|
|
184
|
+
if (!data.success) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: event "${event.type}" does not match its published shape`);
|
|
71
185
|
const secret = (await deps.credentials.reveal(event.orgId, input.data.secretRef))?.["secret"];
|
|
72
|
-
if (typeof secret !== "string") throw new
|
|
186
|
+
if (typeof secret !== "string") throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);
|
|
73
187
|
const body = JSON.stringify({
|
|
74
188
|
id: event.id,
|
|
75
189
|
type: event.type,
|
|
@@ -79,12 +193,23 @@ async function deliverWebhook(action, event, deps) {
|
|
|
79
193
|
data: data.data
|
|
80
194
|
});
|
|
81
195
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
196
|
+
let response;
|
|
197
|
+
try {
|
|
198
|
+
response = await deps.webhooks.send({
|
|
199
|
+
url: input.data.url,
|
|
200
|
+
headers: webhookHeaders(secret, event.id, timestamp, body),
|
|
201
|
+
body
|
|
202
|
+
});
|
|
203
|
+
} catch (err) {
|
|
204
|
+
(deps.logger ?? noopLogger).warn("webhook endpoint not reached", {
|
|
205
|
+
orgId: event.orgId,
|
|
206
|
+
automationId: deps.automationId,
|
|
207
|
+
eventId: event.id,
|
|
208
|
+
err
|
|
209
|
+
});
|
|
210
|
+
throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);
|
|
211
|
+
}
|
|
212
|
+
if (response.status < 200 || response.status >= 300) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);
|
|
88
213
|
}
|
|
89
214
|
//#endregion
|
|
90
215
|
export { SEND_EMAIL_DERIVATIONS, executeAction };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId, EmailTemplateParams } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { CredentialStore, DomainEvent } from '../shared/ports.js';\nimport type { AutomationAction } from './model.js';\nimport type { WebhookSender } from './ports.js';\nimport { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, publicDataSchema } from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\n\nexport interface ActionDeps {\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n credentials: Pick<CredentialStore, 'reveal'>;\n webhooks: WebhookSender;\n}\n\ninterface SendEmailDerivation<K extends EmailTemplateId> {\n /** The only event type this template's params can be derived from. */\n trigger: string;\n /** Undefined = the recipient or content behind the event's ids cannot be resolved. */\n derive(\n event: DomainEvent,\n lookups: MailerLookups,\n ): Promise<{ to: string; params: EmailTemplateParams[K] } | undefined>;\n}\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: SendEmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n trigger: 'entitlement.created',\n derive: async (event, lookups) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n // Bundle grants carry no content id — this email is per content item.\n if (!entitlement.contentId) {\n return undefined;\n }\n const [to, content] = await Promise.all([\n lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId),\n lookups.contentInfo(entitlement.orgId, entitlement.contentId),\n ]);\n if (!to || !content) {\n return undefined;\n }\n return {\n to,\n params: { contentTitle: content.title, contentId: content.id, contentType: content.type },\n };\n },\n },\n accessRevoked: {\n trigger: 'entitlement.deleted',\n derive: async (event, lookups) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n // Bundle grants carry no content id — this email is per content item.\n if (!entitlement.contentId) {\n return undefined;\n }\n const [to, content] = await Promise.all([\n lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId),\n lookups.contentInfo(entitlement.orgId, entitlement.contentId),\n ]);\n if (!to || !content) {\n return undefined;\n }\n return {\n to,\n params: { contentTitle: content.title },\n };\n },\n },\n};\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new Error(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || derivation.trigger !== event.type) {\n throw new Error(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, deps.lookups);\n if (!derived) {\n throw new Error(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n await deps.mailer.send(derived.to, template, derived.params);\n return;\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return;\n }\n default: {\n throw new Error(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new Error(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new Error(`${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`);\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new Error(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new Error(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n const response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n if (response.status < 200 || response.status >= 300) {\n throw new Error(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId, NotificationKind } from '../types/index.js';\nimport { deliverWebhookInputSchema, notificationKindSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { NewNotification } from '../identity/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { ActionFailedError, type AutomationAction } from './model.js';\nimport type { WebhookResponse } from './ports.js';\nimport {\n ALL_EMAIL_TEMPLATE_IDS,\n DELIVER_WEBHOOK_ACTION,\n NOTIFY_ACTION,\n publicDataSchema,\n} from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\nimport { address, firedAtOf, type ActionDeps, type EmailRecipient } from './community-mail.js';\nimport { COMMUNITY_DERIVATIONS, type EmailDerivation } from './community-derivations.js';\nimport { NOTIFICATION_PLACEMENTS } from './community-notifications.js';\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: EmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n contentId: content.id,\n contentType: content.type,\n });\n },\n },\n accessRevoked: {\n triggers: ['entitlement.deleted'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n });\n },\n },\n ...COMMUNITY_DERIVATIONS,\n};\n\nexport type { ActionDeps, EmailRecipient };\n\n/** Who an action reached, and who a delivery to failed for (a partial failure does not throw). */\nexport interface ActionOutcome {\n recipients: string[];\n failed: string[];\n}\n\nfunction reachedNoOne(): ActionOutcome {\n return { recipients: [], failed: [] };\n}\n\nfunction assertSomeDelivered(action: string, outcome: ActionOutcome): ActionOutcome {\n if (outcome.recipients.length === 0 && outcome.failed.length > 0) {\n throw new ActionFailedError(\n `${action}: delivery failed for all ${outcome.failed.length} recipients`,\n );\n }\n return outcome;\n}\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new ActionFailedError(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, { ...deps, actionInput: action.input });\n if (!derived) {\n throw new ActionFailedError(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n const outcome = reachedNoOne();\n for (const recipient of derived) {\n const list = (await send(template, recipient, event, deps))\n ? outcome.recipients\n : outcome.failed;\n list.push(recipient.orgUserId);\n }\n return assertSomeDelivered('sendEmail', outcome);\n }\n case NOTIFY_ACTION: {\n const kind = notificationKindSchema.safeParse(action.input['kind']);\n if (!kind.success) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: unknown kind \"${String(action.input['kind'])}\"`,\n );\n }\n return notify(kind.data, event, { ...deps, actionInput: action.input });\n }\n case 'grantAccess': {\n await grantAccess(action, event, deps);\n return reachedNoOne();\n }\n case 'publishDuePosts': {\n await deps.scheduledWork.publishDuePosts(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'expireDueGrants': {\n await deps.scheduledWork.expireDueGrants(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'sendDigest': {\n await deps.scheduledWork.sendDigest(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'removeUnattachedUploads': {\n await deps.scheduledWork.removeUnattachedUploads(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return reachedNoOne();\n }\n default: {\n throw new ActionFailedError(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function send<K extends EmailTemplateId>(\n template: K,\n recipient: EmailRecipient<K>,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<boolean> {\n try {\n await deps.mailer.send(recipient.to, template, recipient.params);\n return true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation email not delivered', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n template,\n orgUserId: recipient.orgUserId,\n err,\n });\n return false;\n }\n}\n\nasync function notify<K extends NotificationKind>(\n kind: K,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n const derivation = SEND_EMAIL_DERIVATIONS[kind] as EmailDerivation<K> | undefined;\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: kind \"${kind}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const recipients = await derivation.derive(event, deps);\n if (!recipients) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: event \"${event.type}\" is missing the data required to derive kind \"${kind}\"`,\n );\n }\n const preferences = await deps.desk.preferencesFor(\n event.orgId,\n recipients.map((recipient) => recipient.userId),\n kind,\n );\n const placement = NOTIFICATION_PLACEMENTS[kind];\n const notifications: NewNotification[] = [];\n const outcome = reachedNoOne();\n for (const recipient of recipients) {\n const preference = preferences[recipient.userId];\n const email = preference?.email !== false;\n const inApp = preference?.inApp !== false;\n const emailed = email && (await send(kind, recipient, event, deps));\n let placed = false;\n if (inApp) {\n try {\n const placements = await placement.place(event, recipient, deps);\n notifications.push(\n ...placements.map((spot) => ({ ...spot, userId: recipient.userId, kind })),\n );\n placed = true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation notification not placed', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n kind,\n orgUserId: recipient.orgUserId,\n err,\n });\n }\n }\n if (emailed || placed) {\n outcome.recipients.push(recipient.orgUserId);\n }\n if ((email && !emailed) || (inApp && !placed)) {\n outcome.failed.push(recipient.orgUserId);\n }\n }\n await deps.desk.notify(event.orgId, notifications);\n return assertSomeDelivered(NOTIFY_ACTION, outcome);\n}\n\nfunction grantRecipient(event: DomainEvent): string | undefined {\n if (typeof event.data !== 'object' || event.data === null) {\n return undefined;\n }\n const data = event.data as Record<string, unknown>;\n if (event.type === 'organization.student.created' && typeof data.id === 'string') {\n return data.id;\n }\n if (typeof data.orgUserId === 'string') {\n return data.orgUserId;\n }\n return undefined;\n}\n\nasync function grantAccess(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const contentId = typeof action.input.contentId === 'string' ? action.input.contentId : null;\n const bundleId = typeof action.input.bundleId === 'string' ? action.input.bundleId : null;\n if ((contentId == null) === (bundleId == null)) {\n throw new ActionFailedError('grantAccess: provide exactly one of contentId or bundleId');\n }\n const orgUserId = grantRecipient(event);\n if (!orgUserId) {\n throw new ActionFailedError(`grantAccess: event \"${event.type}\" has no recipient`);\n }\n const days = action.input.expiresInDays;\n const expiresAt =\n typeof days === 'number' && Number.isFinite(days)\n ? new Date(Date.now() + days * 86_400_000)\n : null;\n await deps.grants.grant(event.orgId, { orgUserId, contentId, bundleId, expiresAt });\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`,\n );\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n let response: WebhookResponse;\n try {\n response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n } catch (err) {\n (deps.logger ?? noopLogger).warn('webhook endpoint not reached', {\n orgId: event.orgId,\n automationId: deps.automationId,\n eventId: event.id,\n err,\n });\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);\n }\n if (response.status < 200 || response.status >= 300) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -7,9 +7,10 @@ import { z } from "zod";
|
|
|
7
7
|
/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
|
|
8
8
|
declare const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true>;
|
|
9
9
|
declare const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
|
|
10
|
+
declare const NOTIFY_ACTION = "notify";
|
|
10
11
|
declare function catalogActions(): AvailableActions;
|
|
11
12
|
declare function catalogTriggers(): AvailableTriggers['triggers'];
|
|
12
13
|
declare function publicDataSchema(type: string): z.ZodType | undefined;
|
|
13
14
|
//#endregion
|
|
14
|
-
export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema };
|
|
15
|
+
export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, catalogActions, catalogTriggers, publicDataSchema };
|
|
15
16
|
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { notificationKindSchema } from "../types/schemas/identity.js";
|
|
2
2
|
import { assetEvents } from "../assets/events.js";
|
|
3
|
+
import { organizationEvents } from "../organizations/events.js";
|
|
4
|
+
import { entitlementEvents } from "../entitlements/events.js";
|
|
3
5
|
import { contentEvents } from "../content/events.js";
|
|
4
6
|
import { discussionEvents } from "../discussion/events.js";
|
|
5
7
|
import { identityEvents } from "../identity/events.js";
|
|
6
8
|
import { integrationEvents } from "../integrations/events.js";
|
|
7
|
-
import { organizationEvents } from "../organizations/events.js";
|
|
8
9
|
import { progressEvents } from "../progress/events.js";
|
|
9
10
|
//#region src/automations/catalog.ts
|
|
10
11
|
/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
|
|
@@ -17,20 +18,91 @@ const ALL_EMAIL_TEMPLATE_IDS = {
|
|
|
17
18
|
emailVerification: true,
|
|
18
19
|
accessGranted: true,
|
|
19
20
|
accessRevoked: true,
|
|
20
|
-
courseCompleted: true
|
|
21
|
+
courseCompleted: true,
|
|
22
|
+
welcomeToCommunity: true,
|
|
23
|
+
membershipEndingSoon: true,
|
|
24
|
+
membershipEnded: true,
|
|
25
|
+
newPostInSpace: true,
|
|
26
|
+
yourPostWasPublished: true,
|
|
27
|
+
yourPostWasRemoved: true,
|
|
28
|
+
newCommentOnPost: true,
|
|
29
|
+
replyToYourComment: true,
|
|
30
|
+
yourCommentWasRemoved: true,
|
|
31
|
+
requestApproved: true,
|
|
32
|
+
requestDeclined: true,
|
|
33
|
+
invitedToSpace: true,
|
|
34
|
+
messageRequest: true,
|
|
35
|
+
newMessage: true,
|
|
36
|
+
addedToGroupChat: true,
|
|
37
|
+
youAreNowAModerator: true,
|
|
38
|
+
digest: true,
|
|
39
|
+
postAwaitingReview: true,
|
|
40
|
+
joinRequest: true,
|
|
41
|
+
somethingWasFlagged: true,
|
|
42
|
+
messageWasFlagged: true
|
|
21
43
|
};
|
|
22
44
|
const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
|
|
45
|
+
const NOTIFY_ACTION = "notify";
|
|
23
46
|
function catalogActions() {
|
|
24
|
-
return [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
type: "sendEmail",
|
|
50
|
+
description: "Send a transactional email using a built-in template.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
required: ["template"],
|
|
54
|
+
properties: { template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) } }
|
|
55
|
+
},
|
|
56
|
+
source: "system"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: NOTIFY_ACTION,
|
|
60
|
+
description: "Tell each person concerned, in the app and by email, as they chose.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: "object",
|
|
63
|
+
required: ["kind"],
|
|
64
|
+
properties: { kind: { enum: [...notificationKindSchema.options] } }
|
|
65
|
+
},
|
|
66
|
+
source: "system"
|
|
31
67
|
},
|
|
32
|
-
|
|
33
|
-
|
|
68
|
+
{
|
|
69
|
+
type: "grantAccess",
|
|
70
|
+
description: "Grant a content item or bundle to the person on the event.",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
contentId: { type: "string" },
|
|
75
|
+
bundleId: { type: "string" },
|
|
76
|
+
expiresInDays: { type: "number" }
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
source: "system"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: "publishDuePosts",
|
|
83
|
+
description: "Submit scheduled posts whose time has passed.",
|
|
84
|
+
inputSchema: { type: "object" },
|
|
85
|
+
source: "system"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: "expireDueGrants",
|
|
89
|
+
description: "Expire grants whose time has passed.",
|
|
90
|
+
inputSchema: { type: "object" },
|
|
91
|
+
source: "system"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: "sendDigest",
|
|
95
|
+
description: "Open the daily digest for every community.",
|
|
96
|
+
inputSchema: { type: "object" },
|
|
97
|
+
source: "system"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "removeUnattachedUploads",
|
|
101
|
+
description: "Remove member uploads older than an hour that nothing references.",
|
|
102
|
+
inputSchema: { type: "object" },
|
|
103
|
+
source: "system"
|
|
104
|
+
}
|
|
105
|
+
];
|
|
34
106
|
}
|
|
35
107
|
const TRIGGERS = [
|
|
36
108
|
{
|
|
@@ -75,6 +147,12 @@ const TRIGGERS = [
|
|
|
75
147
|
category: "Organization",
|
|
76
148
|
description: "a user was removed from an organization"
|
|
77
149
|
},
|
|
150
|
+
{
|
|
151
|
+
event: organizationEvents.orgUserUpdated,
|
|
152
|
+
label: "Member updated",
|
|
153
|
+
category: "Organization",
|
|
154
|
+
description: "an organization member was updated"
|
|
155
|
+
},
|
|
78
156
|
{
|
|
79
157
|
event: organizationEvents.inviteCreated,
|
|
80
158
|
label: "Invite sent",
|
|
@@ -213,6 +291,48 @@ const TRIGGERS = [
|
|
|
213
291
|
category: "Downloads",
|
|
214
292
|
description: "download assets were reordered"
|
|
215
293
|
},
|
|
294
|
+
{
|
|
295
|
+
event: contentEvents.communityCreated,
|
|
296
|
+
label: "Community created",
|
|
297
|
+
category: "Communities",
|
|
298
|
+
description: "a community was created"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
event: contentEvents.communityUpdated,
|
|
302
|
+
label: "Community updated",
|
|
303
|
+
category: "Communities",
|
|
304
|
+
description: "a community was updated"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
event: contentEvents.communityDeleted,
|
|
308
|
+
label: "Community deleted",
|
|
309
|
+
category: "Communities",
|
|
310
|
+
description: "a community was deleted"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
event: contentEvents.spaceCreated,
|
|
314
|
+
label: "Space created",
|
|
315
|
+
category: "Communities",
|
|
316
|
+
description: "a space was created"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
event: contentEvents.spaceUpdated,
|
|
320
|
+
label: "Space updated",
|
|
321
|
+
category: "Communities",
|
|
322
|
+
description: "a space was updated"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
event: contentEvents.spaceDeleted,
|
|
326
|
+
label: "Space deleted",
|
|
327
|
+
category: "Communities",
|
|
328
|
+
description: "a space was deleted"
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
event: contentEvents.spacesReordered,
|
|
332
|
+
label: "Spaces reordered",
|
|
333
|
+
category: "Communities",
|
|
334
|
+
description: "spaces were reordered"
|
|
335
|
+
},
|
|
216
336
|
{
|
|
217
337
|
event: entitlementEvents.entitlementCreated,
|
|
218
338
|
label: "Access granted",
|
|
@@ -237,6 +357,30 @@ const TRIGGERS = [
|
|
|
237
357
|
category: "Access",
|
|
238
358
|
description: "an entitlement passed its expiry"
|
|
239
359
|
},
|
|
360
|
+
{
|
|
361
|
+
event: entitlementEvents.entitlementEndingSoon,
|
|
362
|
+
label: "Access ending soon",
|
|
363
|
+
category: "Access",
|
|
364
|
+
description: "an entitlement expires within a week"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
event: entitlementEvents.spaceMemberCreated,
|
|
368
|
+
label: "Space member created",
|
|
369
|
+
category: "Access",
|
|
370
|
+
description: "a person was added to a space"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
event: entitlementEvents.spaceMemberUpdated,
|
|
374
|
+
label: "Space member updated",
|
|
375
|
+
category: "Access",
|
|
376
|
+
description: "a space membership changed"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
event: entitlementEvents.spaceMemberDeleted,
|
|
380
|
+
label: "Space member removed",
|
|
381
|
+
category: "Access",
|
|
382
|
+
description: "a space membership was removed"
|
|
383
|
+
},
|
|
240
384
|
{
|
|
241
385
|
event: progressEvents.progressStarted,
|
|
242
386
|
label: "Progress started",
|
|
@@ -291,6 +435,78 @@ const TRIGGERS = [
|
|
|
291
435
|
category: "Discussion",
|
|
292
436
|
description: "a comment was reported"
|
|
293
437
|
},
|
|
438
|
+
{
|
|
439
|
+
event: discussionEvents.postCreated,
|
|
440
|
+
label: "Post created",
|
|
441
|
+
category: "Discussion",
|
|
442
|
+
description: "a post was created"
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
event: discussionEvents.postUpdated,
|
|
446
|
+
label: "Post updated",
|
|
447
|
+
category: "Discussion",
|
|
448
|
+
description: "a post was updated"
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
event: discussionEvents.postPublished,
|
|
452
|
+
label: "Post published",
|
|
453
|
+
category: "Discussion",
|
|
454
|
+
description: "a post was published"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
event: discussionEvents.postRemoved,
|
|
458
|
+
label: "Post removed",
|
|
459
|
+
category: "Discussion",
|
|
460
|
+
description: "a post was removed"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
event: discussionEvents.postReported,
|
|
464
|
+
label: "Post reported",
|
|
465
|
+
category: "Discussion",
|
|
466
|
+
description: "a post was reported"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
event: discussionEvents.chatCreated,
|
|
470
|
+
label: "Chat created",
|
|
471
|
+
category: "Discussion",
|
|
472
|
+
description: "a chat was created"
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
event: discussionEvents.chatUpdated,
|
|
476
|
+
label: "Chat updated",
|
|
477
|
+
category: "Discussion",
|
|
478
|
+
description: "a chat was updated"
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
event: discussionEvents.chatParticipantAdded,
|
|
482
|
+
label: "Chat participant added",
|
|
483
|
+
category: "Discussion",
|
|
484
|
+
description: "a person was added to a chat"
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
event: discussionEvents.messageSent,
|
|
488
|
+
label: "Message sent",
|
|
489
|
+
category: "Discussion",
|
|
490
|
+
description: "a message was sent"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
event: discussionEvents.messageDeleted,
|
|
494
|
+
label: "Message deleted",
|
|
495
|
+
category: "Discussion",
|
|
496
|
+
description: "a message was deleted"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
event: discussionEvents.messageReported,
|
|
500
|
+
label: "Message reported",
|
|
501
|
+
category: "Discussion",
|
|
502
|
+
description: "a message was reported"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
event: discussionEvents.digestDue,
|
|
506
|
+
label: "Digest due",
|
|
507
|
+
category: "Discussion",
|
|
508
|
+
description: "a community digest period opened"
|
|
509
|
+
},
|
|
294
510
|
{
|
|
295
511
|
event: assetEvents.assetCreated,
|
|
296
512
|
label: "Upload requested",
|
|
@@ -320,6 +536,6 @@ function publicDataSchema(type) {
|
|
|
320
536
|
return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;
|
|
321
537
|
}
|
|
322
538
|
//#endregion
|
|
323
|
-
export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema };
|
|
539
|
+
export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, catalogActions, catalogTriggers, publicDataSchema };
|
|
324
540
|
|
|
325
541
|
//# sourceMappingURL=catalog.js.map
|