* Add DB schema * Init zod schema * Init RoutingTrace and PendingRoutingTrace repository interfaces * Create PrismaPendingRoutingTraceRepository * Init RoutingTraceService * Create RoutingTraceService container * User routing trace service in routing * Create RoutingTraceRepository and PrismaRoutingTraceRepoistory * Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository * Update DI containers * RoutingTraceService create process booking method * Use pending routing trace rather than URL params * Fix schema * Fix writing assignment reason for routed booking * Remove from service * Refactor RoutingTraceService to not rely on async local storage * Pass RoutingTraceService through routing call * Add attribute-logic-evaluated to routing trace step * Add routing trace to trpc endpoint * Add CRM routing trace step * Fix extracting routing trace to assignment reason * Add back CRM params to prevent refetching * test: Add unit tests for RoutingTraceService and repositories Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * test: Add missing mock for RoutingTraceService in getRoutedUrl tests Also fix pre-existing lint issues in the test file: - Add explicit types to mockForm and mockSerializableForm variables - Add explicit type to url parameter in mockContext - Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>' Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * Link pending form submission to routing trace * Clean up * Add lookup field assignment reasons * Rename to PendingRoutingTrace * Add migration file * fix: Update RoutingTraceService tests to use assignmentReasonRepository mock - Add getStepsCount() method back to RoutingTraceService - Add queuedFormResponseId support to processForBooking method - Update tests to use mockAssignmentReasonRepository instead of prisma mock - Remove test for missing routingTraceRepository (all deps now required) Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: Remove PII (organizer email) from log payload in RoutingTraceService Addresses Cubic AI review feedback with confidence 9/10. Logging PII violates sensitive information logging rules. Co-Authored-By: unknown <> * Write field values at the time of routing * Write attributes used to route * feat: add CRM routing trace service Add CrmRoutingTraceService as a reusable wrapper around RoutingTraceService for CRM-specific tracing. Also adds CrmRoutingTraceServiceInterface type to support passing trace services through the CRM call chain. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add Salesforce routing trace infrastructure Add SalesforceRoutingTrace static class with 19 trace methods covering: - Account resolution (SOQL path): searching by website, contact domain - Lookup field queries - Owner lookups (contact, lead, account) - Validation and skip scenarios - GraphQL three-tier resolution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: wire CRM trace service through call chain Pass crmTrace parameter through the CRM call chain: - routerGetCrmContactOwnerEmail creates CrmRoutingTraceService - Passes to app booking form handlers and CRM round robin skip - CrmManager.getContacts accepts and forwards crmTrace - All handlers accept optional crmTrace parameter Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add trace instrumentation to Salesforce CRM service Instrument Salesforce CRM methods with routing trace steps: - getContacts: trace owner lookups for contact/lead/account - getAccountIdBasedOnEmailDomainOfContacts: trace website and domain searches - findUserEmailFromLookupField: trace lookup field queries - GetAccountRecordsForRRSkip (GraphQL): trace three-tier resolution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: rename SalesforceRoutingTrace to SalesforceRoutingTraceService Consistent naming with CrmRoutingTraceService. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add unit tests for CrmRoutingTraceService and SalesforceRoutingTraceService Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * refactor: use AsyncLocalStorage for CRM routing trace Replace explicit crmTrace parameter passing with AsyncLocalStorage: - Add AsyncLocalStorage to RoutingTraceService with getCurrent() and runAsync() - Update SalesforceRoutingTraceService to auto-resolve trace from AsyncLocalStorage - Remove CrmRoutingTraceService wrapper (no longer needed) - Remove crmTrace parameter from all CRM method signatures - Wrap CRM operations in routingTraceService.runAsync() context This is cleaner than threading crmTrace through 5+ function layers. The trace context is available within the withReporting wrapper. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Use async local storage for `CrmRoutingTraceService` * fix: correct template literal syntax in RoutingTraceService Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: use narrowed eventTypeId variable in nested async function Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: update SalesforceRoutingTraceService tests to use AsyncLocalStorage API Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * refactor: rename crmTrace to crmRoutingTraceService for consistency Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Volnei Munhoz <volnei@cal.com>
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
|
import type { RoutingTraceService } from "./RoutingTraceService";
|
|
|
|
/**
|
|
* CRM-specific wrapper around RoutingTraceService.
|
|
* Uses AsyncLocalStorage to make the trace service available to CRM-specific
|
|
* trace services (e.g., SalesforceRoutingTraceService) without explicit parameter passing.
|
|
*/
|
|
export class CrmRoutingTraceService {
|
|
private static als = new AsyncLocalStorage<CrmRoutingTraceService>();
|
|
|
|
constructor(private parentTraceService: RoutingTraceService) {}
|
|
|
|
/**
|
|
* Get the current CrmRoutingTraceService from AsyncLocalStorage.
|
|
* Returns undefined if not within a CRM trace context.
|
|
*/
|
|
static getCurrent(): CrmRoutingTraceService | undefined {
|
|
return CrmRoutingTraceService.als.getStore();
|
|
}
|
|
|
|
/**
|
|
* Factory method to create a CrmRoutingTraceService if parent exists.
|
|
* Returns undefined if no parent trace service is provided.
|
|
*/
|
|
static create(parent: RoutingTraceService | undefined): CrmRoutingTraceService | undefined {
|
|
if (!parent) return undefined;
|
|
return new CrmRoutingTraceService(parent);
|
|
}
|
|
|
|
/**
|
|
* Run an async function within this CRM trace service's context.
|
|
* Any code within the callback can access this trace service via getCurrent().
|
|
*/
|
|
runAsync<T>(fn: () => Promise<T>): Promise<T> {
|
|
return CrmRoutingTraceService.als.run(this, fn);
|
|
}
|
|
|
|
/**
|
|
* Add a trace step to the parent service.
|
|
*/
|
|
addStep(domain: string, step: string, data: Record<string, unknown> = {}): void {
|
|
this.parentTraceService.addStep({ domain, step, data });
|
|
}
|
|
}
|