Skip to content

Commit c0dfc54

Browse files
committed
fix: resolve pr review comments
Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com>
1 parent 99bd83b commit c0dfc54

1 file changed

Lines changed: 45 additions & 36 deletions

File tree

backend/src/api/public/v1/members/identities/createMemberIdentity.ts

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,40 @@ const bodySchema = z
3535
path: ['verifiedBy'],
3636
})
3737

38+
type DbConstraintError = Error & {
39+
constraint?: string
40+
original?: { constraint?: string }
41+
parent?: { constraint?: string }
42+
}
43+
44+
function throwIdentityConflict(
45+
error: DbConstraintError,
46+
data: { platform: string; value: string; type: MemberIdentityType },
47+
): never {
48+
const constraint = error.constraint ?? error.original?.constraint ?? error.parent?.constraint
49+
50+
if (constraint === 'uix_memberIdentities_platform_value_type_verified') {
51+
throw new ConflictError('Identity already verified on another member', data)
52+
}
53+
54+
throw error
55+
}
56+
3857
export async function createMemberIdentity(req: Request, res: Response): Promise<void> {
3958
const { memberId } = validateOrThrow(paramsSchema, req.params)
4059
const data = validateOrThrow(bodySchema, req.body)
4160

4261
const qx = optionsQx(req)
43-
4462
const member = await findMemberById(qx, memberId, [MemberField.ID])
4563
if (!member) {
4664
throw new NotFoundError('Member not found')
4765
}
4866

67+
// The data-sink writes identity values as trimmed lowercase, so normalize here
68+
// to keep idempotency checks reliable against existing rows.
69+
const normalizedValue = data.value.trim().toLowerCase()
70+
const conflictContext = { platform: data.platform, value: normalizedValue, type: data.type }
71+
4972
let result!: IMemberIdentity
5073
let alreadyExisted = false
5174

@@ -55,23 +78,22 @@ export async function createMemberIdentity(req: Request, res: Response): Promise
5578
captureOldState({})
5679

5780
await qx.tx(async (tx) => {
58-
const existing = await findMemberIdentitiesByValue(tx, memberId, data.value, {
81+
const existing = await findMemberIdentitiesByValue(tx, memberId, normalizedValue, {
5982
type: data.type,
6083
})
61-
6284
const exactMatch = existing.find((i) => i.platform === data.platform)
6385

64-
if (exactMatch) {
65-
alreadyExisted = true
66-
result = exactMatch
67-
} else {
68-
try {
86+
try {
87+
if (exactMatch) {
88+
alreadyExisted = true
89+
result = exactMatch
90+
} else {
6991
result = await insertMemberIdentity(
7092
tx,
7193
{
7294
memberId,
7395
platform: data.platform,
74-
value: data.value,
96+
value: normalizedValue,
7597
type: data.type,
7698
source: data.source,
7799
verified: data.verified,
@@ -80,39 +102,26 @@ export async function createMemberIdentity(req: Request, res: Response): Promise
80102
true,
81103
true,
82104
)
83-
} catch (error) {
84-
const constraint =
85-
error.constraint ?? error.original?.constraint ?? error.parent?.constraint
86-
87-
if (constraint === 'uix_memberIdentities_platform_value_type_verified') {
88-
throw new ConflictError('Identity already verified on another member', {
89-
platform: data.platform,
90-
value: data.value,
91-
type: data.type,
92-
})
93-
}
94-
95-
throw error
96105
}
97-
}
98106

99-
if (data.verified && existing.length > 0) {
100-
await Promise.all(
101-
existing.map((i) =>
102-
updateMemberIdentity(tx, memberId, i.id, {
107+
// A verified identity confirms the same value for this member, so keep same-value
108+
// identities in sync instead of leaving stale unverified duplicates behind.
109+
if (data.verified && existing.length > 0) {
110+
const updatedResults: IMemberIdentity[] = []
111+
for (const identity of existing) {
112+
const updated = await updateMemberIdentity(tx, memberId, identity.id, {
103113
verified: true,
104114
verifiedBy: data.verifiedBy,
105-
}),
106-
),
107-
)
108-
109-
if (alreadyExisted) {
110-
result = {
111-
...exactMatch,
112-
verified: true,
113-
verifiedBy: data.verifiedBy,
115+
})
116+
if (updated) updatedResults.push(updated)
117+
}
118+
119+
if (alreadyExisted) {
120+
result = updatedResults.find((r) => r.id === exactMatch.id) ?? result
114121
}
115122
}
123+
} catch (error) {
124+
throwIdentityConflict(error as DbConstraintError, conflictContext)
116125
}
117126

118127
await touchMemberUpdatedAt(tx, memberId)

0 commit comments

Comments
 (0)