UNPKG

@accounter/server

Version:
81 lines 3.88 kB
import { __decorate, __metadata } from "tslib"; import DataLoader from 'dataloader'; import { Injectable, Scope } from 'graphql-modules'; import { sql } from '@pgtyped/runtime'; import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js'; import { TransactionsProvider } from '../../transactions/providers/transactions.provider.js'; import { extractSecurityKeys } from '../helpers/security-key.helper.js'; /** * No owner_id predicate: accounter_schema.poalim_securities is FORCE RLS with a * tenant_isolation policy, so going through TenantAwareDBClient scopes this to the * acting tenant. The dedup key includes branch/account, so one tenant can hold the * same security in several accounts — DISTINCT ON keeps the freshest scrape. */ const getSecuritiesByKeys = sql ` SELECT DISTINCT ON (security_key) id, security_key, eng_name, heb_name, symbol, eng_symbol, heb_symbol, item_type, stock_type, exchange, currency_code, is_etf, is_foreign, as_of_date FROM accounter_schema.poalim_securities WHERE security_key = ANY($securityKeys!) ORDER BY security_key, as_of_date DESC;`; let ForeignSecuritiesProvider = class ForeignSecuritiesProvider { db; transactionsProvider; constructor(db, transactionsProvider) { this.db = db; this.transactionsProvider = transactionsProvider; } async batchSecuritiesByKeys(securityKeys) { const securities = await getSecuritiesByKeys.run({ securityKeys: [...securityKeys] }, this.db); // DISTINCT ON in the query guarantees one row per key, so a plain Map is enough. const securityByKey = new Map(securities.map(security => [security.security_key, security])); return securityKeys.map(key => securityByKey.get(key) ?? null); } securityByKeyLoader = new DataLoader((keys) => this.batchSecuritiesByKeys(keys)); /** * The securities a charge's transactions reference, keyed off the security key each * description carries. Keys with no ingested row are still returned, with a null * `details`, so a stale or missing scrape is visible instead of silently dropping data. */ async getChargeSecurities(chargeId) { const transactions = await this.transactionsProvider.transactionsByChargeIDLoader.load(chargeId); const transactionIdsByKey = new Map(); for (const transaction of transactions) { for (const key of extractSecurityKeys(transaction.source_description)) { const transactionIds = transactionIdsByKey.get(key); if (transactionIds) { transactionIds.push(transaction.id); } else { transactionIdsByKey.set(key, [transaction.id]); } } } if (transactionIdsByKey.size === 0) { return []; } const keys = [...transactionIdsByKey.keys()].sort(); const details = await this.securityByKeyLoader.loadMany(keys); return keys.map((key, index) => { const detail = details[index]; return { id: `${chargeId}-${key}`, securityKey: key, // loadMany surfaces a rejected key as an Error rather than throwing; treat it // the same as "not ingested" so one bad key can't blank the whole section. details: detail instanceof Error ? null : detail, transactionIds: transactionIdsByKey.get(key) ?? [], }; }); } }; ForeignSecuritiesProvider = __decorate([ Injectable({ scope: Scope.Operation, global: true, }), __metadata("design:paramtypes", [TenantAwareDBClient, TransactionsProvider]) ], ForeignSecuritiesProvider); export { ForeignSecuritiesProvider }; //# sourceMappingURL=foreign-securities.provider.js.map