diff --git a/migrations/sqls/20260610000000-calling-routing-down.sql b/migrations/sqls/20260610000000-calling-routing-down.sql index 5fe5019..1425f48 100644 --- a/migrations/sqls/20260610000000-calling-routing-down.sql +++ b/migrations/sqls/20260610000000-calling-routing-down.sql @@ -10,7 +10,7 @@ declare v_purchasing_strategy sms.number_purchasing_strategy; begin -- Create the phone number record - insert into sms.all_phone_numbers ( + insert into sms.phone_numbers ( sending_location_id, phone_number ) diff --git a/src/apis/routing.ts b/src/apis/routing.ts index 9fc8743..3ff9f2f 100644 --- a/src/apis/routing.ts +++ b/src/apis/routing.ts @@ -5,8 +5,7 @@ import config from '../config'; import { pgPool } from '../db'; import { auth, ClientAuthenticatedRequest } from '../lib/auth'; import { - countAvailableCallingNumbers, - getCallingNumberForSendingLocation, + getCallingNumberWithCount, requestCallingNumber, } from '../lib/process-call'; import { getFromNumberMapping } from '../lib/process-message'; @@ -45,9 +44,13 @@ app.get('/get-number-for-contact', auth.client, async (req, res) => { [profile_id] ); + if (!profile) { + return res.status(404).json({ error: 'Profile not found' }); + } + const { daily_calling_limit } = profile; - if (!profile || daily_calling_limit === null) { + if (daily_calling_limit === null) { return res.status(400).json({ error: 'Profile does not have calling configured (daily_calling_limit not set)', @@ -61,16 +64,19 @@ app.get('/get-number-for-contact', auth.client, async (req, res) => { }); if (prevMapping !== undefined) { + const { sending_location_id, from_number } = prevMapping; + const { rows: [{ count }], } = await client.query<{ count: string }>( `select count(*) from sms.outbound_calls - where from_number = $1 + where sending_location_id = $1 + and from_number = $2 and created_at > date_trunc('day', now() at time zone 'America/Los_Angeles') at time zone 'UTC'`, - [prevMapping.from_number] + [sending_location_id, from_number] ); - const todayCallCount = parseInt(count, 10); + const todayCallCount = parseInt(count, 10); if (todayCallCount < daily_calling_limit) { await client.query( `update sms.from_number_mappings @@ -110,7 +116,7 @@ app.get('/get-number-for-contact', auth.client, async (req, res) => { .json({ error: 'No sending location found for contact' }); } - const fromNumber = await getCallingNumberForSendingLocation( + const { fromNumber, availableCount } = await getCallingNumberWithCount( client, sendingLocationId, daily_calling_limit @@ -131,12 +137,6 @@ app.get('/get-number-for-contact', auth.client, async (req, res) => { [fromNumber, sendingLocationId] ); - const availableCount = await countAvailableCallingNumbers( - client, - sendingLocationId, - daily_calling_limit - ); - if (availableCount <= sendingLocationMinCallingNumbers) { await requestCallingNumber(client, sendingLocationId); } diff --git a/src/lib/process-call.ts b/src/lib/process-call.ts index f92882c..c7e48e1 100644 --- a/src/lib/process-call.ts +++ b/src/lib/process-call.ts @@ -1,35 +1,32 @@ import { PoolOrPoolClient } from '../db'; import { chooseAreaCodeForSendingLocation } from './process-message'; -export const getCallingNumberForSendingLocation = async ( +export const getCallingNumberWithCount = async ( client: PoolOrPoolClient, sendingLocationId: string, dailyCallingLimit: number -): Promise => { +): Promise<{ fromNumber: string | null; availableCount: number }> => { const { rows: [row], - } = await client.query<{ phone_number: string | null }>( - `select phone_number - from sms.get_available_calling_numbers($1, $2) - order by priority asc, call_count asc - limit 1`, - [sendingLocationId, dailyCallingLimit] - ); - return row?.phone_number ?? null; -}; - -export const countAvailableCallingNumbers = async ( - client: PoolOrPoolClient, - sendingLocationId: string, - dailyCallingLimit: number -): Promise => { - const { - rows: [{ count }], - } = await client.query<{ count: string }>( - 'select count(*) from sms.get_available_calling_numbers($1, $2)', + } = await client.query<{ phone_number: string | null; count: string }>( + `with available as ( + select phone_number, priority, call_count + from sms.get_available_calling_numbers($1, $2) + ), + ranked as ( + select + first_value(phone_number) over (order by priority, call_count) as phone_number, + count(*) over () as count + from available + limit 1 + ) + select phone_number, count from ranked`, [sendingLocationId, dailyCallingLimit] ); - return parseInt(count, 10); + return { + fromNumber: row?.phone_number ?? null, + availableCount: row?.count ? parseInt(row.count, 10) : 0, + }; }; export const requestCallingNumber = async (