diff --git a/backend/src/api/public/middlewares/errorHandler.ts b/backend/src/api/public/middlewares/errorHandler.ts index 4da3552876..626807bad0 100644 --- a/backend/src/api/public/middlewares/errorHandler.ts +++ b/backend/src/api/public/middlewares/errorHandler.ts @@ -4,7 +4,13 @@ import { UnauthorizedError as Auth0UnauthorizedError, } from 'express-oauth2-jwt-bearer' -import { HttpError, InsufficientScopeError, InternalError, UnauthorizedError } from '@crowd/common' +import { + ConflictError, + HttpError, + InsufficientScopeError, + InternalError, + UnauthorizedError, +} from '@crowd/common' import { SlackChannel, SlackPersona, sendSlackNotification } from '@crowd/slack' /** @@ -17,6 +23,29 @@ export const errorHandler: ErrorRequestHandler = ( res: Response, _next: NextFunction, ) => { + if (error instanceof ConflictError) { + const memberIds = (error as ConflictError & { memberIds?: string[] }).memberIds + req.log.warn({ memberIds }, 'Public API conflict') + sendSlackNotification( + SlackChannel.CDP_LFX_SELF_SERVE_ALERTS, + SlackPersona.WARNING_PROPAGATOR, + `Public API Conflict 409: ${req.method} ${req.url}`, + [ + { + title: 'Request', + text: `*Method:* \`${req.method}\`\n*URL:* \`${req.url}\``, + }, + { + title: 'Conflict', + text: `*Message:* ${error.message}`, + }, + ...(memberIds ? [{ title: 'Member IDs', text: memberIds.join(', ') }] : []), + ], + ) + res.status(error.status).json(error.toJSON()) + return + } + if (error instanceof HttpError) { res.status(error.status).json(error.toJSON()) return diff --git a/backend/src/api/public/v1/members/resolveMember.ts b/backend/src/api/public/v1/members/resolveMember.ts index 43d6e948a2..a41870925f 100644 --- a/backend/src/api/public/v1/members/resolveMember.ts +++ b/backend/src/api/public/v1/members/resolveMember.ts @@ -32,7 +32,13 @@ export async function resolveMemberByIdentities(req: Request, res: Response): Pr if (memberIds.length === 0) { throw new NotFoundError('Member not found') } else if (memberIds.length > 1) { - throw new ConflictError('Conflicting identities') + const error = new ConflictError('Conflicting identities') + Object.defineProperty(error, 'memberIds', { + value: memberIds, + enumerable: false, + configurable: true, + }) + throw error } const memberId = memberIds[0] diff --git a/services/apps/members_enrichment_worker/src/activities/member.ts b/services/apps/members_enrichment_worker/src/activities/member.ts index 36225a1176..fe5564578a 100644 --- a/services/apps/members_enrichment_worker/src/activities/member.ts +++ b/services/apps/members_enrichment_worker/src/activities/member.ts @@ -77,14 +77,8 @@ export async function getIdentitiesExistInOtherMembers( excludeMemberId: string, identities: IMemberIdentity[], ): Promise { - let rows: IMemberIdentity[] = [] - - try { - const db = svc.postgres.reader - rows = await getIdentitiesExistInOthers(db, excludeMemberId, identities) - } catch (err) { - throw err - } + const db = svc.postgres.reader + const rows = await getIdentitiesExistInOthers(db, excludeMemberId, identities) return rows } @@ -94,25 +88,21 @@ export async function updateMemberWithEnrichmentData( identities: IMemberIdentity[], attributes?: IAttributes, ): Promise { - try { - await svc.postgres.writer.connection().tx(async (tx) => { - for (const identity of identities) { - await createMemberIdentity(new PgPromiseQueryExecutor(tx), { - memberId, - platform: identity.platform, - value: identity.value, - type: identity.type, - verified: identity.verified || false, - source: 'enrichment', - }) - } - if (attributes) { - await updateMemberAttributes(tx, memberId, attributes) - } - }) - } catch (err) { - throw err - } + await svc.postgres.writer.connection().tx(async (tx) => { + for (const identity of identities) { + await createMemberIdentity(new PgPromiseQueryExecutor(tx), { + memberId, + platform: identity.platform, + value: identity.value, + type: identity.type, + verified: identity.verified || false, + source: 'enrichment', + }) + } + if (attributes) { + await updateMemberAttributes(tx, memberId, attributes) + } + }) } export async function mergeMembers( diff --git a/services/libs/slack/src/channels.ts b/services/libs/slack/src/channels.ts index 8aa8e26357..0bcd30c1f7 100644 --- a/services/libs/slack/src/channels.ts +++ b/services/libs/slack/src/channels.ts @@ -12,6 +12,7 @@ const CHANNEL_WEBHOOK_URLS: Record = { [SlackChannel.CDP_PROJECTS_ALERTS]: process.env.CDP_PROJECTS_ALERTS_SLACK_WEBHOOK_URL, [SlackChannel.INSIGHTS_ALERTS]: process.env.INSIGHTS_ALERTS_SLACK_WEBHOOK_URL, [SlackChannel.INSIGHTS_CRITICAL_ALERTS]: process.env.INSIGHTS_CRITICAL_ALERTS_SLACK_WEBHOOK_URL, + [SlackChannel.CDP_LFX_SELF_SERVE_ALERTS]: process.env.CDP_LFX_SELF_SERVE_ALERTS_SLACK_WEBHOOK_URL, } // Check for missing webhook URLs on initialization diff --git a/services/libs/slack/src/types.ts b/services/libs/slack/src/types.ts index 6fa29ac028..ff08dc14d5 100644 --- a/services/libs/slack/src/types.ts +++ b/services/libs/slack/src/types.ts @@ -6,6 +6,7 @@ export enum SlackChannel { CDP_PROJECTS_ALERTS = 'CDP_PROJECTS_ALERTS', INSIGHTS_ALERTS = 'INSIGHTS_ALERTS', INSIGHTS_CRITICAL_ALERTS = 'INSIGHTS_CRITICAL_ALERTS', + CDP_LFX_SELF_SERVE_ALERTS = 'CDP_LFX_SELF_SERVE_ALERTS', } export enum SlackPersona {