import { billingActionPolicyTable } from '../db/index.js' const POLICY = billingActionPolicyTable.tableName export function createActionPolicyRepo(db, runner = db) { async function findActionPolicy(action) { const row = await runner(POLICY).where({ action }).first() if (row === undefined) return null return actionPolicyFromRow(row) } async function listActionPolicies() { const rows = await runner(POLICY).orderBy('action') return rows.map(actionPolicyFromRow) } async function updateActionPolicy(policy) { await runner(POLICY).where({ action: policy.action }).update({ points: policy.points, period_kind: policy.period_kind, timezone: policy.timezone, enabled: policy.enabled, updated_by: policy.updated_by, updated_at: new Date(), }) return findActionPolicy(policy.action) } return { findActionPolicy, listActionPolicies, updateActionPolicy } } function actionPolicyFromRow(row) { const action = requiredString(row.action, 'billing_action_policy.action') const policy = { action, points: requiredInteger(row.points, 'billing_action_policy.points', action), period_kind: requiredString(row.period_kind, 'billing_action_policy.period_kind', action), timezone: nullableString(row.timezone, 'billing_action_policy.timezone', action), enabled: Boolean(row.enabled), updated_by: nullableString(row.updated_by, 'billing_action_policy.updated_by', action), } assertActionPolicy(policy) return policy } function assertActionPolicy(policy) { switch (policy.period_kind) { case 'daily': if (policy.timezone === null) throw new Error(`Daily billing action requires timezone, id=${policy.action}`) return case 'none': if (policy.timezone !== null) throw new Error(`Non-periodic billing action cannot have timezone, id=${policy.action}`) return default: throw new Error(`Unhandled billing action period: ${policy.period_kind}, id: ${policy.action}`) } } function requiredString(value, field, id = 'unknown') { if (typeof value !== 'string' || value === '') throw new Error(`Invalid ${field}, id=${id}`) return value } function nullableString(value, field, id) { if (value === null) return null return requiredString(value, field, id) } function requiredInteger(value, field, id) { const number = Number(value) if (!Number.isSafeInteger(number) || number <= 0) throw new Error(`Invalid ${field}, id=${id}`) return number }