Unified · Split
| @@ -16,6 +16,7 @@ import { · 15 unmodified lines | ||
| 16 | 16 | } from "./memberships"; |
| 17 | 17 | import { ensureUniqueOrganizationSlug } from "./naming"; |
| 18 | 18 | import { seedWorkspaceMembersFromOrganization } from "./workspace_membership_projection"; |
| 19 | +import { asPayload } from "../lib/object"; | |
| 19 | 20 | |
| 20 | 21 | type WorkosMembershipEventData = { |
| 21 | 22 | id: string; |
| @@ -27,6 +28,30 @@ type WorkosMembershipEventData = { · 5 unmodified lines | ||
| 27 | 28 | status?: string; |
| 28 | 29 | }; |
| 29 | 30 | |
| 31 | +type ResolvedMembershipTarget = { | |
| 32 | + account: Doc<"accounts"> | null; | |
| 33 | + organization: Doc<"organizations"> | null; | |
| 34 | + fallbackRole?: Doc<"workspaceMembers">["role"]; | |
| 35 | +}; | |
| 36 | + | |
| 37 | +function parseMembershipEventData(value: unknown): WorkosMembershipEventData | null { | |
| 38 | + const record = asPayload(value); | |
| 39 | + if (typeof record.id !== "string" || record.id.trim().length === 0) { | |
| 40 | + return null; | |
| 41 | + } | |
| 42 | + | |
| 43 | + const role = asPayload(record.role); | |
| 44 | + return { | |
| 45 | + id: record.id, | |
| 46 | + user_id: typeof record.user_id === "string" ? record.user_id : undefined, | |
| 47 | + userId: typeof record.userId === "string" ? record.userId : undefined, | |
| 48 | + organization_id: typeof record.organization_id === "string" ? record.organization_id : undefined, | |
| 49 | + organizationId: typeof record.organizationId === "string" ? record.organizationId : undefined, | |
| 50 | + role: typeof role.slug === "string" ? { slug: role.slug } : undefined, | |
| 51 | + status: typeof record.status === "string" ? record.status : undefined, | |
| 52 | + }; | |
| 53 | +} | |
| 54 | + | |
| 30 | 55 | function deriveOrganizationRoleFromWorkosSlug(workosRoleSlug?: string): Doc<"organizationMembers">["role"] { |
| 31 | 56 | return workosRoleSlug === "admin" ? "admin" : "member"; |
| 32 | 57 | } |
| @@ -34,7 +59,7 @@ function deriveOrganizationRoleFromWorkosSlug(workosRoleSlug?: string): Doc<"org · 1 unmodified line | ||
| 34 | 59 | async function resolveMembershipAccountAndOrganization( |
| 35 | 60 | ctx: Pick<MutationCtx, "db">, |
| 36 | 61 | data: WorkosMembershipEventData, |
| 37 | −) { | |
| 62 | +): Promise<ResolvedMembershipTarget> { | |
| 38 | 63 | const workosUserId = data.user_id ?? data.userId; |
| 39 | 64 | const workosOrgId = data.organization_id ?? data.organizationId; |
| 40 | 65 | |
| @@ -48,7 +73,6 @@ async function resolveMembershipAccountAndOrganization( · 7 unmodified lines | ||
| 48 | 73 | return { |
| 49 | 74 | account, |
| 50 | 75 | organization, |
| 51 | − fallbackRole: undefined as Doc<"workspaceMembers">["role"] | undefined, | |
| 52 | 76 | }; |
| 53 | 77 | } |
| 54 | 78 | } |
| @@ -61,7 +85,6 @@ async function resolveMembershipAccountAndOrganization( · 6 unmodified lines | ||
| 61 | 85 | return { |
| 62 | 86 | account: null, |
| 63 | 87 | organization: null, |
| 64 | − fallbackRole: undefined as Doc<"workspaceMembers">["role"] | undefined, | |
| 65 | 88 | }; |
| 66 | 89 | } |
| 67 | 90 | |
| @@ -251,7 +274,10 @@ export const workosEventHandlers = { · 183 unmodified lines | ||
| 251 | 274 | |
| 252 | 275 | "organization_membership.created": async (ctx, event) => { |
| 253 | 276 | const now = Date.now(); |
| 254 | − const data = event.data as WorkosMembershipEventData; | |
| 277 | + const data = parseMembershipEventData(event.data); | |
| 278 | + if (!data) { | |
| 279 | + return; | |
| 280 | + } | |
| 255 | 281 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 256 | 282 | if (!account || !organization) return; |
| 257 | 283 | |
| @@ -279,7 +305,10 @@ export const workosEventHandlers = { · 21 unmodified lines | ||
| 279 | 305 | |
| 280 | 306 | "organization_membership.updated": async (ctx, event) => { |
| 281 | 307 | const now = Date.now(); |
| 282 | − const data = event.data as WorkosMembershipEventData; | |
| 308 | + const data = parseMembershipEventData(event.data); | |
| 309 | + if (!data) { | |
| 310 | + return; | |
| 311 | + } | |
| 283 | 312 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 284 | 313 | |
| 285 | 314 | if (!account || !organization) { |
| @@ -310,7 +339,10 @@ export const workosEventHandlers = { · 24 unmodified lines | ||
| 310 | 339 | |
| 311 | 340 | "organization_membership.deleted": async (ctx, event) => { |
| 312 | 341 | const now = Date.now(); |
| 313 | − const data = event.data as WorkosMembershipEventData; | |
| 342 | + const data = parseMembershipEventData(event.data); | |
| 343 | + if (!data) { | |
| 344 | + return; | |
| 345 | + } | |
| 314 | 346 | const { account, organization, fallbackRole } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 315 | 347 | if (!account || !organization) { |
| 316 | 348 | return; |