Files
Neon-Desk/lib/server/repos/company-financial-bundles.ts

113 lines
3.3 KiB
TypeScript

import { and, eq } from 'drizzle-orm';
import type {
FinancialCadence,
FinancialSurfaceKind
} from '@/lib/types';
import { db, getSqliteClient } from '@/lib/server/db';
import { withFinancialIngestionSchemaRetry } from '@/lib/server/db/financial-ingestion-schema';
import { companyFinancialBundle } from '@/lib/server/db/schema';
export const CURRENT_COMPANY_FINANCIAL_BUNDLE_VERSION = 14;
export type CompanyFinancialBundleRecord = {
id: number;
ticker: string;
surface_kind: FinancialSurfaceKind;
cadence: FinancialCadence;
bundle_version: number;
source_snapshot_ids: number[];
source_signature: string;
payload: Record<string, unknown>;
created_at: string;
updated_at: string;
};
function toBundleRecord(row: typeof companyFinancialBundle.$inferSelect): CompanyFinancialBundleRecord {
return {
id: row.id,
ticker: row.ticker,
surface_kind: row.surface_kind,
cadence: row.cadence,
bundle_version: row.bundle_version,
source_snapshot_ids: row.source_snapshot_ids ?? [],
source_signature: row.source_signature,
payload: row.payload,
created_at: row.created_at,
updated_at: row.updated_at
};
}
export async function getCompanyFinancialBundle(input: {
ticker: string;
surfaceKind: FinancialSurfaceKind;
cadence: FinancialCadence;
}) {
const [row] = await db
.select()
.from(companyFinancialBundle)
.where(and(
eq(companyFinancialBundle.ticker, input.ticker.trim().toUpperCase()),
eq(companyFinancialBundle.surface_kind, input.surfaceKind),
eq(companyFinancialBundle.cadence, input.cadence)
))
.limit(1);
return row ? toBundleRecord(row) : null;
}
export async function upsertCompanyFinancialBundle(input: {
ticker: string;
surfaceKind: FinancialSurfaceKind;
cadence: FinancialCadence;
sourceSnapshotIds: number[];
sourceSignature: string;
payload: Record<string, unknown>;
}) {
const now = new Date().toISOString();
const [saved] = await withFinancialIngestionSchemaRetry({
client: getSqliteClient(),
context: 'company-financial-bundle-upsert',
operation: async () => await db
.insert(companyFinancialBundle)
.values({
ticker: input.ticker.trim().toUpperCase(),
surface_kind: input.surfaceKind,
cadence: input.cadence,
bundle_version: CURRENT_COMPANY_FINANCIAL_BUNDLE_VERSION,
source_snapshot_ids: input.sourceSnapshotIds,
source_signature: input.sourceSignature,
payload: input.payload,
created_at: now,
updated_at: now
})
.onConflictDoUpdate({
target: [
companyFinancialBundle.ticker,
companyFinancialBundle.surface_kind,
companyFinancialBundle.cadence
],
set: {
bundle_version: CURRENT_COMPANY_FINANCIAL_BUNDLE_VERSION,
source_snapshot_ids: input.sourceSnapshotIds,
source_signature: input.sourceSignature,
payload: input.payload,
updated_at: now
}
})
.returning()
});
return toBundleRecord(saved);
}
export async function deleteCompanyFinancialBundlesForTicker(ticker: string) {
return await db
.delete(companyFinancialBundle)
.where(eq(companyFinancialBundle.ticker, ticker.trim().toUpperCase()));
}
export const __companyFinancialBundlesInternals = {
BUNDLE_VERSION: CURRENT_COMPANY_FINANCIAL_BUNDLE_VERSION
};