feat: integrate analytics and fragment receipt improvements
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import type { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
export type FinalReceiptMessage = {
|
||||
id: string;
|
||||
tenantId?: string | null;
|
||||
applicationId?: string | null;
|
||||
messageId: string;
|
||||
phoneNumber: string;
|
||||
cmppSubmitSequenceId?: string | null;
|
||||
cmppSubmitGroupMessageId?: string | null;
|
||||
cmppRegisteredDelivery?: boolean | null;
|
||||
};
|
||||
|
||||
export type DownstreamDeliveryQueueRequest = {
|
||||
tenantId: string;
|
||||
applicationId?: string | null;
|
||||
messageRecordId?: string | null;
|
||||
messageId?: string | null;
|
||||
deliveryType: 'receipt' | 'uplink';
|
||||
payload: Record<string, unknown>;
|
||||
receiptDedupeKey?: string;
|
||||
queueHttpWebhook?: boolean;
|
||||
queueCmppDelivery?: boolean;
|
||||
propagateHttpQueueError?: boolean;
|
||||
};
|
||||
|
||||
type ClientReceiptTarget = {
|
||||
segmentIndex: number;
|
||||
segmentTotal: number;
|
||||
submitSequenceId: number;
|
||||
submitGroupMessageId: string;
|
||||
registeredDelivery: boolean;
|
||||
};
|
||||
|
||||
async function resolveClientReceiptTargets(
|
||||
prisma: PrismaService,
|
||||
message: FinalReceiptMessage,
|
||||
): Promise<ClientReceiptTarget[]> {
|
||||
if (message.cmppSubmitGroupMessageId) {
|
||||
const group = await prisma.cmppInboundLongMessage.findFirst({
|
||||
where: { messageId: message.cmppSubmitGroupMessageId },
|
||||
include: { segments: { orderBy: { segmentIndex: 'asc' } } },
|
||||
});
|
||||
if (group?.segments.length) {
|
||||
return group.segments.flatMap((segment) => {
|
||||
const submitSequenceId = Number(segment.sequenceId);
|
||||
if (!Number.isInteger(submitSequenceId) || submitSequenceId <= 0) return [];
|
||||
return [{
|
||||
segmentIndex: segment.segmentIndex,
|
||||
segmentTotal: group.segmentTotal,
|
||||
submitSequenceId,
|
||||
submitGroupMessageId: group.messageId,
|
||||
registeredDelivery: segment.registeredDelivery,
|
||||
}];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const submitSequenceId = Number(message.cmppSubmitSequenceId);
|
||||
if (!Number.isInteger(submitSequenceId) || submitSequenceId <= 0) return [];
|
||||
return [{
|
||||
segmentIndex: 1,
|
||||
segmentTotal: 1,
|
||||
submitSequenceId,
|
||||
submitGroupMessageId: message.cmppSubmitGroupMessageId ?? message.messageId,
|
||||
// Null means a historical CMPP record created before this field existed.
|
||||
registeredDelivery: message.cmppRegisteredDelivery !== false,
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue one business-level HTTP callback and one CMPP status report for every
|
||||
* original client fragment that requested Registered_Delivery. Internal retry,
|
||||
* refund and billing remain message-level; only protocol delivery is expanded.
|
||||
*/
|
||||
export async function queueFinalReceiptDeliveries(
|
||||
prisma: PrismaService,
|
||||
queue: (request: DownstreamDeliveryQueueRequest) => Promise<unknown>,
|
||||
data: {
|
||||
message: FinalReceiptMessage;
|
||||
payload: Record<string, unknown>;
|
||||
segmentPayloads?: Record<number, Record<string, unknown>>;
|
||||
propagateHttpQueueError?: boolean;
|
||||
},
|
||||
) {
|
||||
const { message } = data;
|
||||
if (!message.tenantId || !message.applicationId) {
|
||||
return { queued: false, cmppTargetCount: 0 };
|
||||
}
|
||||
|
||||
// HTTP submissions have one client message identity, so their webhook stays
|
||||
// message-level even when the carrier internally split the SMS into segments.
|
||||
await queue({
|
||||
tenantId: message.tenantId,
|
||||
applicationId: message.applicationId,
|
||||
messageRecordId: message.id,
|
||||
messageId: message.messageId,
|
||||
deliveryType: 'receipt',
|
||||
payload: data.payload,
|
||||
queueHttpWebhook: true,
|
||||
queueCmppDelivery: false,
|
||||
propagateHttpQueueError: data.propagateHttpQueueError,
|
||||
});
|
||||
|
||||
const targets = (await resolveClientReceiptTargets(prisma, message))
|
||||
.filter((target) => target.registeredDelivery);
|
||||
for (const target of targets) {
|
||||
const isSingleFragment = target.segmentTotal === 1;
|
||||
await queue({
|
||||
tenantId: message.tenantId,
|
||||
applicationId: message.applicationId,
|
||||
messageRecordId: message.id,
|
||||
messageId: message.messageId,
|
||||
deliveryType: 'receipt',
|
||||
payload: {
|
||||
...data.payload,
|
||||
...data.segmentPayloads?.[target.segmentIndex],
|
||||
submitSequenceId: target.submitSequenceId,
|
||||
submitGroupMessageId: target.submitGroupMessageId,
|
||||
clientSegmentIndex: target.segmentIndex,
|
||||
clientSegmentTotal: target.segmentTotal,
|
||||
},
|
||||
receiptDedupeKey: isSingleFragment
|
||||
? `receipt:${message.id}`
|
||||
: `receipt:${message.id}:segment:${target.segmentIndex}`,
|
||||
queueHttpWebhook: false,
|
||||
queueCmppDelivery: true,
|
||||
});
|
||||
}
|
||||
return { queued: true, cmppTargetCount: targets.length };
|
||||
}
|
||||
Reference in New Issue
Block a user