UNPKG

@accounter/server

Version:
74 lines (67 loc) 2.97 kB
import { GraphQLError } from 'graphql'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { ChargesProvider } from '../../charges/providers/charges.provider.js'; import { BY_SCORE_EVALUATION_CAP, chargeRequiresMatch, matchesQueueMode, } from '../helpers/awaiting-match-queue.helper.js'; import { QueueMatchEvaluatorProvider } from '../providers/queue-match-evaluator.provider.js'; import type { ChargesMatcherModule } from '../types.js'; export const chargesAwaitingMatchQueueResolver: ChargesMatcherModule.Resolvers = { Query: { chargesAwaitingMatchQueue: async ( _, { limit, offset, businessId, fromDate, toDate, mode, sortBy }, { injector }, ) => { try { // Tenant scoping: only ever fetch charges of the verified admin owner, // so a requested businessId can't leak other tenants' data const { ownerId } = await injector.get(AdminContextProvider).getVerifiedAdminContext(); // Most recent first (default sort of getChargesByFilters) const charges = await injector.get(ChargesProvider).getChargesByFilters({ ownerIds: [ownerId], businessIds: businessId ? [businessId] : undefined, fromDate, toDate, }); const queue = charges.filter( charge => chargeRequiresMatch(charge) && matchesQueueMode(charge, mode), ); const safeLimit = Math.max(0, limit ?? 20); const safeOffset = Math.max(0, offset ?? 0); const evaluator = injector.get(QueueMatchEvaluatorProvider); if (sortBy === 'BY_SCORE') { // Evaluate the capped window of most recent charges, sort the whole // derived list by top confidence score, and paginate that list const window = queue.slice(0, BY_SCORE_EVALUATION_CAP); const evaluated = await evaluator.evaluateMatchesForCharges(window); const topScore = (suggestions: { confidenceScore: number }[]): number => suggestions[0]?.confidenceScore ?? 0; const sorted = evaluated.toSorted( (a, b) => topScore(b.suggestions) - topScore(a.suggestions), ); return { baseCharges: sorted.slice(safeOffset, safeOffset + safeLimit), totalCount: window.length, }; } // BY_DATE (default): paginate first, then lazily evaluate scores for // the requested page only const page = queue.slice(safeOffset, safeOffset + safeLimit); const baseCharges = await evaluator.evaluateMatchesForCharges(page); return { baseCharges, totalCount: queue.length, }; } catch (e) { if (e instanceof GraphQLError) { throw e; } const message = (e as Error)?.message ?? 'Unknown error'; throw new GraphQLError(`Failed to fetch charges awaiting match: ${message}`); } }, }, };