diff --git a/services/workflows-service/src/alert/alert.repository.ts b/services/workflows-service/src/alert/alert.repository.ts index 4078850a9e..3157798498 100644 --- a/services/workflows-service/src/alert/alert.repository.ts +++ b/services/workflows-service/src/alert/alert.repository.ts @@ -24,6 +24,7 @@ export class AlertRepository { const queryArgs = this.scopeService.scopeFindFirst(args, projectIds); return await this.prisma.extendedClient.alert.findFirst({ + ...queryArgs, where: queryArgs.where, orderBy: { createdAt: 'desc', diff --git a/services/workflows-service/src/alert/alert.service.ts b/services/workflows-service/src/alert/alert.service.ts index 9967680934..2f45f9b862 100644 --- a/services/workflows-service/src/alert/alert.service.ts +++ b/services/workflows-service/src/alert/alert.service.ts @@ -352,7 +352,7 @@ export class AlertService { const projectId = alertDef.projectId; const now = new Date(); - return this.alertRepository.create({ + const alertData = { data: { projectId, alertDefinitionId: alertDef.id, @@ -377,7 +377,9 @@ export class AlertService { createdAt: now, dataTimestamp: now, }, - }); + }; + + return this.alertRepository.create(alertData); } private async isDuplicateAlert( diff --git a/services/workflows-service/src/test/helpers/create-alert-definition.ts b/services/workflows-service/src/test/helpers/create-alert-definition.ts index 7c525c993a..db804a7e29 100644 --- a/services/workflows-service/src/test/helpers/create-alert-definition.ts +++ b/services/workflows-service/src/test/helpers/create-alert-definition.ts @@ -58,7 +58,9 @@ export const createAlertDefinition = async ( }, excludePaymentMethods: faker.datatype.boolean(), }, - subjects: [faker.helpers.arrayElement(['counterpartyId', 'businessId', 'transactionId'])], + subjects: [ + faker.helpers.arrayElement(['counterpartyBeneficiaryId', 'counterpartyOriginatorId']), + ], }, }; diff --git a/services/workflows-service/src/test/helpers/create-alert.ts b/services/workflows-service/src/test/helpers/create-alert.ts index 15e50bdf6c..3b10028fdc 100644 --- a/services/workflows-service/src/test/helpers/create-alert.ts +++ b/services/workflows-service/src/test/helpers/create-alert.ts @@ -1,18 +1,27 @@ import { AlertService } from '@/alert/alert.service'; import { AlertDefinition } from '@prisma/client'; +import { createTransactionRecord } from './create-transaction-record'; +import { InlineRule } from '@/data-analytics/types'; export const createAlert = async ( projectId: string, alertDefinition: AlertDefinition, alertService: AlertService, + transactions: Awaited>, ) => { + const subject = (alertDefinition.inlineRule as InlineRule).subjects[0] as + | 'counterpartyBeneficiaryId' + | 'counterpartyOriginatorId'; + + const subjectValue = transactions[0]?.[subject]; + // Accessing private method for testing purposes while maintaining types return await alertService.createAlert( { ...alertDefinition, projectId, }, - [], + [{ [subject]: subjectValue }], {}, {}, ); diff --git a/services/workflows-service/src/test/helpers/create-transaction-record.ts b/services/workflows-service/src/test/helpers/create-transaction-record.ts index 46c9200298..e93ee3c73a 100644 --- a/services/workflows-service/src/test/helpers/create-transaction-record.ts +++ b/services/workflows-service/src/test/helpers/create-transaction-record.ts @@ -22,6 +22,7 @@ import { Test } from '@nestjs/testing'; import { ClsModule } from 'nestjs-cls'; import { ProjectScopeService } from '@/project/project-scope.service'; import { DataAnalyticsService } from '@/data-analytics/data-analytics.service'; +import { TransactionCreatedDto } from '@/transaction/dtos/transaction-created.dto'; export const createTransactionRecord = async ( prisma: PrismaClient, @@ -122,7 +123,7 @@ export const createTransactionRecord = async ( brand: faker.helpers.arrayElement(['Visa', 'Mastercard', 'Amex']), expiryMonth: faker.date.future().getMonth().toString().padStart(2, '0'), expiryYear: faker.date.future().getFullYear().toString(), - holderName: faker.name.findName(), + holderName: faker.name.fullName(), tokenized: faker.random.alphaNumeric(24), cardBin: parseInt(faker.finance.creditCardNumber().slice(0, 6)), }, @@ -131,10 +132,30 @@ export const createTransactionRecord = async ( }; // Use createBulk to create the transaction - const createdTransactions = await transactionService.createBulk({ + const createdTransactions = (await transactionService.createBulk({ transactionsPayload: [transactionCreateDto], projectId: project.id, + })) satisfies Array< + | TransactionCreatedDto + | { + errorMessage: string; + correlationId: string; + } + >; + + const result = await prisma.transactionRecord.findMany({ + include: { + counterpartyOriginator: true, + counterpartyBeneficiary: true, + }, + where: { + id: { + in: createdTransactions + .map(transaction => 'id' in transaction && transaction.id) + .filter(Boolean), + }, + }, }); - return createdTransactions; + return result; }; diff --git a/services/workflows-service/src/transaction/transaction.controller.external.intg.test.ts b/services/workflows-service/src/transaction/transaction.controller.external.intg.test.ts index f1430945c7..1d7a0dc4b6 100644 --- a/services/workflows-service/src/transaction/transaction.controller.external.intg.test.ts +++ b/services/workflows-service/src/transaction/transaction.controller.external.intg.test.ts @@ -624,7 +624,7 @@ describe('#TransactionControllerExternal', () => { const createTransactionWithDate = async (daysAgo: number) => { const currentDate = new Date(); - await createTransactionRecord(app.get(PrismaService), project, { + return await createTransactionRecord(app.get(PrismaService), project, { date: new Date(currentDate.getTime() - daysAgo * 24 * 60 * 60 * 1000), }); }; @@ -635,9 +635,8 @@ describe('#TransactionControllerExternal', () => { getAlertDefinitionWithTimeOptions('days', 7) as any, alertService, ); - const alert = await createAlert(project.id, alertDefinition, alertService); - await Promise.all([ + const result = await Promise.all([ // 5 transactions in the past 7 days createTransactionWithDate(1), createTransactionWithDate(3), @@ -652,12 +651,14 @@ describe('#TransactionControllerExternal', () => { createTransactionWithDate(20), ]); + const alert = await createAlert(project.id, alertDefinition, alertService, result[0]); + const response = await request(app.getHttpServer()) .get(`/external/transactions/by-alert?alertId=${alert.id}`) .set('authorization', `Bearer ${API_KEY}`); expect(response.status).toBe(200); - expect(response.body).toHaveLength(5); + expect(response.body).toHaveLength(1); }); it('returns 404 when alertId is not found', async () => { const nonExistentAlertId = faker.datatype.uuid(); @@ -674,12 +675,15 @@ describe('#TransactionControllerExternal', () => { getAlertDefinitionWithTimeOptions('days', 1) as any, alertService, ); - const alert = await createAlert(project.id, alertDefinition, alertService); + // TODO: shouldnt happen, might we remove this test? + const alert = await createAlert(project.id, alertDefinition, alertService, []); // Create a transaction older than the alert criteria - await createTransactionRecord(app.get(PrismaService), project, { - date: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), // 2 days ago - }); + const tx1 = ( + await createTransactionRecord(app.get(PrismaService), project, { + date: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), // 2 days ago + }) + )[0]; const response = await request(app.getHttpServer()) .get(`/external/transactions/by-alert?alertId=${alert.id}`) @@ -710,7 +714,7 @@ describe('#TransactionControllerExternal', () => { alertService, ); - const alert = await createAlert(otherProject.id, alertDefinition, alertService); + const alert = await createAlert(otherProject.id, alertDefinition, alertService, []); const response = await request(app.getHttpServer()) .get(`/external/transactions/by-alert?alertId=${alert.id}`) @@ -726,9 +730,11 @@ describe('#TransactionControllerExternal', () => { const fiveDaysAgo = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000); // Create transactions at different times - await createTransactionRecord(app.get(PrismaService), project, { date: fifteenDaysAgo }); - await createTransactionRecord(app.get(PrismaService), project, { date: tenDaysAgo }); - await createTransactionRecord(app.get(PrismaService), project, { date: fiveDaysAgo }); + const tx1 = ( + await createTransactionRecord(app.get(PrismaService), project, { + date: fifteenDaysAgo, + }) + )[0]; alertDefinition = await createAlertDefinition( project.id, @@ -736,14 +742,14 @@ describe('#TransactionControllerExternal', () => { alertService, ); - const alert = await createAlert(project.id, alertDefinition, alertService); + const alert = await createAlert(project.id, alertDefinition, alertService, [tx1!]); const response = await request(app.getHttpServer()) .get(`/external/transactions/by-alert?alertId=${alert.id}`) .set('authorization', `Bearer ${API_KEY}`); expect(response.status).toBe(200); - expect(response.body).toHaveLength(3); + expect(response.body).toHaveLength(1); // Verify that all returned transactions are within the last 15 days response.body.forEach((transaction: any) => { diff --git a/services/workflows-service/src/transaction/transaction.controller.external.ts b/services/workflows-service/src/transaction/transaction.controller.external.ts index 711c2700f4..9d1ef8a163 100644 --- a/services/workflows-service/src/transaction/transaction.controller.external.ts +++ b/services/workflows-service/src/transaction/transaction.controller.external.ts @@ -292,13 +292,6 @@ export class TransactionControllerExternal { @Get('/by-alert') // @UseCustomerAuthGuard() - @swagger.ApiOkResponse({ description: 'Returns an array of transactions.' }) - @swagger.ApiQuery({ name: 'businessId', description: 'Filter by business ID.', required: false }) - @swagger.ApiQuery({ - name: 'counterpartyId', - description: 'Filter by counterparty ID.', - required: false, - }) @swagger.ApiQuery({ name: 'startDate', type: Date,