diff --git a/api/logger.config.ts b/api/logger.config.ts index b5946c8c..5fc79de1 100644 --- a/api/logger.config.ts +++ b/api/logger.config.ts @@ -3,6 +3,7 @@ import { LoggerConfig } from './src/types'; export const defaultLoggerConfig: LoggerConfig = { // Filter by module name moduleFilter: [], + debugModules: [], // Filter by log prefix, e.g. adding // `pref` to filter is going to correspond to showing `[PREF] ` logs prefixFilter: [], @@ -17,6 +18,7 @@ export const defaultLoggerConfig: LoggerConfig = { export const debugLogConfig: LoggerConfig = { // Filter by module name moduleFilter: [], + debugModules: [], // Filter by log prefix, e.g. adding // `pref` to filter is going to correspond to showing `[PERF]` logs prefixFilter: [], diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 246530d7..29414311 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -123,27 +123,36 @@ model Listener { // RWA Operational Data model RWAPortfolio { - id String - driveId String - accounts RWAAccountOnPortfolio[] - principalLenderAccountId String - spvs RWAPortfolioSpvOnPortfolio[] - feeTypes RWAPortfolioServiceProviderOnPortfolio[] - fixedIncomeTypes RWAPortfolioFixedIncomeTypeOnPortfolio[] - portfolio RWAPortfolioAsset[] - - @@id([id, driveId]) + id String @id @default(uuid()) + driveId String + documentId String + accounts RWAAccountOnPortfolio[] + principalLenderAccountId String + spvs RWAPortfolioSpvOnPortfolio[] + feeTypes RWAPortfolioServiceProviderOnPortfolio[] + fixedIncomeTypes RWAPortfolioFixedIncomeTypeOnPortfolio[] + portfolio RWAPortfolioAsset[] + RWAPortfolioAccount RWAPortfolioAccount[] + RWAPortfolioFixedIncomeType RWAPortfolioFixedIncomeType[] + RWAPortfolioSpv RWAPortfolioSpv[] + RWAPortfolioServiceProvider RWAPortfolioServiceProvider[] + RWABaseTransaction RWABaseTransaction[] + RWAGroupTransaction RWAGroupTransaction[] + RWABaseTransactionOnGroupTransaction RWABaseTransactionOnGroupTransaction[] + + @@unique([driveId, documentId]) } model RWAPortfolioAccount { - id String - driveId String + id String @default(uuid()) + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) reference String label String? RWAAccountOnPortfolio RWAAccountOnPortfolio[] RWAPortfolioServiceProvider RWAPortfolioServiceProvider[] - @@id([id, driveId]) + @@id([id, portfolioId]) } enum RWAPortfolioAssetType { @@ -152,9 +161,12 @@ enum RWAPortfolioAssetType { } model RWAPortfolioAsset { - driveId String id String @default(uuid()) + assetRefId String + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) fixedIncomeTypeId String? + fixedIncomeType RWAPortfolioFixedIncomeType? @relation(fields: [fixedIncomeTypeId, portfolioId], references: [id, portfolioId]) name String? spvId String maturity String? @@ -167,89 +179,88 @@ model RWAPortfolioAsset { ISIN String? CUSIP String? coupon Float? - portfolio RWAPortfolio @relation(fields: [portfolioId, driveId], references: [id, driveId]) - portfolioId String - currency String? - assetType String - fixedIncomeType RWAPortfolioFixedIncomeType? @relation(fields: [fixedIncomeTypeId, driveId], references: [id, driveId]) - @@id([id, driveId]) + currency String? + assetType String + + @@id([id, portfolioId]) // TODO: Change to id, + @@unique([assetRefId, portfolioId]) } model RWAPortfolioFixedIncomeType { - id String - driveId String + id String @default(uuid()) + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) name String RWAPortfolioFixedIncomeTypeOnPortfolio RWAPortfolioFixedIncomeTypeOnPortfolio[] RWAPortfolioAsset RWAPortfolioAsset[] - @@id([id, driveId]) + @@id([id, portfolioId]) } model RWAPortfolioSpv { - id String - driveId String + id String @default(uuid()) + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) name String RWAPortfolioSpvOnPortfolio RWAPortfolioSpvOnPortfolio[] RWAPortfolioServiceProviderOnPortfolio RWAPortfolioServiceProviderOnPortfolio[] - @@id([id, driveId]) + @@id([id, portfolioId]) } model RWAPortfolioSpvOnPortfolio { - driveId String portfolioId String spvId String - portfolio RWAPortfolio @relation(fields: [portfolioId, driveId], references: [id, driveId]) - spv RWAPortfolioSpv @relation(fields: [spvId, driveId], references: [id, driveId]) + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) + spv RWAPortfolioSpv @relation(fields: [spvId, portfolioId], references: [id, portfolioId]) - @@id([spvId, portfolioId, driveId]) + @@id([spvId, portfolioId]) } model RWAAccountOnPortfolio { - driveId String portfolioId String accountId String - portfolio RWAPortfolio @relation(fields: [portfolioId, driveId], references: [id, driveId]) - account RWAPortfolioAccount @relation(fields: [accountId, driveId], references: [id, driveId]) + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) + account RWAPortfolioAccount @relation(fields: [accountId, portfolioId], references: [id, portfolioId]) - @@id([accountId, portfolioId, driveId]) + @@id([accountId, portfolioId]) } model RWAPortfolioServiceProvider { - id String @default(uuid()) - driveId String - name String - feeType String - accountId String - account RWAPortfolioAccount @relation(fields: [accountId, driveId], references: [id, driveId]) + id String @default(uuid()) + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) + name String + feeType String + accountId String + account RWAPortfolioAccount @relation(fields: [accountId, portfolioId], references: [id, portfolioId]) - @@id([id, driveId]) + @@id([id, portfolioId]) } model RWAPortfolioServiceProviderOnPortfolio { - driveId String portfolioId String spvId String - portfolio RWAPortfolio @relation(fields: [portfolioId, driveId], references: [id, driveId]) - spv RWAPortfolioSpv @relation(fields: [spvId, driveId], references: [id, driveId]) + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) + spv RWAPortfolioSpv @relation(fields: [spvId, portfolioId], references: [id, portfolioId]) - @@id([spvId, portfolioId, driveId]) + @@id([spvId, portfolioId]) } model RWAPortfolioFixedIncomeTypeOnPortfolio { - driveId String portfolioId String fixedIncomeTypeId String - portfolio RWAPortfolio @relation(fields: [portfolioId, driveId], references: [id, driveId]) - fixedIncome RWAPortfolioFixedIncomeType @relation(fields: [fixedIncomeTypeId, driveId], references: [id, driveId]) + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) + fixedIncome RWAPortfolioFixedIncomeType @relation(fields: [fixedIncomeTypeId, portfolioId], references: [id, portfolioId]) - @@id([fixedIncomeTypeId, portfolioId, driveId]) + @@id([fixedIncomeTypeId, portfolioId]) } model RWABaseTransaction { - driveId String id String + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) assetId String amount Float entryTime DateTime? @@ -262,31 +273,32 @@ model RWABaseTransaction { fixedIncomeTransactions RWAGroupTransaction[] @relation(name: "fixedIncomeTransaction") feeTransactions RWABaseTransactionOnGroupTransaction[] - @@id([id, driveId]) + @@id([id, portfolioId]) } model RWAGroupTransaction { - driveId String id String portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) type String - cashTransaction RWABaseTransaction? @relation(name: "cashTransaction", fields: [cashTransactionId, driveId], references: [id, driveId]) + cashTransaction RWABaseTransaction? @relation(name: "cashTransaction", fields: [cashTransactionId, portfolioId], references: [id, portfolioId]) cashTransactionId String? - fixedIncomeTransaction RWABaseTransaction? @relation(name: "fixedIncomeTransaction", fields: [fixedTransactionId, driveId], references: [id, driveId]) + fixedIncomeTransaction RWABaseTransaction? @relation(name: "fixedIncomeTransaction", fields: [fixedTransactionId, portfolioId], references: [id, portfolioId]) fixedTransactionId String? feeTransactions RWABaseTransactionOnGroupTransaction[] - @@id([id, driveId]) + @@id([id, portfolioId]) } model RWABaseTransactionOnGroupTransaction { - driveId String id String @default(uuid()) + portfolioId String + portfolio RWAPortfolio @relation(fields: [portfolioId], references: [id], onDelete: Cascade) groupTransactionId String baseTransactionId String - groupTransaction RWAGroupTransaction @relation(fields: [groupTransactionId, driveId], references: [id, driveId]) - baseTransaction RWABaseTransaction @relation(fields: [baseTransactionId, driveId], references: [id, driveId]) + groupTransaction RWAGroupTransaction @relation(fields: [groupTransactionId, portfolioId], references: [id, portfolioId]) + baseTransaction RWABaseTransaction @relation(fields: [baseTransactionId, portfolioId], references: [id, portfolioId]) - @@id([id, driveId]) + @@id([id, portfolioId]) } diff --git a/api/src/modules/document-drive/drive-resolver.ts b/api/src/modules/document-drive/drive-resolver.ts index ff1b265a..39bb7f5e 100644 --- a/api/src/modules/document-drive/drive-resolver.ts +++ b/api/src/modules/document-drive/drive-resolver.ts @@ -16,7 +16,7 @@ import { OperationScope } from 'document-model/document'; import stringify from 'json-stringify-deterministic'; import { getChildLogger } from '../../logger'; -const logger = getChildLogger({ msgPrefix: 'Drive' }); +const logger = getChildLogger({ msgPrefix: 'Drive Resolver' }); export const Node = objectType({ name: 'Node', @@ -275,6 +275,7 @@ export const pushUpdates = mutationField('pushUpdates', { strands: list(nonNull(InputStrandUpdate)), }, resolve: async (_parent, { strands }, ctx) => { + logger.info('pushUpdates') if (!strands || strands?.length === 0) return []; const listenerRevisions: IListenerRevision[] = await Promise.all(strands.map(async (s) => { diff --git a/api/src/modules/document/listenerManager.ts b/api/src/modules/document/listenerManager.ts index 9e845923..6446ba9a 100644 --- a/api/src/modules/document/listenerManager.ts +++ b/api/src/modules/document/listenerManager.ts @@ -4,6 +4,9 @@ import { DocumentDriveServer, IReceiver, InternalTransmitter, InternalTransmitte import { Listener, DocumentDriveDocument } from 'document-model-libs/document-drive'; import { Document, OperationScope } from "document-model/document" import { Prisma } from '@prisma/client'; +import { getChildLogger } from '../../logger'; + +const logger = getChildLogger({ msgPrefix: 'Listener Manager' }); const listeners: Promise[] = []; function loadModules(startPath: string, filter: string): Promise[] { @@ -19,6 +22,7 @@ function loadModules(startPath: string, filter: string): Promise[] { if (stat.isDirectory()) { loadModules(filename, filter); //recursive } else if (filename.endsWith(filter)) { + logger.info(`Loading listener from ${filename}`); listeners.push(import(filename)); }; }; @@ -40,6 +44,8 @@ async function registerListener(driveServer: DocumentDriveServer, driveId: strin block: false, label: listener.label!, }) + + logger.info(`Listener ${listener.label}(${listener.listenerId}) registered for drive ${driveId}`); } export async function init(driveServer: DocumentDriveServer, prisma: Prisma.TransactionClient) { @@ -59,12 +65,12 @@ export async function init(driveServer: DocumentDriveServer, prisma: Prisma.Tran const transmitter = (await driveServer.getTransmitter(driveId, listener.listenerId)); if (transmitter instanceof InternalTransmitter) { + logger.info(`Setting receiver for ${listener.listenerId}`); transmitter.setReceiver({ transmit: async (strands: InternalTransmitterUpdate[]) => { transmit(strands, prisma) } }) - } } } diff --git a/api/src/modules/document/model.ts b/api/src/modules/document/model.ts index 5f2403a5..db64ac86 100644 --- a/api/src/modules/document/model.ts +++ b/api/src/modules/document/model.ts @@ -1,4 +1,4 @@ -import type { Prisma } from '@prisma/client'; +import type { Prisma, PrismaClient } from '@prisma/client'; import { DocumentDriveServer, DriveInput, @@ -23,10 +23,11 @@ import { import { actions as rwaActions } from 'document-model-libs/dist/real-world-assets' -import logger from '../../logger'; -import { init } from './listenerManager'; +import { init } from './listenerManager'; +import { getChildLogger } from '../../logger'; +const logger = getChildLogger({ msgPrefix: 'Document Model' }); export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { const documentModels = [ DocumentModelLib, @@ -35,7 +36,7 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { const driveServer = new DocumentDriveServer( documentModels, - new PrismaStorage(prisma), + new PrismaStorage(prisma as PrismaClient), ); async function initialize() { @@ -89,6 +90,7 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { documentId?: string, ) => { if (!documentId) { + logger.info('adding drive operations') const result = await driveServer.addDriveOperations( driveId, operations, @@ -96,6 +98,7 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { return result; } + logger.info('adding operations to document') const result = await driveServer.addOperations( driveId, documentId, diff --git a/api/src/modules/real-world-assets/listener.ts b/api/src/modules/real-world-assets/listener.ts index ce1ec6a9..14ea6bbe 100644 --- a/api/src/modules/real-world-assets/listener.ts +++ b/api/src/modules/real-world-assets/listener.ts @@ -1,34 +1,266 @@ import { Prisma } from "@prisma/client"; -import { InternalTransmitterUpdate } from "document-drive"; -import { ListenerFilter } from "document-model-libs/document-drive"; -import { CashGroupTransactionType, RealWorldAssetsDocument, utils } from "document-model-libs/real-world-assets" +import { InternalTransmitterUpdate, OperationUpdate } from "document-drive"; +import { AddFileInput, DeleteNodeInput, DocumentDriveDocument, DocumentDriveState, ListenerFilter, actions } from "document-model-libs/document-drive"; +import { CashGroupTransactionType, CreateFixedIncomeAssetInput, EditFixedIncomeAssetInput, RealWorldAssetsDocument, RealWorldAssetsState, utils } from "document-model-libs/real-world-assets" +import { getChildLogger } from "../../logger"; +import { Action } from "document-model/document"; + +const logger = getChildLogger({ msgPrefix: 'RWA Internal Listener' }); export interface IReceiverOptions { - listenerId: string; - label: string; - block: boolean; - filter: ListenerFilter; + listenerId: string; + label: string; + block: boolean; + filter: ListenerFilter; } export const listener: IReceiverOptions = { - listenerId: "real-world-assets", - filter: { - branch: ["main"], - documentId: ["*"], - documentType: ["makerdao/rwa-portfolio", "powerhouse/document-drive"], - scope: ["*"], - }, - block: false, - label: "real-world-assets", + listenerId: "real-world-assets", + filter: { + branch: ["main"], + documentId: ["*"], + documentType: ["makerdao/rwa-portfolio", "powerhouse/document-drive"], + scope: ["*"], + }, + block: false, + label: "real-world-assets", } -export async function transmit(strands: InternalTransmitterUpdate[], prisma: Prisma.TransactionClient) { - for (const strand of strands) { - // TODO: come up with a better idea to check whether strandupdate is part of real world assets - if (!strand.state.principalLenderAccountId) { - continue; +export async function transmit(strands: InternalTransmitterUpdate[], prisma: Prisma.TransactionClient) { + // logger.info(strands); + for (const strand of strands) { + + if (strand.documentId === "") { + await handleDriveStrand(strand as InternalTransmitterUpdate, prisma); + } else { + await handleRwaDocumentStrand(strand as InternalTransmitterUpdate, prisma); + } + + + + } +} + +async function updateEntireState(strand: InternalTransmitterUpdate, prisma: Prisma.TransactionClient) { + const { transactions, principalLenderAccountId, fixedIncomeTypes, spvs, accounts, feeTypes, portfolio } = strand.state; + const { driveId, documentId } = strand; + // create portfolio document + const portfolioEntity = await prisma.rWAPortfolio.upsert({ + where: { + driveId_documentId: { + documentId, + driveId + } + }, + create: { + documentId, + driveId, + principalLenderAccountId: principalLenderAccountId, + }, + update: { + principalLenderAccountId: principalLenderAccountId, + }, + }); + + // create spvs + await prisma.rWAPortfolioSpv.createMany({ + data: spvs.map((spv) => ({ ...spv, portfolioId: portfolioEntity.id })), + skipDuplicates: true, + }); + + // create feeTypes + await prisma.rWAPortfolioServiceProvider.createMany({ + data: feeTypes.map((feeType) => ({ ...feeType, portfolioId: portfolioEntity.id })), + skipDuplicates: true, + }); + + // fixed income types + await prisma.rWAPortfolioFixedIncomeType.createMany({ + data: fixedIncomeTypes.map((fixedIncomeType) => ({ ...fixedIncomeType, portfolioId: portfolioEntity.id })), + skipDuplicates: true, + }); + + // create accounts + await prisma.rWAPortfolioAccount.createMany({ + data: accounts.map((account) => ({ ...account, portfolioId: portfolioEntity.id })), + skipDuplicates: true, + }); + + // create RWAPortfolioAsset + await prisma.rWAPortfolioAsset.createMany({ + data: portfolio.map((asset) => ({ ...asset, assetRefId: asset.id, portfolioId: portfolioEntity.id, assetType: utils.isCashAsset(asset) ? "Cash" : "FixedIncome" })), + skipDuplicates: true, + }); + + // create transactions + // TODO: add transactions + for (const transaction of transactions) { + const isCashTx = ["PrincipalDraw", "PrincipalReturn"].includes(transaction.type); + + // Create Grpup TX Entity + const groupTxEntity = await prisma.rWAGroupTransaction.create({ + data: { + id: transaction.id, + portfolioId: portfolioEntity.id, + type: transaction.type + }, + }) + + // Create Cash and/or FixedIncome TX Entity + // cashtx: + + + // Create and Link Fee TX Entities + console.log(transaction) + } + + // add relationships + await prisma.rWAPortfolioFixedIncomeTypeOnPortfolio.createMany({ + data: fixedIncomeTypes.map((fixedIncomeType) => ({ + fixedIncomeTypeId: fixedIncomeType.id, + portfolioId: portfolioEntity.id, + })), + skipDuplicates: true, + }); + + await prisma.rWAPortfolioServiceProviderOnPortfolio.createMany({ + data: feeTypes.map((feeType) => ({ portfolioId: portfolioEntity.id, spvId: feeType.id })), + skipDuplicates: true, + }); + + await prisma.rWAPortfolioSpvOnPortfolio.createMany({ + data: spvs.map((spv) => ({ portfolioId: portfolioEntity.id, spvId: spv.id })), + skipDuplicates: true, + }); + + await prisma.rWAAccountOnPortfolio.createMany({ + data: accounts.map((account) => ({ portfolioId: portfolioEntity.id, accountId: account.id })), + skipDuplicates: true, + }); +} + +async function analytics(prisma: Prisma.TransactionClient) { + // const data: Record = {}; + // for (const asset of portfolioEntity!.portfolio) { + // const type = asset.fixedIncomeType?.name; + // if (!type || asset.purchasePrice === null) { + // continue; + // } + // if (!data[type]) { + // data[type] = 0; + // } + + // data[type] = data[type] + asset.purchasePrice; + // } + + // console.log(data); +} +async function handleDriveStrand(strand: InternalTransmitterUpdate, prisma: Prisma.TransactionClient) { + logger.info("Received strand for drive"); + if (strandStartsFromOpZero(strand)) { + await deleteDriveState(strand.state, prisma); } + await doSurgicalDriveUpdate(strand, prisma); +} + +function strandStartsFromOpZero(strand: InternalTransmitterUpdate) { + const resetNeeded = strand.operations.length > 0 && strand.operations[0].index === 0; + logger.info(`Reset needed: ${resetNeeded}`); + return resetNeeded; +} +async function doSurgicalDriveUpdate(strand: InternalTransmitterUpdate, prisma: Prisma.TransactionClient) { + logger.info("Doing surgical drive update"); + for (const operation of strand.operations) { + logger.info(`Operation: ${operation.type}`); + switch (operation.type) { + case "ADD_FILE": + const addFileInput = operation.input as AddFileInput; + if (addFileInput.documentType === "makerdao/rwa-portfolio") { + const result = await prisma.rWAPortfolio.create({ + data: { + driveId: strand.driveId, + documentId: addFileInput.id, + principalLenderAccountId: "", + } + }) + logger.info({ PortfolioID: result.id }) + logger.info({ msg: "Adding file", operation }); + } + break; + case "DELETE_NODE": + const deleteNodeInput = operation.input as DeleteNodeInput; + const driveId = strand.driveId; + logger.info(`Removing file ${deleteNodeInput.id} from ${driveId}`); + const result = await prisma.rWAPortfolio.deleteMany({ + where: { + AND: { + documentId: deleteNodeInput.id, + driveId + } + } + }) + logger.info(`Removed ${result.count} portfolios`); + logger.info({ msg: "Removing file", operation }); + break; + default: + logger.info(`Ignoring operation ${operation.type}`); + break; + } + } +} + +async function deleteDriveState(state: DocumentDriveState, prisma: Prisma.TransactionClient) { + logger.info("Deleting rwa read model"); + await prisma.rWAPortfolio.deleteMany({ + where: { + driveId: state.id + } + }); +} + +async function rebuildRwaPortfolio(driveId: string, documentId: string, state: RealWorldAssetsState) { + logger.info("Rebuilding rwa portfolio"); + +} + +async function rwaPortfolioExists(driveId: string, documentId: string, prisma: Prisma.TransactionClient) { + const portfolio = await prisma.rWAPortfolio.findFirst({ + where: { + driveId, + documentId + } + }); + + return !!portfolio; +} + +async function handleRwaDocumentStrand(strand: InternalTransmitterUpdate, prisma: Prisma.TransactionClient) { + logger.info(`Received strand for document ${strand.documentId} with operations: ${strand.operations.map(op => op.type).join(", ")}`); + if (!await rwaPortfolioExists(strand.driveId, strand.documentId, prisma)) { + logger.info(`Skipping strand for document ${strand.documentId} as it doesn't exist in the read model`); + return; + } + + const surgicalOperations: Record void> = { + "CREATE_FIXED_INCOME_ASSET": (input: CreateFixedIncomeAssetInput) => { + logger.info({ msg: "Creating fixed income asset", input }); + }, + } + + + + if (strandStartsFromOpZero(strand) || !allOperationsAreSurgical(strand, surgicalOperations)) { + // await deleteDriveState(strand.state, prisma); + await rebuildRwaPortfolio(strand.driveId, strand.documentId, strand.state); + } else { + for (const operation of strand.operations) { + await doSurgicalRwaPortfolioUpdate(operation, prisma); + } + + } + + // TODO: come up with a better idea to check whether strandupdate is part of real world assets //TODO: check whether all operations can be applied individually otherwise delete database and insert everything new let operationsCanbeAppliedIndividually = false; for (const operation of strand.operations) { @@ -36,120 +268,22 @@ export async function transmit(strands: InternalTransmitterUpdate, prisma: Prisma.TransactionClient) { - const { transactions, principalLenderAccountId, fixedIncomeTypes, spvs, accounts, feeTypes, portfolio } = strand.state; - const driveId = strand.driveId; - const documentId = strand.documentId; - // create portfolio document - await prisma.rWAPortfolio.upsert({ - where: { - id_driveId: { id: documentId, driveId: driveId }, - }, - create: { - id: documentId, - driveId: driveId, - principalLenderAccountId: principalLenderAccountId, - }, - update: { - principalLenderAccountId: principalLenderAccountId, - }, - }); - - // create spvs - await prisma.rWAPortfolioSpv.createMany({ - data: spvs.map((spv) => ({ ...spv, driveId: driveId })), - skipDuplicates: true, - }); - - // create feeTypes - await prisma.rWAPortfolioServiceProvider.createMany({ - data: feeTypes.map((feeType) => ({ ...feeType, portfolioId: documentId, driveId: driveId })), - skipDuplicates: true, - }); - - // fixed income types - await prisma.rWAPortfolioFixedIncomeType.createMany({ - data: fixedIncomeTypes.map((fixedIncomeType) => ({ ...fixedIncomeType, driveId: driveId })), - skipDuplicates: true, - }); - - // create accounts - await prisma.rWAPortfolioAccount.createMany({ - data: accounts.map((account) => ({ ...account, portfolioId: documentId, driveId: driveId })), - skipDuplicates: true, - }); - - // create RWAPortfolioAsset - await prisma.rWAPortfolioAsset.createMany({ - data: portfolio.map((asset) => ({ ...asset, portfolioId: documentId, driveId: driveId, assetType: utils.isCashAsset(asset) ? "Cash" : "FixedIncome" })), - skipDuplicates: true, - }); - - // create transactions - // TODO: add transactions - // for (const transaction of transactions) { - // const isCashTx = ["PrincipalDraw", "PrincipalReturn"].includes(transaction.type); - - // // const cashTx = transaction as CashGroupTransactionType; - // const groupTxEntity = await prisma.rWAGroupTransaction.create({ - // data: { - // id: transaction.id, - // driveId: driveId, - // portfolioId: documentId, - // type: transaction.type - // }, - // }) - // console.log(transaction) - // } - - // add relationships - await prisma.rWAPortfolioFixedIncomeTypeOnPortfolio.createMany({ - data: fixedIncomeTypes.map((fixedIncomeType) => ({ - fixedIncomeTypeId: fixedIncomeType.id, - portfolioId: documentId, - driveId: driveId, - })), - skipDuplicates: true, - }); - - await prisma.rWAPortfolioServiceProviderOnPortfolio.createMany({ - data: feeTypes.map((feeType) => ({ portfolioId: documentId, driveId: driveId, spvId: feeType.id })), - skipDuplicates: true, - }); - - await prisma.rWAPortfolioSpvOnPortfolio.createMany({ - data: spvs.map((spv) => ({ portfolioId: documentId, driveId: driveId, spvId: spv.id })), - skipDuplicates: true, - }); - - await prisma.rWAAccountOnPortfolio.createMany({ - data: accounts.map((account) => ({ portfolioId: documentId, driveId: driveId, accountId: account.id })), - skipDuplicates: true, - }); +function doSurgicalRwaPortfolioUpdate(operation: OperationUpdate, prisma: Prisma.TransactionClient) { + logger.info({ msg: "Doing surgical rwa portfolio update", name: operation.type }); + return; } -async function analytics(prisma: Prisma.TransactionClient) { - // const data: Record = {}; - // for (const asset of portfolioEntity!.portfolio) { - // const type = asset.fixedIncomeType?.name; - // if (!type || asset.purchasePrice === null) { - // continue; - // } - // if (!data[type]) { - // data[type] = 0; - // } - - // data[type] = data[type] + asset.purchasePrice; - // } - - // console.log(data); +function allOperationsAreSurgical(strand: InternalTransmitterUpdate, surgicalOperations: Record void>) { + const allOperationsAreSurgical = strand.operations.filter(op => surgicalOperations[op.type] === undefined).length === 0; + logger.info(`All operations are surgical: ${allOperationsAreSurgical}`); + return allOperationsAreSurgical } + diff --git a/api/src/types.d.ts b/api/src/types.d.ts index 7bd5857f..33076f3c 100644 --- a/api/src/types.d.ts +++ b/api/src/types.d.ts @@ -3,6 +3,7 @@ import { Prisma } from '@prisma/client'; export declare interface LoggerConfig { moduleFilter: string[]; + debugModules: [], prefixFilter: string[]; logLevel: PinoLevel; dbLogLevel: Prisma.LogLevel[];