From 92774073b8f655cf3c381bac73cc208e80d18bf4 Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Thu, 26 Sep 2024 17:14:48 +0330 Subject: [PATCH 1/7] Added projectUserRecord and its repository --- package.json | 3 +- src/entities/entities.ts | 2 + src/entities/projectUserRecord.ts | 44 +++++++++++ .../projectRoundRecordRepository.test.ts | 32 ++++---- .../projectUserRecordRepository.test.ts | 76 +++++++++++++++++++ .../projectUserRecordRepository.ts | 35 +++++++++ 6 files changed, 176 insertions(+), 16 deletions(-) create mode 100644 src/entities/projectUserRecord.ts create mode 100644 src/repositories/projectUserRecordRepository.test.ts create mode 100644 src/repositories/projectUserRecordRepository.ts diff --git a/package.json b/package.json index 19025d718..576312e61 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,8 @@ "test:projectUpdateRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectUpdateRepository.test.ts", "test:broadcastNotificationRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/broadcastNotificationRepository.test.ts", "test:projectAddressRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectAddressRepository.test.ts", - "test:ProjectRoundRecordRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/ProjectRoundRecordRepository.test.ts", + "test:ProjectRoundRecordRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectRoundRecordRepository.test.ts", + "test:ProjectUserRecordRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectUserRecordRepository.test.ts", "test:userPassportScoreRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/userPassportScoreRepository.test.ts", "test:donationService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/qfRoundHistoryRepository.test.ts ./src/services/donationService.test.ts", "test:draftDonationService": "NODE_ENV=test mocha -t 99999 ./test/pre-test-scripts.ts src/services/chains/evm/draftDonationService.test.ts src/repositories/draftDonationRepository.test.ts src/workers/draftDonationMatchWorker.test.ts src/resolvers/draftDonationResolver.test.ts", diff --git a/src/entities/entities.ts b/src/entities/entities.ts index 6ffbd3ff7..1baacf651 100644 --- a/src/entities/entities.ts +++ b/src/entities/entities.ts @@ -34,6 +34,7 @@ import { UserQfRoundModelScore } from './userQfRoundModelScore'; import { UserEmailVerification } from './userEmailVerification'; import { EarlyAccessRound } from './earlyAccessRound'; import { ProjectRoundRecord } from './projectRoundRecord'; +import { ProjectUserRecord } from './projectUserRecord'; export const getEntities = (): DataSourceOptions['entities'] => { return [ @@ -80,5 +81,6 @@ export const getEntities = (): DataSourceOptions['entities'] => { UserQfRoundModelScore, EarlyAccessRound, ProjectRoundRecord, + ProjectUserRecord, ]; }; diff --git a/src/entities/projectUserRecord.ts b/src/entities/projectUserRecord.ts new file mode 100644 index 000000000..eb03517f7 --- /dev/null +++ b/src/entities/projectUserRecord.ts @@ -0,0 +1,44 @@ +import { Field, Float, ID, ObjectType } from 'type-graphql'; +import { + BaseEntity, + Column, + Entity, + Index, + ManyToOne, + PrimaryGeneratedColumn, + RelationId, +} from 'typeorm'; +import { Project } from './project'; +import { ProjectRoundRecord } from './projectRoundRecord'; +import { User } from './user'; + +@Entity() +@ObjectType() +@Index(['projectId', 'userId'], { + unique: true, +}) +export class ProjectUserRecord extends BaseEntity { + @Field(_type => ID) + @PrimaryGeneratedColumn() + id: number; + + @Field(_type => Float) + @Column({ type: 'float', default: 0 }) + totalDonationAmount: number; + + @Field(_type => Project) + @ManyToOne(_type => Project, { eager: true }) + project: Project; + + @Column({ nullable: false }) + @RelationId((ps: ProjectRoundRecord) => ps.project) + projectId: number; + + @Field(_type => User) + @ManyToOne(_type => User, { eager: true }) + user: User; + + @Column({ nullable: false }) + @RelationId((ps: ProjectUserRecord) => ps.user) + userId: number; +} diff --git a/src/repositories/projectRoundRecordRepository.test.ts b/src/repositories/projectRoundRecordRepository.test.ts index e7dccabde..9795484dd 100644 --- a/src/repositories/projectRoundRecordRepository.test.ts +++ b/src/repositories/projectRoundRecordRepository.test.ts @@ -21,25 +21,19 @@ describe('ProjectRoundRecord test cases', () => { let earlyAccessRound1, earlyAccessRound2, earlyAccessRound3; let qfRound1, qfRound2; - async function insertDonation({ - amount, - valueUsd, - earlyAccessRoundId, - qfRoundId, - }: { - amount: number; - valueUsd: number; - earlyAccessRoundId?: number; - qfRoundId?: number; - }) { + async function insertDonation( + overrides: Partial< + Pick< + Donation, + 'amount' | 'valueUsd' | 'earlyAccessRoundId' | 'qfRoundId' | 'status' + > + >, + ) { return saveDonationDirectlyToDb( { ...createDonationData(), - amount, - valueUsd, - earlyAccessRoundId, - qfRoundId, status: DONATION_STATUS.VERIFIED, + ...overrides, }, SEED_DATA.FIRST_USER.id, projectId, @@ -112,7 +106,15 @@ describe('ProjectRoundRecord test cases', () => { const amount = 100; const valueUsd = 150; + const unverifiedAmount = 200; + const unverifiedValueUsd = 300; + await insertDonation({ amount, valueUsd }); + await insertDonation({ + amount: unverifiedAmount, + valueUsd: unverifiedValueUsd, + status: DONATION_STATUS.PENDING, + }); await updateOrCreateProjectRoundRecord(projectId); diff --git a/src/repositories/projectUserRecordRepository.test.ts b/src/repositories/projectUserRecordRepository.test.ts new file mode 100644 index 000000000..a821550f1 --- /dev/null +++ b/src/repositories/projectUserRecordRepository.test.ts @@ -0,0 +1,76 @@ +import { assert } from 'chai'; +import { + createDonationData, + createProjectData, + generateRandomEtheriumAddress, + saveDonationDirectlyToDb, + saveProjectDirectlyToDb, + saveUserDirectlyToDb, +} from '../../test/testUtils'; +import { updateOrCreateProjectUserRecord } from './projectUserRecordRepository'; +import { DONATION_STATUS } from '../entities/donation'; + +describe('projectUserRecordRepository', () => { + let project; + let user; + + beforeEach(async () => { + project = await saveProjectDirectlyToDb(createProjectData()); + user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); + }); + + it('should return 0 when there is no donation', async () => { + const projectUserRecord = await updateOrCreateProjectUserRecord({ + projectId: project.id, + userId: user.id, + }); + + assert.isOk(projectUserRecord); + assert.equal(projectUserRecord.totalDonationAmount, 0); + }); + + it('should return the total verified donation amount', async () => { + const verifiedDonationAmount1 = 100; + const verifiedDonationAmount2 = 200; + const unverifiedDonationAmount = 300; + + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount1, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount2, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: unverifiedDonationAmount, + status: DONATION_STATUS.PENDING, + }, + user.id, + project.id, + ); + + const projectUserRecord = await updateOrCreateProjectUserRecord({ + projectId: project.id, + userId: user.id, + }); + + assert.isOk(projectUserRecord); + assert.equal( + projectUserRecord.totalDonationAmount, + verifiedDonationAmount1 + verifiedDonationAmount2, + ); + }); +}); diff --git a/src/repositories/projectUserRecordRepository.ts b/src/repositories/projectUserRecordRepository.ts new file mode 100644 index 000000000..f6c2d6dfe --- /dev/null +++ b/src/repositories/projectUserRecordRepository.ts @@ -0,0 +1,35 @@ +import { Donation, DONATION_STATUS } from '../entities/donation'; +import { ProjectUserRecord } from '../entities/projectUserRecord'; + +export async function updateOrCreateProjectUserRecord({ + projectId, + userId, +}: { + projectId: number; + userId: number; +}): Promise { + const { totalDonationAmount } = await Donation.createQueryBuilder('donation') + .select('SUM(donation.amount)', 'totalDonationAmount') + .where('donation.projectId = :projectId', { projectId }) + .andWhere('donation.status = :status', { + status: DONATION_STATUS.VERIFIED, + }) + .andWhere('donation.userId = :userId', { userId }) + .getRawOne(); + + let projectUserRecord = await ProjectUserRecord.findOneBy({ + projectId, + userId, + }); + + if (!projectUserRecord) { + projectUserRecord = ProjectUserRecord.create({ + projectId, + userId, + }); + } + + projectUserRecord.totalDonationAmount = totalDonationAmount || 0; + + return projectUserRecord.save(); +} From 53ff13dee373e37ff1e82a51642e02a7b7a846be Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Thu, 26 Sep 2024 23:57:38 +0330 Subject: [PATCH 2/7] Added projectUserRecode to user resolver --- .../projectUserRecordRepository.test.ts | 51 ++++++++++++++- .../projectUserRecordRepository.ts | 11 ++++ src/resolvers/userResolver.test.ts | 64 +++++++++++++++++++ src/resolvers/userResolver.ts | 9 +++ src/services/donationService.ts | 5 ++ test/graphqlQueries.ts | 6 ++ 6 files changed, 145 insertions(+), 1 deletion(-) diff --git a/src/repositories/projectUserRecordRepository.test.ts b/src/repositories/projectUserRecordRepository.test.ts index a821550f1..6a25c9c50 100644 --- a/src/repositories/projectUserRecordRepository.test.ts +++ b/src/repositories/projectUserRecordRepository.test.ts @@ -7,7 +7,10 @@ import { saveProjectDirectlyToDb, saveUserDirectlyToDb, } from '../../test/testUtils'; -import { updateOrCreateProjectUserRecord } from './projectUserRecordRepository'; +import { + getProjectUserRecordAmount, + updateOrCreateProjectUserRecord, +} from './projectUserRecordRepository'; import { DONATION_STATUS } from '../entities/donation'; describe('projectUserRecordRepository', () => { @@ -73,4 +76,50 @@ describe('projectUserRecordRepository', () => { verifiedDonationAmount1 + verifiedDonationAmount2, ); }); + + it('should return the total verified donation amount for a specific project', async () => { + const verifiedDonationAmount1 = 100; + const verifiedDonationAmount2 = 200; + const unverifiedDonationAmount = 300; + + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount1, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount2, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: unverifiedDonationAmount, + status: DONATION_STATUS.PENDING, + }, + user.id, + project.id, + ); + + await updateOrCreateProjectUserRecord({ + projectId: project.id, + userId: user.id, + }); + + const amount = await getProjectUserRecordAmount({ + projectId: project.id, + userId: user.id, + }); + + assert.equal(amount, verifiedDonationAmount1 + verifiedDonationAmount2); + }); }); diff --git a/src/repositories/projectUserRecordRepository.ts b/src/repositories/projectUserRecordRepository.ts index f6c2d6dfe..98e5cb642 100644 --- a/src/repositories/projectUserRecordRepository.ts +++ b/src/repositories/projectUserRecordRepository.ts @@ -33,3 +33,14 @@ export async function updateOrCreateProjectUserRecord({ return projectUserRecord.save(); } + +export async function getProjectUserRecordAmount({ + projectId, + userId, +}: { + projectId: number; + userId: number; +}): Promise { + const record = await ProjectUserRecord.findOneBy({ projectId, userId }); + return record?.totalDonationAmount || 0; +} diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index 78ceced4b..b111bdd7e 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -19,6 +19,7 @@ import { acceptedTermsOfService, batchMintingEligibleUsers, checkUserPrivadoVerifiedState, + projectUserTotalDonationAmount, refreshUserScores, updateUser, userByAddress, @@ -32,6 +33,7 @@ import { updateUserTotalDonated } from '../services/userService'; import { getUserEmailConfirmationFields } from '../repositories/userRepository'; import { UserEmailVerification } from '../entities/userEmailVerification'; import { PrivadoAdapter } from '../adapters/privado/privadoAdapter'; +import { updateOrCreateProjectUserRecord } from '../repositories/projectUserRecordRepository'; describe('updateUser() test cases', updateUserTestCases); describe('userByAddress() test cases', userByAddressTestCases); @@ -59,6 +61,11 @@ describe( batchMintingEligibleUsersTestCases, ); +describe( + 'projectUserTotalDonationAmount() test cases', + projectUserTotalDonationAmountTestCases, +); + // TODO I think we can delete addUserVerification query // describe('addUserVerification() test cases', addUserVerificationTestCases); function refreshUserScoresTestCases() { @@ -1292,3 +1299,60 @@ function batchMintingEligibleUsersTestCases() { ]); }); } + +function projectUserTotalDonationAmountTestCases() { + it('should return total donation amount of a user for a project', async () => { + const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); + const project = await saveProjectDirectlyToDb(createProjectData()); + const verifiedDonationAmount1 = 100; + const verifiedDonationAmount2 = 200; + const unverifiedDonationAmount = 300; + + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount1, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: verifiedDonationAmount2, + status: DONATION_STATUS.VERIFIED, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: unverifiedDonationAmount, + status: DONATION_STATUS.PENDING, + }, + user.id, + project.id, + ); + + await updateOrCreateProjectUserRecord({ + projectId: project.id, + userId: user.id, + }); + + const result = await axios.post(graphqlUrl, { + query: projectUserTotalDonationAmount, + variables: { + projectId: project.id, + userId: user.id, + }, + }); + + assert.isOk(result.data); + assert.equal( + result.data.data.projectUserTotalDonationAmount, + verifiedDonationAmount1 + verifiedDonationAmount2, + ); + }); +} diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index 133e20e8c..ba2078c53 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -36,6 +36,7 @@ import { addressHasDonated } from '../repositories/donationRepository'; // import { getOrttoPersonAttributes } from '../adapters/notifications/NotificationCenterAdapter'; import { retrieveActiveQfRoundUserMBDScore } from '../repositories/qfRoundRepository'; import { PrivadoAdapter } from '../adapters/privado/privadoAdapter'; +import { getProjectUserRecordAmount } from '../repositories/projectUserRecordRepository'; @ObjectType() class UserRelatedAddressResponse { @@ -451,4 +452,12 @@ export class UserResolver { } return false; } + + @Query(_returns => Number) + async projectUserTotalDonationAmount( + @Arg('projectId', _type => Int, { nullable: false }) projectId: number, + @Arg('userId', _type => Int, { nullable: false }) userId: number, + ) { + return getProjectUserRecordAmount({ projectId, userId }); + } } diff --git a/src/services/donationService.ts b/src/services/donationService.ts index bee49db9a..6083354a0 100644 --- a/src/services/donationService.ts +++ b/src/services/donationService.ts @@ -40,6 +40,7 @@ import { getOrttoPersonAttributes } from '../adapters/notifications/Notification import { CustomToken, getTokenPrice } from './priceService'; import { updateProjectStatistics } from './projectService'; import { updateOrCreateProjectRoundRecord } from '../repositories/projectRoundRecordRepository'; +import { updateOrCreateProjectUserRecord } from '../repositories/projectUserRecordRepository'; export const TRANSAK_COMPLETED_STATUS = 'COMPLETED'; @@ -272,6 +273,10 @@ export const syncDonationStatusWithBlockchainNetwork = async (params: { donation.qfRoundId, donation.earlyAccessRoundId, ); + await updateOrCreateProjectUserRecord({ + projectId: donation.projectId, + userId: donation.userId, + }); await sendNotificationForDonation({ donation, diff --git a/test/graphqlQueries.ts b/test/graphqlQueries.ts index 04fa12ee9..374904f23 100644 --- a/test/graphqlQueries.ts +++ b/test/graphqlQueries.ts @@ -2127,3 +2127,9 @@ export const getProjectRoundRecordsQuery = ` } } `; + +export const projectUserTotalDonationAmount = ` + query ProjectUserTotalDonationAmount($projectId: Int!, $userId: Int!) { + projectUserTotalDonationAmount(projectId: $projectId, userId: $userId) + } +`; From 85d8d63036387490477b9b48719806b0c88de10e Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Sun, 29 Sep 2024 14:21:15 +0330 Subject: [PATCH 3/7] Update ProjectUserRecord and related resolvers --- src/entities/projectUserRecord.ts | 10 ++ .../earlyAccessRoundRepository.test.ts | 12 +- .../projectUserRecordRepository.test.ts | 82 +++++++++- .../projectUserRecordRepository.ts | 40 +++-- src/resolvers/userResolver.test.ts | 143 +++++++++++------- src/resolvers/userResolver.ts | 24 ++- test/graphqlQueries.ts | 10 +- test/testUtils.ts | 2 +- 8 files changed, 247 insertions(+), 76 deletions(-) diff --git a/src/entities/projectUserRecord.ts b/src/entities/projectUserRecord.ts index eb03517f7..bcdfa5a93 100644 --- a/src/entities/projectUserRecord.ts +++ b/src/entities/projectUserRecord.ts @@ -26,6 +26,16 @@ export class ProjectUserRecord extends BaseEntity { @Column({ type: 'float', default: 0 }) totalDonationAmount: number; + // Early access total donation amount + @Field(_type => Float) + @Column({ type: 'float', default: 0 }) + eaTotalDonationAmount: number; + + // QF rounds total donation amount + @Field(_type => Float) + @Column({ type: 'float', default: 0 }) + qfTotalDonationAmount: number; + @Field(_type => Project) @ManyToOne(_type => Project, { eager: true }) project: Project; diff --git a/src/repositories/earlyAccessRoundRepository.test.ts b/src/repositories/earlyAccessRoundRepository.test.ts index 94009b7f2..df98aeb4f 100644 --- a/src/repositories/earlyAccessRoundRepository.test.ts +++ b/src/repositories/earlyAccessRoundRepository.test.ts @@ -4,7 +4,7 @@ import { findAllEarlyAccessRounds, findActiveEarlyAccessRound, } from './earlyAccessRoundRepository'; -import { saveRoundDirectlyToDb } from '../../test/testUtils'; +import { saveEARoundDirectlyToDb } from '../../test/testUtils'; describe('EarlyAccessRound Repository Test Cases', () => { beforeEach(async () => { @@ -24,7 +24,7 @@ describe('EarlyAccessRound Repository Test Cases', () => { endDate: new Date('2024-09-05'), }; - const savedRound = await saveRoundDirectlyToDb(roundData); + const savedRound = await saveEARoundDirectlyToDb(roundData); expect(savedRound).to.be.an.instanceof(EarlyAccessRound); expect(savedRound.roundNumber).to.equal(roundData.roundNumber); @@ -38,12 +38,12 @@ describe('EarlyAccessRound Repository Test Cases', () => { it('should find all Early Access Rounds', async () => { // Save a couple of rounds first - await saveRoundDirectlyToDb({ + await saveEARoundDirectlyToDb({ roundNumber: 1, startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }); - await saveRoundDirectlyToDb({ + await saveEARoundDirectlyToDb({ roundNumber: 2, startDate: new Date('2024-09-06'), endDate: new Date('2024-09-10'), @@ -71,8 +71,8 @@ describe('EarlyAccessRound Repository Test Cases', () => { }; // Save both active and inactive rounds - await saveRoundDirectlyToDb(activeRoundData); - await saveRoundDirectlyToDb(inactiveRoundData); + await saveEARoundDirectlyToDb(activeRoundData); + await saveEARoundDirectlyToDb(inactiveRoundData); const activeRound = await findActiveEarlyAccessRound(); diff --git a/src/repositories/projectUserRecordRepository.test.ts b/src/repositories/projectUserRecordRepository.test.ts index 6a25c9c50..5c57fd7aa 100644 --- a/src/repositories/projectUserRecordRepository.test.ts +++ b/src/repositories/projectUserRecordRepository.test.ts @@ -1,9 +1,11 @@ import { assert } from 'chai'; +import moment from 'moment'; import { createDonationData, createProjectData, generateRandomEtheriumAddress, saveDonationDirectlyToDb, + saveEARoundDirectlyToDb, saveProjectDirectlyToDb, saveUserDirectlyToDb, } from '../../test/testUtils'; @@ -12,6 +14,7 @@ import { updateOrCreateProjectUserRecord, } from './projectUserRecordRepository'; import { DONATION_STATUS } from '../entities/donation'; +import { QfRound } from '../entities/qfRound'; describe('projectUserRecordRepository', () => { let project; @@ -120,6 +123,83 @@ describe('projectUserRecordRepository', () => { userId: user.id, }); - assert.equal(amount, verifiedDonationAmount1 + verifiedDonationAmount2); + assert.equal( + amount.totalDonationAmount, + verifiedDonationAmount1 + verifiedDonationAmount2, + ); + }); + + it('should return correct ea and qf donation amounts', async () => { + const ea1 = await saveEARoundDirectlyToDb({ + roundNumber: 1, + startDate: new Date('2024-09-01'), + endDate: new Date('2024-09-05'), + }); + const ea2 = await saveEARoundDirectlyToDb({ + roundNumber: 2, + startDate: new Date('2024-09-06'), + endDate: new Date('2024-09-10'), + }); + + const qfRound = await QfRound.create({ + isActive: true, + name: 'test qf ', + allocatedFund: 100, + minimumPassportScore: 8, + slug: 'QF - 2024-09-10', + beginDate: moment('2024-09-10').add(1, 'days').toDate(), + endDate: moment('2024-09-10').add(10, 'days').toDate(), + }).save(); + + const ea1DonationAmount = 100; + const ea2DonationAmount = 200; + const qfDonationAmount = 400; + + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: ea1DonationAmount, + status: DONATION_STATUS.VERIFIED, + earlyAccessRoundId: ea1.id, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: ea2DonationAmount, + status: DONATION_STATUS.VERIFIED, + earlyAccessRoundId: ea2.id, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: qfDonationAmount, + status: DONATION_STATUS.VERIFIED, + qfRoundId: qfRound.id, + }, + user.id, + project.id, + ); + + const userRecord = await updateOrCreateProjectUserRecord({ + projectId: project.id, + userId: user.id, + }); + + assert.isOk(userRecord); + assert.equal( + userRecord.eaTotalDonationAmount, + ea1DonationAmount + ea2DonationAmount, + ); + assert.equal(userRecord.qfTotalDonationAmount, qfDonationAmount); + assert.equal( + userRecord.totalDonationAmount, + ea1DonationAmount + ea2DonationAmount + qfDonationAmount, + ); }); }); diff --git a/src/repositories/projectUserRecordRepository.ts b/src/repositories/projectUserRecordRepository.ts index 98e5cb642..98fdd6cb7 100644 --- a/src/repositories/projectUserRecordRepository.ts +++ b/src/repositories/projectUserRecordRepository.ts @@ -8,14 +8,24 @@ export async function updateOrCreateProjectUserRecord({ projectId: number; userId: number; }): Promise { - const { totalDonationAmount } = await Donation.createQueryBuilder('donation') - .select('SUM(donation.amount)', 'totalDonationAmount') - .where('donation.projectId = :projectId', { projectId }) - .andWhere('donation.status = :status', { - status: DONATION_STATUS.VERIFIED, - }) - .andWhere('donation.userId = :userId', { userId }) - .getRawOne(); + const { eaTotalDonationAmount, qfTotalDonationAmount, totalDonationAmount } = + await Donation.createQueryBuilder('donation') + .select('SUM(donation.amount)', 'totalDonationAmount') + // sum eaTotalDonationAmount if earlyAccessRoundId is not null + .addSelect( + 'SUM(CASE WHEN donation.earlyAccessRoundId IS NOT NULL THEN donation.amount ELSE 0 END)', + 'eaTotalDonationAmount', + ) + .addSelect( + 'SUM(CASE WHEN donation.qfRoundId IS NOT NULL THEN donation.amount ELSE 0 END)', + 'qfTotalDonationAmount', + ) + .where('donation.projectId = :projectId', { projectId }) + .andWhere('donation.status = :status', { + status: DONATION_STATUS.VERIFIED, + }) + .andWhere('donation.userId = :userId', { userId }) + .getRawOne(); let projectUserRecord = await ProjectUserRecord.findOneBy({ projectId, @@ -29,18 +39,28 @@ export async function updateOrCreateProjectUserRecord({ }); } + projectUserRecord.eaTotalDonationAmount = eaTotalDonationAmount || 0; + projectUserRecord.qfTotalDonationAmount = qfTotalDonationAmount || 0; projectUserRecord.totalDonationAmount = totalDonationAmount || 0; return projectUserRecord.save(); } +export type ProjectUserRecordAmounts = Pick< + ProjectUserRecord, + 'totalDonationAmount' | 'eaTotalDonationAmount' | 'qfTotalDonationAmount' +>; export async function getProjectUserRecordAmount({ projectId, userId, }: { projectId: number; userId: number; -}): Promise { +}): Promise { const record = await ProjectUserRecord.findOneBy({ projectId, userId }); - return record?.totalDonationAmount || 0; + return { + totalDonationAmount: record?.totalDonationAmount || 0, + eaTotalDonationAmount: record?.eaTotalDonationAmount || 0, + qfTotalDonationAmount: record?.qfTotalDonationAmount || 0, + }; } diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index b111bdd7e..ab3a1ecbb 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -1,8 +1,10 @@ // TODO Write test cases -import axios from 'axios'; +import axios, { AxiosResponse } from 'axios'; import { assert } from 'chai'; import sinon from 'sinon'; +import { ExecutionResult } from 'graphql'; +import moment from 'moment'; import { User } from '../entities/user'; import { createDonationData, @@ -11,6 +13,7 @@ import { generateTestAccessToken, graphqlUrl, saveDonationDirectlyToDb, + saveEARoundDirectlyToDb, saveProjectDirectlyToDb, saveUserDirectlyToDb, SEED_DATA, @@ -19,7 +22,7 @@ import { acceptedTermsOfService, batchMintingEligibleUsers, checkUserPrivadoVerifiedState, - projectUserTotalDonationAmount, + projectUserTotalDonationAmounts, refreshUserScores, updateUser, userByAddress, @@ -33,7 +36,11 @@ import { updateUserTotalDonated } from '../services/userService'; import { getUserEmailConfirmationFields } from '../repositories/userRepository'; import { UserEmailVerification } from '../entities/userEmailVerification'; import { PrivadoAdapter } from '../adapters/privado/privadoAdapter'; -import { updateOrCreateProjectUserRecord } from '../repositories/projectUserRecordRepository'; +import { + ProjectUserRecordAmounts, + updateOrCreateProjectUserRecord, +} from '../repositories/projectUserRecordRepository'; +import { QfRound } from '../entities/qfRound'; describe('updateUser() test cases', updateUserTestCases); describe('userByAddress() test cases', userByAddressTestCases); @@ -1302,57 +1309,89 @@ function batchMintingEligibleUsersTestCases() { function projectUserTotalDonationAmountTestCases() { it('should return total donation amount of a user for a project', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb(createProjectData()); - const verifiedDonationAmount1 = 100; - const verifiedDonationAmount2 = 200; - const unverifiedDonationAmount = 300; - - await saveDonationDirectlyToDb( - { - ...createDonationData(), - amount: verifiedDonationAmount1, - status: DONATION_STATUS.VERIFIED, - }, - user.id, - project.id, - ); - await saveDonationDirectlyToDb( - { - ...createDonationData(), - amount: verifiedDonationAmount2, - status: DONATION_STATUS.VERIFIED, - }, - user.id, - project.id, - ); - await saveDonationDirectlyToDb( - { - ...createDonationData(), - amount: unverifiedDonationAmount, - status: DONATION_STATUS.PENDING, - }, - user.id, - project.id, - ); - - await updateOrCreateProjectUserRecord({ - projectId: project.id, - userId: user.id, - }); - - const result = await axios.post(graphqlUrl, { - query: projectUserTotalDonationAmount, - variables: { + it('should return total donation amount of a user for a project', async () => { + const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); + const project = await saveProjectDirectlyToDb(createProjectData()); + + const ea1 = await saveEARoundDirectlyToDb({ + roundNumber: 1, + startDate: new Date('2024-09-01'), + endDate: new Date('2024-09-05'), + }); + const ea2 = await saveEARoundDirectlyToDb({ + roundNumber: 2, + startDate: new Date('2024-09-06'), + endDate: new Date('2024-09-10'), + }); + + const qfRound = await QfRound.create({ + isActive: true, + name: 'test qf ', + allocatedFund: 100, + minimumPassportScore: 8, + slug: 'QF - 2024-09-10', + beginDate: moment('2024-09-10').add(1, 'days').toDate(), + endDate: moment('2024-09-10').add(10, 'days').toDate(), + }).save(); + + const ea1DonationAmount = 100; + const ea2DonationAmount = 200; + const qfDonationAmount = 400; + + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: ea1DonationAmount, + status: DONATION_STATUS.VERIFIED, + earlyAccessRoundId: ea1.id, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: ea2DonationAmount, + status: DONATION_STATUS.VERIFIED, + earlyAccessRoundId: ea2.id, + }, + user.id, + project.id, + ); + await saveDonationDirectlyToDb( + { + ...createDonationData(), + amount: qfDonationAmount, + status: DONATION_STATUS.VERIFIED, + qfRoundId: qfRound.id, + }, + user.id, + project.id, + ); + await updateOrCreateProjectUserRecord({ projectId: project.id, userId: user.id, - }, + }); + + const result: AxiosResponse< + ExecutionResult<{ + projectUserTotalDonationAmounts: ProjectUserRecordAmounts; + }> + > = await axios.post(graphqlUrl, { + query: projectUserTotalDonationAmounts, + variables: { + projectId: project.id, + userId: user.id, + }, + }); + + assert.isOk(result.data); + assert.deepEqual(result.data.data?.projectUserTotalDonationAmounts, { + eaTotalDonationAmount: ea1DonationAmount + ea2DonationAmount, + qfTotalDonationAmount: qfDonationAmount, + totalDonationAmount: + ea1DonationAmount + ea2DonationAmount + qfDonationAmount, + }); }); - - assert.isOk(result.data); - assert.equal( - result.data.data.projectUserTotalDonationAmount, - verifiedDonationAmount1 + verifiedDonationAmount2, - ); }); } diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index ba2078c53..0b4543625 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -2,6 +2,7 @@ import { Arg, Ctx, Field, + Float, Int, Mutation, ObjectType, @@ -59,6 +60,18 @@ class BatchMintingEligibleUserResponse { skip: number; } +@ObjectType() +class ProjectUserRecordAmounts { + @Field(_type => Float) + totalDonationAmount: number; + + @Field(_type => Float) + eaTotalDonationAmount: number; + + @Field(_type => Float) + qfTotalDonationAmount: number; +} + // eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class UserResolver { @@ -453,11 +466,16 @@ export class UserResolver { return false; } - @Query(_returns => Number) - async projectUserTotalDonationAmount( + @Query(_returns => ProjectUserRecordAmounts) + async projectUserTotalDonationAmounts( @Arg('projectId', _type => Int, { nullable: false }) projectId: number, @Arg('userId', _type => Int, { nullable: false }) userId: number, ) { - return getProjectUserRecordAmount({ projectId, userId }); + const record = await getProjectUserRecordAmount({ projectId, userId }); + return { + totalDonationAmount: record.totalDonationAmount, + eaTotalDonationAmount: record.eaTotalDonationAmount, + qfTotalDonationAmount: record.qfTotalDonationAmount, + }; } } diff --git a/test/graphqlQueries.ts b/test/graphqlQueries.ts index 374904f23..cb1710ca1 100644 --- a/test/graphqlQueries.ts +++ b/test/graphqlQueries.ts @@ -2128,8 +2128,12 @@ export const getProjectRoundRecordsQuery = ` } `; -export const projectUserTotalDonationAmount = ` - query ProjectUserTotalDonationAmount($projectId: Int!, $userId: Int!) { - projectUserTotalDonationAmount(projectId: $projectId, userId: $userId) +export const projectUserTotalDonationAmounts = ` + query ProjectUserTotalDonationAmounts($projectId: Int!, $userId: Int!) { + projectUserTotalDonationAmounts(projectId: $projectId, userId: $userId) { + totalDonationAmount + eaTotalDonationAmount + qfTotalDonationAmount + } } `; diff --git a/test/testUtils.ts b/test/testUtils.ts index 81e7bce5d..020299912 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -2059,7 +2059,7 @@ export function generateRandomSolanaTxHash() { // list of test cases titles that doesn't require DB interaction export const dbIndependentTests = ['AdminJsPermissions']; -export const saveRoundDirectlyToDb = async ( +export const saveEARoundDirectlyToDb = async ( roundData: Partial, ): Promise => { const round = EarlyAccessRound.create(roundData) as EarlyAccessRound; From 78ddc0db7b37a254679eb5e59f0e825b388dd077 Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Sun, 29 Sep 2024 14:33:49 +0330 Subject: [PATCH 4/7] Made qf slug unique in tests --- src/repositories/projectUserRecordRepository.test.ts | 3 ++- src/resolvers/userResolver.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/repositories/projectUserRecordRepository.test.ts b/src/repositories/projectUserRecordRepository.test.ts index 5c57fd7aa..c7eedc267 100644 --- a/src/repositories/projectUserRecordRepository.test.ts +++ b/src/repositories/projectUserRecordRepository.test.ts @@ -3,6 +3,7 @@ import moment from 'moment'; import { createDonationData, createProjectData, + generateQfRoundNumber, generateRandomEtheriumAddress, saveDonationDirectlyToDb, saveEARoundDirectlyToDb, @@ -146,7 +147,7 @@ describe('projectUserRecordRepository', () => { name: 'test qf ', allocatedFund: 100, minimumPassportScore: 8, - slug: 'QF - 2024-09-10', + slug: 'QF - 2024-09-10 - ' + generateQfRoundNumber(), beginDate: moment('2024-09-10').add(1, 'days').toDate(), endDate: moment('2024-09-10').add(10, 'days').toDate(), }).save(); diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index ab3a1ecbb..cbdd316af 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -9,6 +9,7 @@ import { User } from '../entities/user'; import { createDonationData, createProjectData, + generateQfRoundNumber, generateRandomEtheriumAddress, generateTestAccessToken, graphqlUrl, @@ -1329,7 +1330,7 @@ function projectUserTotalDonationAmountTestCases() { name: 'test qf ', allocatedFund: 100, minimumPassportScore: 8, - slug: 'QF - 2024-09-10', + slug: 'QF - 2024-09-10 - ' + generateQfRoundNumber(), beginDate: moment('2024-09-10').add(1, 'days').toDate(), endDate: moment('2024-09-10').add(10, 'days').toDate(), }).save(); From 632dbee10e9bce43d82324b5e26ee599dbf2bc4e Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Sun, 29 Sep 2024 15:27:22 +0330 Subject: [PATCH 5/7] Added round number generator --- .../earlyAccessRoundRepository.test.ts | 15 +++++++++------ .../projectRoundRecordRepository.test.ts | 12 +++++++----- .../projectUserRecordRepository.test.ts | 5 +++-- src/resolvers/projectResolver.test.ts | 2 +- src/resolvers/roundsResolver.test.ts | 17 +++++++++++------ src/resolvers/userResolver.test.ts | 10 ++++++---- test/testUtils.ts | 4 ++++ 7 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/repositories/earlyAccessRoundRepository.test.ts b/src/repositories/earlyAccessRoundRepository.test.ts index b5804aa1d..01827a4de 100644 --- a/src/repositories/earlyAccessRoundRepository.test.ts +++ b/src/repositories/earlyAccessRoundRepository.test.ts @@ -7,7 +7,10 @@ import { findActiveEarlyAccessRound, fillMissingTokenPriceInEarlyAccessRounds, } from './earlyAccessRoundRepository'; -import { saveEARoundDirectlyToDb } from '../../test/testUtils'; +import { + generateEARoundNumber, + saveEARoundDirectlyToDb, +} from '../../test/testUtils'; import { CoingeckoPriceAdapter } from '../adapters/price/CoingeckoPriceAdapter'; import { QACC_DONATION_TOKEN_COINGECKO_ID } from '../constants/qacc'; @@ -37,7 +40,7 @@ describe('EarlyAccessRound Repository Test Cases', () => { it('should save a new Early Access Round directly to the database', async () => { const roundData = { - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }; @@ -57,12 +60,12 @@ describe('EarlyAccessRound Repository Test Cases', () => { it('should find all Early Access Rounds', async () => { // Save a couple of rounds first await saveEARoundDirectlyToDb({ - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }); await saveEARoundDirectlyToDb({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-06'), endDate: new Date('2024-09-10'), }); @@ -77,13 +80,13 @@ describe('EarlyAccessRound Repository Test Cases', () => { it('should find the active Early Access Round', async () => { const activeRoundData = { - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date(new Date().setDate(new Date().getDate() - 1)), // yesterday endDate: new Date(new Date().setDate(new Date().getDate() + 1)), // tomorrow }; const inactiveRoundData = { - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: new Date(new Date().getDate() + 1), endDate: new Date(new Date().getDate() + 2), }; diff --git a/src/repositories/projectRoundRecordRepository.test.ts b/src/repositories/projectRoundRecordRepository.test.ts index 9795484dd..3148b45d7 100644 --- a/src/repositories/projectRoundRecordRepository.test.ts +++ b/src/repositories/projectRoundRecordRepository.test.ts @@ -2,6 +2,8 @@ import { expect } from 'chai'; import { createDonationData, createProjectData, + generateEARoundNumber, + generateQfRoundNumber, saveDonationDirectlyToDb, saveProjectDirectlyToDb, SEED_DATA, @@ -52,17 +54,17 @@ describe('ProjectRoundRecord test cases', () => { const earlyAccessRounds = await EarlyAccessRound.create([ { - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date('2000-01-01'), endDate: new Date('2000-01-02'), }, { - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: new Date('2000-01-02'), endDate: new Date('2000-01-03'), }, { - roundNumber: 3, + roundNumber: generateEARoundNumber(), startDate: new Date('2000-01-03'), endDate: new Date('2000-01-04'), }, @@ -72,7 +74,7 @@ describe('ProjectRoundRecord test cases', () => { earlyAccessRounds; qfRound1 = await QfRound.create({ - roundNumber: 1, + roundNumber: generateQfRoundNumber(), isActive: true, name: new Date().toString() + ' - 1', allocatedFund: 100, @@ -82,7 +84,7 @@ describe('ProjectRoundRecord test cases', () => { endDate: new Date('2001-01-03'), }).save(); qfRound2 = await QfRound.create({ - roundNumber: 2, + roundNumber: generateQfRoundNumber(), isActive: true, name: new Date().toString() + ' - 2', allocatedFund: 100, diff --git a/src/repositories/projectUserRecordRepository.test.ts b/src/repositories/projectUserRecordRepository.test.ts index c7eedc267..2ddad08ab 100644 --- a/src/repositories/projectUserRecordRepository.test.ts +++ b/src/repositories/projectUserRecordRepository.test.ts @@ -3,6 +3,7 @@ import moment from 'moment'; import { createDonationData, createProjectData, + generateEARoundNumber, generateQfRoundNumber, generateRandomEtheriumAddress, saveDonationDirectlyToDb, @@ -132,12 +133,12 @@ describe('projectUserRecordRepository', () => { it('should return correct ea and qf donation amounts', async () => { const ea1 = await saveEARoundDirectlyToDb({ - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }); const ea2 = await saveEARoundDirectlyToDb({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-06'), endDate: new Date('2024-09-10'), }); diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts index 099e9be04..32caa7071 100644 --- a/src/resolvers/projectResolver.test.ts +++ b/src/resolvers/projectResolver.test.ts @@ -1290,7 +1290,7 @@ function getProjectRoundRecordsTestCases() { // Create Early Access Round (Assuming you have such an entity) earlyAccessRoundId = ( await EarlyAccessRound.create({ - roundNumber: 1, + roundNumber: generateQfRoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }).save() diff --git a/src/resolvers/roundsResolver.test.ts b/src/resolvers/roundsResolver.test.ts index 99bb712f9..45faf1248 100644 --- a/src/resolvers/roundsResolver.test.ts +++ b/src/resolvers/roundsResolver.test.ts @@ -2,7 +2,11 @@ import { assert } from 'chai'; import moment from 'moment'; import axios from 'axios'; import { AppDataSource } from '../orm'; -import { graphqlUrl } from '../../test/testUtils'; +import { + generateEARoundNumber, + generateQfRoundNumber, + graphqlUrl, +} from '../../test/testUtils'; import { QfRound } from '../entities/qfRound'; import { EarlyAccessRound } from '../entities/earlyAccessRound'; import { generateRandomString } from '../utils/utils'; @@ -52,13 +56,13 @@ function fetchAllRoundsTestCases() { it('should return all rounds (QF Rounds and Early Access Rounds)', async () => { // Create Early Access Rounds const earlyAccessRound1 = await EarlyAccessRound.create({ - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date(), endDate: moment().add(3, 'days').toDate(), }).save(); const earlyAccessRound2 = await EarlyAccessRound.create({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: moment().add(4, 'days').toDate(), endDate: moment().add(7, 'days').toDate(), }).save(); @@ -67,6 +71,7 @@ function fetchAllRoundsTestCases() { const qfRound1 = await QfRound.create({ name: 'QF Round 1', slug: generateRandomString(10), + roundNumber: generateQfRoundNumber(), allocatedFund: 100000, minimumPassportScore: 8, beginDate: new Date(), @@ -145,7 +150,7 @@ function fetchActiveRoundTestCases() { it('should return the currently active Early Access round and no active QF round', async () => { // Create an active Early Access Round const activeEarlyAccessRound = await EarlyAccessRound.create({ - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: moment().subtract(1, 'days').toDate(), endDate: moment().add(2, 'days').toDate(), }).save(); @@ -179,7 +184,7 @@ function fetchActiveRoundTestCases() { it('should return the currently active QF round and no active Early Access round', async () => { // Create a non-active Early Access Round await EarlyAccessRound.create({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: moment().add(10, 'days').toDate(), endDate: moment().add(20, 'days').toDate(), }).save(); @@ -210,7 +215,7 @@ function fetchActiveRoundTestCases() { it('should return null when there are no active rounds', async () => { // Create a non-active Early Access Round await EarlyAccessRound.create({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: moment().add(10, 'days').toDate(), endDate: moment().add(20, 'days').toDate(), }).save(); diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index cbdd316af..f9050b230 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -9,7 +9,7 @@ import { User } from '../entities/user'; import { createDonationData, createProjectData, - generateQfRoundNumber, + generateEARoundNumber, generateRandomEtheriumAddress, generateTestAccessToken, graphqlUrl, @@ -1315,22 +1315,24 @@ function projectUserTotalDonationAmountTestCases() { const project = await saveProjectDirectlyToDb(createProjectData()); const ea1 = await saveEARoundDirectlyToDb({ - roundNumber: 1, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }); const ea2 = await saveEARoundDirectlyToDb({ - roundNumber: 2, + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-06'), endDate: new Date('2024-09-10'), }); + const qfRoundNumber = generateEARoundNumber(); const qfRound = await QfRound.create({ isActive: true, name: 'test qf ', allocatedFund: 100, minimumPassportScore: 8, - slug: 'QF - 2024-09-10 - ' + generateQfRoundNumber(), + slug: 'QF - 2024-09-10 - ' + qfRoundNumber, + roundNumber: qfRoundNumber, beginDate: moment('2024-09-10').add(1, 'days').toDate(), endDate: moment('2024-09-10').add(10, 'days').toDate(), }).save(); diff --git a/test/testUtils.ts b/test/testUtils.ts index de7ccd2cf..4a723bfba 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -2070,3 +2070,7 @@ let nextQfRoundNumber = 1000; export function generateQfRoundNumber(): number { return nextQfRoundNumber++; } +let nextEARoundNumber = 1000; +export function generateEARoundNumber(): number { + return nextEARoundNumber++; +} From aa9a0197f826c207b57e2735d3f60a5f3d67197f Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Sun, 29 Sep 2024 15:32:38 +0330 Subject: [PATCH 6/7] Fixed an issue in importing --- src/resolvers/projectResolver.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts index 32caa7071..04dd50b75 100644 --- a/src/resolvers/projectResolver.test.ts +++ b/src/resolvers/projectResolver.test.ts @@ -4,6 +4,7 @@ import { ArgumentValidationError } from 'type-graphql'; import { createProjectData, deleteProjectDirectlyFromDb, + generateEARoundNumber, generateRandomEtheriumAddress, generateTestAccessToken, graphqlUrl, @@ -1290,7 +1291,7 @@ function getProjectRoundRecordsTestCases() { // Create Early Access Round (Assuming you have such an entity) earlyAccessRoundId = ( await EarlyAccessRound.create({ - roundNumber: generateQfRoundNumber(), + roundNumber: generateEARoundNumber(), startDate: new Date('2024-09-01'), endDate: new Date('2024-09-05'), }).save() From d3d5854ad44970476922e9c888f0880869181a8f Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Sun, 29 Sep 2024 15:56:29 +0330 Subject: [PATCH 7/7] Added project user record migration --- .../1727612450457-addProjectUserRecord.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 migration/1727612450457-addProjectUserRecord.ts diff --git a/migration/1727612450457-addProjectUserRecord.ts b/migration/1727612450457-addProjectUserRecord.ts new file mode 100644 index 000000000..beab0e076 --- /dev/null +++ b/migration/1727612450457-addProjectUserRecord.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProjectUserRecord1727612450457 implements MigrationInterface { + name = 'AddProjectUserRecord1727612450457'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "project_user_record" ("id" SERIAL NOT NULL, "totalDonationAmount" double precision NOT NULL DEFAULT '0', "eaTotalDonationAmount" double precision NOT NULL DEFAULT '0', "qfTotalDonationAmount" double precision NOT NULL DEFAULT '0', "projectId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_491352d8cb0de1670d85f622f30" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_29abdbcc3e6e7090cbc8fb1a90" ON "project_user_record" ("projectId", "userId") `, + ); + await queryRunner.query( + `ALTER TABLE "project_user_record" ADD CONSTRAINT "FK_6481d6181bd857725e903b0f330" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "project_user_record" ADD CONSTRAINT "FK_47c452701e3e8553fb01a904256" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "project_user_record" DROP CONSTRAINT "FK_47c452701e3e8553fb01a904256"`, + ); + await queryRunner.query( + `ALTER TABLE "project_user_record" DROP CONSTRAINT "FK_6481d6181bd857725e903b0f330"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_29abdbcc3e6e7090cbc8fb1a90"`, + ); + await queryRunner.query(`DROP TABLE "project_user_record"`); + } +}