Unified · Split
| @@ -16,6 +16,7 @@ import { · 15 unmodified lines | |||
| 16 | } from "./memberships"; | 16 | } from "./memberships"; |
| 17 | import { ensureUniqueOrganizationSlug } from "./naming"; | 17 | import { ensureUniqueOrganizationSlug } from "./naming"; |
| 18 | import { seedWorkspaceMembersFromOrganization } from "./workspace_membership_projection"; | 18 | import { seedWorkspaceMembersFromOrganization } from "./workspace_membership_projection"; |
| 19 | import { asPayload } from "../lib/object"; | ||
| 19 | 20 | ||
| 20 | type WorkosMembershipEventData = { | 21 | type WorkosMembershipEventData = { |
| 21 | id: string; | 22 | id: string; |
| @@ -27,6 +28,30 @@ type WorkosMembershipEventData = { · 5 unmodified lines | |||
| 27 | status?: string; | 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 | function deriveOrganizationRoleFromWorkosSlug(workosRoleSlug?: string): Doc<"organizationMembers">["role"] { | 55 | function deriveOrganizationRoleFromWorkosSlug(workosRoleSlug?: string): Doc<"organizationMembers">["role"] { |
| 31 | return workosRoleSlug === "admin" ? "admin" : "member"; | 56 | return workosRoleSlug === "admin" ? "admin" : "member"; |
| 32 | } | 57 | } |
| @@ -34,7 +59,7 @@ function deriveOrganizationRoleFromWorkosSlug(workosRoleSlug?: string): Doc<"org · 1 unmodified line | |||
| 34 | async function resolveMembershipAccountAndOrganization( | 59 | async function resolveMembershipAccountAndOrganization( |
| 35 | ctx: Pick<MutationCtx, "db">, | 60 | ctx: Pick<MutationCtx, "db">, |
| 36 | data: WorkosMembershipEventData, | 61 | data: WorkosMembershipEventData, |
| 37 | ) { | 62 | ): Promise<ResolvedMembershipTarget> { |
| 38 | const workosUserId = data.user_id ?? data.userId; | 63 | const workosUserId = data.user_id ?? data.userId; |
| 39 | const workosOrgId = data.organization_id ?? data.organizationId; | 64 | const workosOrgId = data.organization_id ?? data.organizationId; |
| 40 | 65 | ||
| @@ -48,7 +73,6 @@ async function resolveMembershipAccountAndOrganization( · 7 unmodified lines | |||
| 48 | return { | 73 | return { |
| 49 | account, | 74 | account, |
| 50 | organization, | 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 | return { | 85 | return { |
| 62 | account: null, | 86 | account: null, |
| 63 | organization: null, | 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 | "organization_membership.created": async (ctx, event) => { | 275 | "organization_membership.created": async (ctx, event) => { |
| 253 | const now = Date.now(); | 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 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); | 281 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 256 | if (!account || !organization) return; | 282 | if (!account || !organization) return; |
| 257 | 283 | ||
| @@ -279,7 +305,10 @@ export const workosEventHandlers = { · 21 unmodified lines | |||
| 279 | 305 | ||
| 280 | "organization_membership.updated": async (ctx, event) => { | 306 | "organization_membership.updated": async (ctx, event) => { |
| 281 | const now = Date.now(); | 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 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); | 312 | const { account, organization } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 284 | 313 | ||
| 285 | if (!account || !organization) { | 314 | if (!account || !organization) { |
| @@ -310,7 +339,10 @@ export const workosEventHandlers = { · 24 unmodified lines | |||
| 310 | 339 | ||
| 311 | "organization_membership.deleted": async (ctx, event) => { | 340 | "organization_membership.deleted": async (ctx, event) => { |
| 312 | const now = Date.now(); | 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 | const { account, organization, fallbackRole } = await resolveMembershipAccountAndOrganization(ctx, data); | 346 | const { account, organization, fallbackRole } = await resolveMembershipAccountAndOrganization(ctx, data); |
| 315 | if (!account || !organization) { | 347 | if (!account || !organization) { |
| 316 | return; | 348 | return; |