Skip to content

Commit 71359bf

Browse files
authored
refactor and improve memberOrganizationService transaction handling (#2849)
1 parent 90965c5 commit 71359bf

File tree

2 files changed

+147
-172
lines changed

2 files changed

+147
-172
lines changed

backend/src/database/repositories/member/memberOrganizationsRepository.ts

Lines changed: 0 additions & 122 deletions
This file was deleted.
Lines changed: 147 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
/* eslint-disable no-continue */
22
import { Error404 } from '@crowd/common'
3+
import {
4+
OrganizationField,
5+
cleanSoftDeletedMemberOrganization,
6+
createMemberOrganization,
7+
deleteMemberOrganization,
8+
fetchMemberOrganizations,
9+
queryOrgs,
10+
updateMemberOrganization,
11+
} from '@crowd/data-access-layer'
12+
import { findOverrides as findMemberOrganizationAffiliationOverrides } from '@crowd/data-access-layer/src/member_organization_affiliation_overrides'
313
import { LoggerBase } from '@crowd/logging'
4-
import { IMemberOrganization, IOrganization } from '@crowd/types'
14+
import { IMemberOrganization, IOrganization, IRenderFriendlyMemberOrganization } from '@crowd/types'
515

6-
import MemberOrganizationsRepository from '@/database/repositories/member/memberOrganizationsRepository'
16+
import SequelizeRepository from '@/database/repositories/sequelizeRepository'
717

818
import { IServiceOptions } from '../IServiceOptions'
919
import MemberAffiliationService from '../memberAffiliationService'
1020

21+
type IOrganizationSummary = Pick<IOrganization, 'id' | 'displayName' | 'logo'>
22+
1123
export default class MemberOrganizationsService extends LoggerBase {
1224
options: IServiceOptions
1325

@@ -17,70 +29,155 @@ export default class MemberOrganizationsService extends LoggerBase {
1729
}
1830

1931
// Member organization list
20-
async list(memberId: string): Promise<IOrganization[]> {
21-
return MemberOrganizationsRepository.list(memberId, this.options)
22-
}
32+
async list(memberId: string): Promise<IRenderFriendlyMemberOrganization[]> {
33+
const qx = SequelizeRepository.getQueryExecutor(this.options)
2334

24-
// Member organization creation
25-
async create(memberId: string, data: Partial<IMemberOrganization>): Promise<IOrganization[]> {
26-
const memberOrganizations = await MemberOrganizationsRepository.create(
35+
// Fetch member organizations
36+
const memberOrganizations: IMemberOrganization[] = await fetchMemberOrganizations(qx, memberId)
37+
38+
if (memberOrganizations.length === 0) {
39+
return []
40+
}
41+
42+
// Parse unique organization ids
43+
const orgIds: string[] = [...new Set(memberOrganizations.map((mo) => mo.organizationId))]
44+
45+
// Fetch organizations
46+
let organizations: IOrganizationSummary[] = []
47+
if (orgIds.length) {
48+
organizations = await queryOrgs(qx, {
49+
filter: {
50+
[OrganizationField.ID]: {
51+
in: orgIds,
52+
},
53+
},
54+
fields: [OrganizationField.ID, OrganizationField.DISPLAY_NAME, OrganizationField.LOGO],
55+
})
56+
}
57+
58+
// Fetch affiliation overrides
59+
const affiliationOverrides = await findMemberOrganizationAffiliationOverrides(
60+
qx,
2761
memberId,
28-
data,
29-
this.options,
62+
memberOrganizations.map((mo) => mo.id),
3063
)
31-
await MemberAffiliationService.startAffiliationRecalculation(
32-
memberId,
33-
[data.organizationId],
34-
this.options,
64+
65+
// Create mapping by id to speed up the processing
66+
const orgByid: Record<string, IOrganizationSummary> = organizations.reduce(
67+
(obj: Record<string, IOrganizationSummary>, org) => ({
68+
...obj,
69+
[org.id]: org,
70+
}),
71+
{},
3572
)
36-
return memberOrganizations
73+
74+
// Format the results
75+
return memberOrganizations.map((mo) => ({
76+
...(orgByid[mo.organizationId] || {}),
77+
id: mo.organizationId,
78+
memberOrganizations: {
79+
...mo,
80+
affiliationOverride: affiliationOverrides.find((ao) => ao.memberOrganizationId === mo.id),
81+
},
82+
}))
83+
}
84+
85+
// Member organization creation
86+
async create(
87+
memberId: string,
88+
data: Partial<IMemberOrganization>,
89+
): Promise<IRenderFriendlyMemberOrganization[]> {
90+
const transaction = await SequelizeRepository.createTransaction(this.options)
91+
const repositoryOptions = { ...this.options, transaction }
92+
93+
try {
94+
const qx = SequelizeRepository.getQueryExecutor(repositoryOptions)
95+
96+
// Clean up any soft-deleted entries
97+
await cleanSoftDeletedMemberOrganization(qx, memberId, data.organizationId, data)
98+
99+
// Create new member organization
100+
await createMemberOrganization(qx, memberId, data)
101+
102+
// Start affiliation recalculation within the same transaction
103+
await MemberAffiliationService.startAffiliationRecalculation(
104+
memberId,
105+
[data.organizationId],
106+
repositoryOptions,
107+
)
108+
109+
// Fetch updated list
110+
const result = await this.list(memberId)
111+
112+
await SequelizeRepository.commitTransaction(transaction)
113+
return result
114+
} catch (error) {
115+
await SequelizeRepository.rollbackTransaction(transaction)
116+
throw error
117+
}
37118
}
38119

39120
// Update member organization
40121
async update(
41122
id: string,
42123
memberId: string,
43124
data: Partial<IMemberOrganization>,
44-
): Promise<IOrganization[]> {
45-
const memberOrganizations = await MemberOrganizationsRepository.update(
46-
id,
47-
memberId,
48-
data,
49-
this.options,
50-
)
51-
await MemberAffiliationService.startAffiliationRecalculation(
52-
memberId,
53-
[data.organizationId],
54-
this.options,
55-
)
56-
return memberOrganizations
125+
): Promise<IRenderFriendlyMemberOrganization[]> {
126+
const transaction = await SequelizeRepository.createTransaction(this.options)
127+
const repositoryOptions = { ...this.options, transaction }
128+
129+
try {
130+
const qx = SequelizeRepository.getQueryExecutor(repositoryOptions)
131+
132+
await cleanSoftDeletedMemberOrganization(qx, memberId, data.organizationId, data)
133+
await updateMemberOrganization(qx, memberId, id, data)
134+
135+
await MemberAffiliationService.startAffiliationRecalculation(
136+
memberId,
137+
[data.organizationId],
138+
repositoryOptions,
139+
)
140+
141+
const result = await this.list(memberId)
142+
143+
await SequelizeRepository.commitTransaction(transaction)
144+
return result
145+
} catch (error) {
146+
await SequelizeRepository.rollbackTransaction(transaction)
147+
throw error
148+
}
57149
}
58150

59151
// Delete member organization
60-
async delete(id: string, memberId: string): Promise<IOrganization[]> {
61-
const existingMemberOrganizations = await MemberOrganizationsRepository.list(
62-
memberId,
63-
this.options,
64-
)
65-
const memberOrganizationToBeDeleted = existingMemberOrganizations.find(
66-
(mo) => mo.memberOrganizations.id === id,
67-
)
68-
if (!memberOrganizationToBeDeleted) {
69-
throw new Error404(`Member organization with id ${id} not found!`)
70-
}
152+
async delete(id: string, memberId: string): Promise<IRenderFriendlyMemberOrganization[]> {
153+
const transaction = await SequelizeRepository.createTransaction(this.options)
154+
const repositoryOptions = { ...this.options, transaction }
71155

72-
const remainingMemberOrganizations = await MemberOrganizationsRepository.delete(
73-
id,
74-
memberId,
75-
this.options,
76-
)
156+
try {
157+
const qx = SequelizeRepository.getQueryExecutor(repositoryOptions)
77158

78-
await MemberAffiliationService.startAffiliationRecalculation(
79-
memberId,
80-
[memberOrganizationToBeDeleted.memberOrganizations.organizationId],
81-
this.options,
82-
)
159+
const existingMemberOrganizations = await fetchMemberOrganizations(qx, memberId)
160+
const memberOrganizationToBeDeleted = existingMemberOrganizations.find((mo) => mo.id === id)
83161

84-
return remainingMemberOrganizations
162+
if (!memberOrganizationToBeDeleted) {
163+
throw new Error404(`Member organization with id ${id} not found!`)
164+
}
165+
166+
await deleteMemberOrganization(qx, memberId, id)
167+
168+
await MemberAffiliationService.startAffiliationRecalculation(
169+
memberId,
170+
[memberOrganizationToBeDeleted.organizationId],
171+
repositoryOptions,
172+
)
173+
174+
const result = await this.list(memberId)
175+
176+
await SequelizeRepository.commitTransaction(transaction)
177+
return result
178+
} catch (error) {
179+
await SequelizeRepository.rollbackTransaction(transaction)
180+
throw error
181+
}
85182
}
86183
}

0 commit comments

Comments
 (0)