-
Notifications
You must be signed in to change notification settings - Fork 730
Expand file tree
/
Copy pathcreateMemberWorkExperience.ts
More file actions
127 lines (103 loc) · 3.68 KB
/
createMemberWorkExperience.ts
File metadata and controls
127 lines (103 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import type { Request, Response } from 'express'
import { z } from 'zod'
import { captureApiChange, memberEditOrganizationsAction } from '@crowd/audit-logs'
import {
BadRequestError,
ConflictError,
NotFoundError,
sanitizeMemberOrganizationDateRange,
} from '@crowd/common'
import { signalMemberUpdate } from '@crowd/common_services'
import {
MemberField,
changeMemberOrganizationAffiliationOverrides,
cleanSoftDeletedMemberOrganization,
createMemberOrganization,
fetchManyMemberOrgsWithOrgData,
fetchManyOrganizationAffiliationPolicies,
findMemberById,
optionsQx,
} from '@crowd/data-access-layer'
import type {
IMemberOrganization,
IMemberRoleWithOrganization,
MemberOrganizationDateRange,
} from '@crowd/types'
import { created } from '@/utils/api'
import { toMemberWorkExperience } from '@/utils/mapper'
import { validateOrThrow } from '@/utils/validation'
const paramsSchema = z.object({
memberId: z.uuid(),
})
const bodySchema = z.object({
organizationId: z.uuid(),
jobTitle: z.string(),
verified: z.boolean(),
verifiedBy: z.string(),
source: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date().nullable().optional(),
})
export async function createMemberWorkExperience(req: Request, res: Response): Promise<void> {
const { memberId } = validateOrThrow(paramsSchema, req.params)
const data = validateOrThrow(bodySchema, req.body)
const qx = optionsQx(req)
const member = await findMemberById(qx, memberId, [MemberField.ID])
if (!member) {
throw new NotFoundError('Member not found')
}
let createdMo: IMemberRoleWithOrganization | undefined
await captureApiChange(
req,
memberEditOrganizationsAction(memberId, async (captureOldState, captureNewState) => {
captureOldState({})
let dates: MemberOrganizationDateRange
try {
dates = sanitizeMemberOrganizationDateRange(data.startDate, data.endDate, true)
} catch (error) {
throw new BadRequestError('Invalid work experience date range')
}
const memberOrgData: IMemberOrganization = {
memberId,
organizationId: data.organizationId,
title: data.jobTitle,
dateStart: dates.dateStart,
dateEnd: dates.dateEnd,
source: data.source,
verified: data.verified,
verifiedBy: data.verifiedBy,
}
let newMemberOrgId: string | undefined
await qx.tx(async (tx) => {
await cleanSoftDeletedMemberOrganization(tx, memberId, data.organizationId, memberOrgData)
newMemberOrgId = await createMemberOrganization(tx, memberId, memberOrgData)
if (!newMemberOrgId) {
throw new ConflictError('A work experience with the same dates already exists')
}
const orgAffiliationPolicyById = await fetchManyOrganizationAffiliationPolicies(tx, [
data.organizationId,
])
if (newMemberOrgId && orgAffiliationPolicyById.get(data.organizationId)) {
await changeMemberOrganizationAffiliationOverrides(tx, [
{
memberId,
memberOrganizationId: newMemberOrgId,
allowAffiliation: false,
},
])
}
})
// Signal after commit so the workflow sees persisted changes
await signalMemberUpdate(req.temporal, memberId, {
memberOrganizationIds: [data.organizationId],
})
const orgsMap = await fetchManyMemberOrgsWithOrgData(qx, [memberId])
createdMo = (orgsMap.get(memberId) ?? []).find((mo) => mo.id === newMemberOrgId)
captureNewState(createdMo ?? null)
}),
)
if (!createdMo) {
throw new NotFoundError('Work experience not found after creation')
}
created(res, toMemberWorkExperience(createdMo))
}