Skip to content

Commit 1e166b7

Browse files
committed
fix: prevent foreign key violations during entity merges
1 parent 44c408f commit 1e166b7

1 file changed

Lines changed: 1 addition & 146 deletions

File tree

services/libs/data-access-layer/src/members/organizations.ts

Lines changed: 1 addition & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -424,15 +424,6 @@ export async function findNonIntersectingRoles(
424424
}
425425

426426
export async function removeMemberRole(qx: QueryExecutor, role: IMemberOrganization) {
427-
console.log(`[DEBUG removeMemberRole] Deleting role:`, {
428-
id: role.id,
429-
memberId: role.memberId,
430-
organizationId: role.organizationId,
431-
title: role.title,
432-
dateStart: role.dateStart,
433-
dateEnd: role.dateEnd,
434-
})
435-
436427
if (role.id) {
437428
await deleteAffiliationOverrides(qx, role.memberId, [role.id])
438429
}
@@ -461,23 +452,13 @@ export async function removeMemberRole(qx: QueryExecutor, role: IMemberOrganizat
461452
replacements.dateEnd = (role.dateEnd as Date).toISOString()
462453
}
463454

464-
console.log(`[DEBUG removeMemberRole] Query:`, deleteMemberRole.replace(/\s+/g, ' '))
465455
await qx.result(deleteMemberRole, replacements)
466-
console.log(`[DEBUG removeMemberRole] Delete completed`)
467456
}
468457

