Skip to content

Commit

Permalink
Merge pull request #71 from GeneralMagicio/feat/contribution-cap
Browse files Browse the repository at this point in the history
Record user contribution
  • Loading branch information
aminlatifi authored Sep 29, 2024
2 parents 9258509 + d3d5854 commit adc0894
Show file tree
Hide file tree
Showing 15 changed files with 569 additions and 41 deletions.
33 changes: 33 additions & 0 deletions migration/1727612450457-addProjectUserRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddProjectUserRecord1727612450457 implements MigrationInterface {
name = 'AddProjectUserRecord1727612450457';

public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
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"`);
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/entities/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand Down Expand Up @@ -80,5 +81,6 @@ export const getEntities = (): DataSourceOptions['entities'] => {
UserQfRoundModelScore,
EarlyAccessRound,
ProjectRoundRecord,
ProjectUserRecord,
];
};
54 changes: 54 additions & 0 deletions src/entities/projectUserRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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;

// 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;

@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;
}
25 changes: 14 additions & 11 deletions src/repositories/earlyAccessRoundRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
findActiveEarlyAccessRound,
fillMissingTokenPriceInEarlyAccessRounds,
} from './earlyAccessRoundRepository';
import { saveRoundDirectlyToDb } from '../../test/testUtils';
import {
generateEARoundNumber,
saveEARoundDirectlyToDb,
} from '../../test/testUtils';
import { CoingeckoPriceAdapter } from '../adapters/price/CoingeckoPriceAdapter';
import { QACC_DONATION_TOKEN_COINGECKO_ID } from '../constants/qacc';

Expand Down Expand Up @@ -37,12 +40,12 @@ 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'),
};

const savedRound = await saveRoundDirectlyToDb(roundData);
const savedRound = await saveEARoundDirectlyToDb(roundData);

expect(savedRound).to.be.an.instanceof(EarlyAccessRound);
expect(savedRound.roundNumber).to.equal(roundData.roundNumber);
Expand All @@ -56,13 +59,13 @@ describe('EarlyAccessRound Repository Test Cases', () => {

it('should find all Early Access Rounds', async () => {
// Save a couple of rounds first
await saveRoundDirectlyToDb({
roundNumber: 1,
await saveEARoundDirectlyToDb({
roundNumber: generateEARoundNumber(),
startDate: new Date('2024-09-01'),
endDate: new Date('2024-09-05'),
});
await saveRoundDirectlyToDb({
roundNumber: 2,
await saveEARoundDirectlyToDb({
roundNumber: generateEARoundNumber(),
startDate: new Date('2024-09-06'),
endDate: new Date('2024-09-10'),
});
Expand All @@ -77,20 +80,20 @@ 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),
};

// Save both active and inactive rounds
await saveRoundDirectlyToDb(activeRoundData);
await saveRoundDirectlyToDb(inactiveRoundData);
await saveEARoundDirectlyToDb(activeRoundData);
await saveEARoundDirectlyToDb(inactiveRoundData);

const activeRound = await findActiveEarlyAccessRound();

Expand Down
44 changes: 24 additions & 20 deletions src/repositories/projectRoundRecordRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { expect } from 'chai';
import {
createDonationData,
createProjectData,
generateEARoundNumber,
generateQfRoundNumber,
saveDonationDirectlyToDb,
saveProjectDirectlyToDb,
SEED_DATA,
Expand All @@ -21,25 +23,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,
Expand All @@ -58,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'),
},
Expand All @@ -78,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,
Expand All @@ -88,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,
Expand All @@ -112,7 +108,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);

Expand Down
Loading

0 comments on commit adc0894

Please sign in to comment.