469458
export async function addMemberRole(
470459
qx: QueryExecutor,
471460
role: IMemberOrganization,
472461
): Promise<string | undefined> {
473-
console.log(`[DEBUG addMemberRole] Adding role:`, {
474-
memberId: role.memberId,
475-
organizationId: role.organizationId,
476-
title: role.title,
477-
dateStart: role.dateStart,
478-
dateEnd: role.dateEnd,
479-
})
480-
481462
const query = `
482463
insert into "memberOrganizations" ("memberId", "organizationId", "createdAt", "updatedAt", "title", "dateStart", "dateEnd", "source")
483464
values ($(memberId), $(organizationId), NOW(), NOW(), $(title), $(dateStart), $(dateEnd), $(source))
@@ -493,12 +474,7 @@ export async function addMemberRole(
493474
source: role.source || null,
494475
})
495476

496-
if (row) {
497-
console.log(`[DEBUG addMemberRole] Created with ID: ${row.id}`)
498-
return row.id
499-
} else {
500-
console.log(`[DEBUG addMemberRole] Conflict - role already exists`)
501-
}
477+
return row?.id
502478
}
503479

504480
async function moveRolesBetweenEntities(
@@ -508,13 +484,6 @@ async function moveRolesBetweenEntities(
508484
mergeStrat: IMergeStrat,
509485
entityType: EntityType,
510486
) {
511-
console.log(`[DEBUG moveRolesBetweenEntities] Starting ${entityType} merge:`, {
512-
primaryId,
513-
secondaryId,
514-
entityIdField: mergeStrat.entityIdField,
515-
intersectBasedOnField: mergeStrat.intersectBasedOnField,
516-
})
517-
518487
// first, handle members that belong to both organizations,
519488
// then make a full update on remaining org2 members (that doesn't belong to o1)
520489
const rolesForBothEntities = await findRolesBelongingToBothEntities(
@@ -528,11 +497,6 @@ async function moveRolesBetweenEntities(
528497
const primaryRoles = rolesForBothEntities.filter((m) => mergeStrat.entityId(m) === primaryId)
529498
const secondaryRoles = rolesForBothEntities.filter((m) => mergeStrat.entityId(m) === secondaryId)
530499

531-
console.log(`[DEBUG moveRolesBetweenEntities] Found intersecting roles:`, {
532-
primaryCount: primaryRoles.length,
533-
secondaryCount: secondaryRoles.length,
534-
})
535-
536500
const findAffiliationOverrides =
537501
entityType === EntityType.MEMBER
538502
? findMemberAffiliationOverrides
@@ -541,11 +505,6 @@ async function moveRolesBetweenEntities(
541505
const primaryAffiliationOverrides = await findAffiliationOverrides(qx, primaryId)
542506
const secondaryAffiliationOverrides = await findAffiliationOverrides(qx, secondaryId)
543507

544-
console.log(`[DEBUG moveRolesBetweenEntities] Found affiliation overrides:`, {
545-
primaryOverridesCount: primaryAffiliationOverrides.length,
546-
secondaryOverridesCount: secondaryAffiliationOverrides.length,
547-
})
548-
549508
await mergeRoles(
550509
qx,
551510
primaryRoles,
@@ -564,30 +523,14 @@ async function moveRolesBetweenEntities(
564523
mergeStrat.intersectBasedOnField,
565524
)
566525

567-
console.log(
568-
`[DEBUG moveRolesBetweenEntities] Processing ${remainingRoles.length} remaining (non-intersecting) roles`,
569-
)
570-
571526
// Process non-intersecting roles: these are roles that exist only in secondary entity
572527
// We need to move them to primary entity and preserve their overrides
573528
for (const role of remainingRoles) {
574-
console.log(`[DEBUG moveRolesBetweenEntities] Processing remaining role:`, {
575-
id: role.id,
576-
memberId: role.memberId,
577-
organizationId: role.organizationId,
578-
})
579-
580529
// Check if this role has an affiliation override
581530
const existingOverride = secondaryAffiliationOverrides.find(
582531
(o) => o.memberOrganizationId === role.id,
583532
)
584533

585-
if (existingOverride) {
586-
console.log(
587-
`[DEBUG moveRolesBetweenEntities] Found override for role ${role.id}, will recreate after transfer`,
588-
)
589-
}
590-
591534
// Remove the old role (this will also clean up its override via removeMemberRole)
592535
await removeMemberRole(qx, role)
593536

@@ -613,28 +556,20 @@ async function moveRolesBetweenEntities(
613556
)
614557

615558
if (primaryHasPrimaryWorkExp) {
616-
console.log(
617-
`[DEBUG moveRolesBetweenEntities] Primary already has isPrimaryWorkExperience, setting to false`,
618-
)
619559
overrideToApply = {
620560
...existingOverride,
621561
isPrimaryWorkExperience: false,
622562
}
623563
}
624564
}
625565

626-
console.log(`[DEBUG moveRolesBetweenEntities] Recreating override for new role ${newRoleId}`)
627566
await changeOverride(qx, {
628567
...overrideToApply,
629568
memberId: mergeStrat.targetMemberId(role),
630569
memberOrganizationId: newRoleId,
631570
})
632571
}
633572
}
634-
635-
console.log(`[DEBUG moveRolesBetweenEntities] Non-intersecting roles processed:`, {
636-
totalProcessed: remainingRoles.length,
637-
})
638573
}
639574

640575
export async function moveMembersBetweenOrganizations(
@@ -673,19 +608,6 @@ export async function mergeRoles(
673608
secondaryAffiliationOverrides: IMemberOrganizationAffiliationOverride[],
674609
mergeStrat: IMergeStrat,
675610
) {
676-
console.log(
677-
`[DEBUG mergeRoles] Starting with ${primaryRoles.length} primary, ${secondaryRoles.length} secondary roles`,
678-
)
679-
console.log(
680-
`[DEBUG mergeRoles] Secondary affiliation overrides:`,
681-
secondaryAffiliationOverrides.map((o) => ({
682-
id: o.id,
683-
memberOrganizationId: o.memberOrganizationId,
684-
allowAffiliation: o.allowAffiliation,
685-
isPrimaryWorkExperience: o.isPrimaryWorkExperience,
686-
})),
687-
)
688-
689611
const allExistingOverrides = [...primaryAffiliationOverrides, ...secondaryAffiliationOverrides]
690612
const removeRoles: IMemberOrganization[] = []
691613
const addRoles: (IMemberOrganization & { originalRoleId?: string })[] = []
@@ -696,34 +618,21 @@ export async function mergeRoles(
696618

697619
// Phase 1: Analyze all secondary roles and build the complete plan
698620
for (const memberOrganization of secondaryRoles) {
699-
console.log(`[DEBUG] Processing secondary role:`, {
700-
id: memberOrganization.id,
701-
memberId: memberOrganization.memberId,
702-
organizationId: memberOrganization.organizationId,
703-
title: memberOrganization.title,
704-
dateStart: memberOrganization.dateStart,
705-
dateEnd: memberOrganization.dateEnd,
706-
})
707-
708621
// if dateEnd and dateStart isn't available, we don't need to move but delete it from org2
709622
if (memberOrganization.dateStart === null && memberOrganization.dateEnd === null) {
710-
console.log(`[DEBUG] Role has no dates - adding to removeRoles`)
711623
removeRoles.push(memberOrganization)
712624
}
713625
// it's a current role, also check org1 to see which one starts earlier
714626
else if (memberOrganization.dateStart !== null && memberOrganization.dateEnd === null) {
715-
console.log(`[DEBUG] Role is current (no end date) - checking for conflicts`)
716627
const currentRoles = primaryRoles.filter(
717628
(mo) =>
718629
mergeStrat.worthMerging(mo, memberOrganization) &&
719630
mo.dateStart !== null &&
720631
mo.dateEnd === null,
721632
)
722-
console.log(`[DEBUG] Found ${currentRoles.length} current roles in primary`)
723633

724634
if (currentRoles.length === 0) {
725635
// no current role in org1, add the memberOrganization to org1
726-
console.log(`[DEBUG] No current role conflict - will move role to primary entity`)
727636

728637
const transformedRole = {
729638
title: memberOrganization.title,
@@ -735,14 +644,6 @@ export async function mergeRoles(
735644
originalRoleId: memberOrganization.id, // Track which secondary role this came from
736645
}
737646

738-
console.log(`[DEBUG] Transformed role:`, {
739-
originalRoleId: memberOrganization.id,
740-
originalMemberId: memberOrganization.memberId,
741-
originalOrgId: memberOrganization.organizationId,
742-
newMemberId: transformedRole.memberId,
743-
newOrgId: transformedRole.organizationId,
744-
})
745-
746647
// Add transformed role to primary entity
747648
addRoles.push(transformedRole)
748649
// Remove the original role from secondary entity
@@ -766,10 +667,8 @@ export async function mergeRoles(
766667
}
767668

768669
// delete role from org2
769-
console.log(`[DEBUG] Removing secondary current role from removeRoles`)
770670
removeRoles.push(memberOrganization)
771671
} else {
772-
console.log(`[DEBUG] ERROR: Multiple current roles found`)
773672
throw new Error(`Member ${memberOrganization.memberId} has more than one current roles.`)
774673
}
775674
} else if (memberOrganization.dateStart === null && memberOrganization.dateEnd !== null) {
@@ -824,15 +723,12 @@ export async function mergeRoles(
824723
}
825724

826725
// Phase 2: Execute batch removal of roles
827-
// Note: removeMemberRole handles override cleanup automatically
828-
console.log(`[DEBUG] === REMOVE PHASE: ${removeRoles.length} roles ===`)
829726
for (const removeRole of removeRoles) {
830727
// Track if this role has an override that we need to recreate later
831728
const existingOverride = allExistingOverrides.find(
832729
(o) => o.memberOrganizationId === removeRole.id,
833730
)
834731

835-
console.log(`[DEBUG] Removing role ${removeRole.id}, has override: ${!!existingOverride}`)
836732
if (existingOverride) {
837733
// Store the override so we can recreate it for the new merged role
838734
affiliationOverridesToRecreate.push({
@@ -846,29 +742,12 @@ export async function mergeRoles(
846742
}
847743

848744
// Phase 3: Execute batch addition of roles and recreate overrides
849-
console.log(`[DEBUG] === ADD PHASE: ${addRoles.length} roles ===`)
850745
for (const addRole of addRoles) {
851-
console.log(`[DEBUG addMemberRole] Adding role:`, {
852-
memberId: addRole.memberId,
853-
organizationId: addRole.organizationId,
854-
title: addRole.title,
855-
dateStart: addRole.dateStart,
856-
dateEnd: addRole.dateEnd,
857-
})
858-
859746
const newRoleId = await addMemberRole(qx, addRole)
860747

861748
if (newRoleId) {
862749
// Role was successfully created, apply affiliation overrides
863750
// Find overrides by matching the original role ID if available, otherwise fallback to member + title matching
864-
console.log(`[DEBUG] Looking for overrides to apply to new role ${newRoleId}:`, {
865-
addRoleMemberId: addRole.memberId,
866-
addRoleOrgId: addRole.organizationId,
867-
addRoleTitle: addRole.title,
868-
originalRoleId: addRole.originalRoleId,
869-
availableOverrides: affiliationOverridesToRecreate.length,
870-
})
871-
872751
const relevantOverrides = affiliationOverridesToRecreate.filter((item) => {
873752
// If we tracked the original role ID, use exact matching
874753
if (addRole.originalRoleId) {
@@ -878,8 +757,6 @@ export async function mergeRoles(
878757
return item.role.memberId === addRole.memberId && item.role.title === addRole.title
879758
})
880759

881-
console.log(`[DEBUG] Found ${relevantOverrides.length} relevant overrides for new role`)
882-
883760
let overrideToApply: IMemberOrganizationAffiliationOverride | undefined
884761
if (relevantOverrides.length > 0) {
885762
// Prefer the override from the primary role if it exists
@@ -889,15 +766,9 @@ export async function mergeRoles(
889766

890767
// If we found a primary override, use it, otherwise, use the first one
891768
overrideToApply = primaryOverride?.override || relevantOverrides[0]?.override
892-
console.log(`[DEBUG] Selected override:`, {
893-
isPrimary: !!primaryOverride,
894-
allowAffiliation: overrideToApply.allowAffiliation,
895-
isPrimaryWorkExperience: overrideToApply.isPrimaryWorkExperience,
896-
})
897769
}
898770

899771
if (overrideToApply) {
900-
console.log(`[DEBUG] Applying override to new role ${newRoleId}`)
901772
await changeOverride(qx, {
902773
...overrideToApply,
903774
memberId: mergeStrat.targetMemberId(addRole),
@@ -906,7 +777,6 @@ export async function mergeRoles(
906777
}
907778
} else {
908779
// Role already exists (duplicate), need to transfer override to existing role
909-
console.log(`[DEBUG addMemberRole] Conflict - role already exists, looking for existing role`)
910780

911781
// Find the existing role in primary that matches this addRole
912782
const existingPrimaryRole = primaryRoles.find(
@@ -925,18 +795,8 @@ export async function mergeRoles(
925795
)
926796

927797
if (existingPrimaryRole) {
928-
console.log(
929-
`[DEBUG] Found existing primary role ${existingPrimaryRole.id}, transferring overrides`,
930-
)
931-
932798
// Find overrides from secondary roles that should be transferred
933799
// Use original role ID if available for exact matching
934-
console.log(`[DEBUG] Looking for overrides to transfer (duplicate case):`, {
935-
addRoleOriginalId: addRole.originalRoleId,
936-
addRoleMemberId: addRole.memberId,
937-
addRoleTitle: addRole.title,
938-
})
939-
940800
const secondaryOverridesToTransfer = affiliationOverridesToRecreate.filter((item) => {
941801
const isSecondaryOverride = secondaryAffiliationOverrides.some(
942802
(so) => so.memberOrganizationId === item.role.id,
@@ -953,10 +813,6 @@ export async function mergeRoles(
953813
return item.role.memberId === addRole.memberId && item.role.title === addRole.title
954814
})
955815

956-
console.log(
957-
`[DEBUG] Found ${secondaryOverridesToTransfer.length} secondary overrides to transfer`,
958-
)
959-
960816
// Also check if there's a direct override on the secondary role we're trying to add
961817
const directSecondaryOverride = secondaryAffiliationOverrides.find((so) =>
962818
secondaryRoles.some(
@@ -1011,7 +867,6 @@ export async function mergeRoles(
1011867
}
1012868
}
1013869

1014-
console.log(`[DEBUG] Applying merged override to existing role ${existingPrimaryRole.id}`)
1015870
await changeOverride(qx, {
1016871
...finalOverride,
1017872
memberId: existingPrimaryRole.memberId,

0 commit comments

Comments
 (0)