Skip to content

Commit

Permalink
Merge pull request #37 from GeneralMagicio/addEarlyAccessRound
Browse files Browse the repository at this point in the history
Add early access rounds
  • Loading branch information
ae2079 authored Aug 28, 2024
2 parents 84581b8 + c3eebbc commit 1d7bbe4
Show file tree
Hide file tree
Showing 16 changed files with 413 additions and 24 deletions.
1 change: 0 additions & 1 deletion config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -278,5 +278,4 @@ ABC_LAUNCH_DATA_SOURCE=

QACC_NETWORK_ID=
QACC_DONATION_TOKEN_ADDRESS=
QACC_EARLY_ACCESS_ROUND_FINISH_TIMESTAMP=
ABC_LAUNCHER_ADAPTER=
41 changes: 41 additions & 0 deletions migration/1724799772891-addEarlyAccessRoundTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

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

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "donation" RENAME COLUMN "earlyAccessRound" TO "earlyAccessRoundId"`,
);
await queryRunner.query(
`CREATE TABLE "early_access_round" ("id" SERIAL NOT NULL, "roundNumber" integer NOT NULL, "startDate" TIMESTAMP NOT NULL, "endDate" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_e2f9598b0bbed3f05ca5c49fedc" UNIQUE ("roundNumber"), CONSTRAINT "PK_b128520615d2666c576399b07d3" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "donation" DROP COLUMN "earlyAccessRoundId"`,
);
await queryRunner.query(
`ALTER TABLE "donation" ADD "earlyAccessRoundId" integer`,
);
await queryRunner.query(
`ALTER TABLE "donation" ADD CONSTRAINT "FK_635e96839361920b7f80da1dd51" FOREIGN KEY ("earlyAccessRoundId") REFERENCES "early_access_round"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "donation" DROP CONSTRAINT "FK_635e96839361920b7f80da1dd51"`,
);
await queryRunner.query(
`ALTER TABLE "donation" DROP COLUMN "earlyAccessRoundId"`,
);
await queryRunner.query(
`ALTER TABLE "donation" ADD "earlyAccessRoundId" boolean DEFAULT false`,
);
await queryRunner.query(`DROP TABLE "early_access_round"`);
await queryRunner.query(
`ALTER TABLE "donation" RENAME COLUMN "earlyAccessRoundId" TO "earlyAccessRound"`,
);
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@
"test:projectResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/projectResolver.test.ts ./src/resolvers/projectResolver.allProject.test.ts",
"test:chainvineResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/chainvineResolver.test.ts",
"test:qfRoundResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/qfRoundResolver.test.ts",
"test:earlyAccessRoundRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/earlyAccessRoundRepository.test.ts",
"test:earlyAccessRoundResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/earlyAccessRoundResolver.test.ts",
"test:qfRoundHistoryResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/qfRoundHistoryResolver.test.ts",
"test:projectVerificationResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/projectVerificationFormResolver.test.ts",
"test:projectRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectRepository.test.ts",
Expand Down
11 changes: 8 additions & 3 deletions src/entities/donation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Project } from './project';
import { User } from './user';
import { QfRound } from './qfRound';
import { ChainType } from '../types/network';
import { EarlyAccessRound } from './earlyAccessRound';

export const DONATION_STATUS = {
PENDING: 'pending',
Expand Down Expand Up @@ -257,9 +258,13 @@ export class Donation extends BaseEntity {
@Column('decimal', { precision: 5, scale: 2, nullable: true })
donationPercentage?: number;

@Field(_type => Boolean, { nullable: false })
@Column({ nullable: true, default: false })
earlyAccessRound: boolean;
@Field(_type => EarlyAccessRound, { nullable: true })
@ManyToOne(_type => EarlyAccessRound, { eager: true, nullable: true })
earlyAccessRound: EarlyAccessRound | null;

@RelationId((donation: Donation) => donation.earlyAccessRound)
@Column({ nullable: true })
earlyAccessRoundId: number;

static async findXdaiGivDonationsWithoutPrice() {
return this.createQueryBuilder('donation')
Expand Down
37 changes: 37 additions & 0 deletions src/entities/earlyAccessRound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
BaseEntity,
Column,
Entity,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { Field, ID, ObjectType, Int } from 'type-graphql';

@Entity()
@ObjectType()
export class EarlyAccessRound extends BaseEntity {
@Field(_type => ID)
@PrimaryGeneratedColumn()
id: number;

@Field(() => Int)
@Column({ unique: true })
roundNumber: number;

@Field(() => Date)
@Column()
startDate: Date;

@Field(() => Date)
@Column()
endDate: Date;

@Field(() => Date)
@CreateDateColumn()
createdAt: Date;

@Field(() => Date)
@UpdateDateColumn()
updatedAt: Date;
}
2 changes: 2 additions & 0 deletions src/entities/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { ProjectActualMatchingView } from './ProjectActualMatchingView';
import { ProjectSocialMedia } from './projectSocialMedia';
import { UserQfRoundModelScore } from './userQfRoundModelScore';
import { UserEmailVerification } from './userEmailVerification';
import { EarlyAccessRound } from './earlyAccessRound';

export const getEntities = (): DataSourceOptions['entities'] => {
return [
Expand Down Expand Up @@ -76,5 +77,6 @@ export const getEntities = (): DataSourceOptions['entities'] => {
Sybil,
ProjectFraud,
UserQfRoundModelScore,
EarlyAccessRound,
];
};
93 changes: 93 additions & 0 deletions src/repositories/earlyAccessRoundRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { expect } from 'chai';
import { EarlyAccessRound } from '../entities/earlyAccessRound';
import {
findAllEarlyAccessRounds,
findActiveEarlyAccessRound,
} from './earlyAccessRoundRepository';
import { saveRoundDirectlyToDb } from '../../test/testUtils';

describe('EarlyAccessRound Repository Test Cases', () => {
beforeEach(async () => {
// Clean up data before each test case
await EarlyAccessRound.delete({});
});

afterEach(async () => {
// Clean up data after each test case
await EarlyAccessRound.delete({});
});

it('should save a new Early Access Round directly to the database', async () => {
const roundData = {
roundNumber: 1,
startDate: new Date('2024-09-01'),
endDate: new Date('2024-09-05'),
};

const savedRound = await saveRoundDirectlyToDb(roundData);

expect(savedRound).to.be.an.instanceof(EarlyAccessRound);
expect(savedRound.roundNumber).to.equal(roundData.roundNumber);
expect(savedRound.startDate.toISOString()).to.equal(
roundData.startDate.toISOString(),
);
expect(savedRound.endDate.toISOString()).to.equal(
roundData.endDate.toISOString(),
);
});

it('should find all Early Access Rounds', async () => {
// Save a couple of rounds first
await saveRoundDirectlyToDb({
roundNumber: 1,
startDate: new Date('2024-09-01'),
endDate: new Date('2024-09-05'),
});
await saveRoundDirectlyToDb({
roundNumber: 2,
startDate: new Date('2024-09-06'),
endDate: new Date('2024-09-10'),
});

const rounds = await findAllEarlyAccessRounds();

expect(rounds).to.be.an('array');
expect(rounds.length).to.equal(2);
expect(rounds[0]).to.be.an.instanceof(EarlyAccessRound);
expect(rounds[1]).to.be.an.instanceof(EarlyAccessRound);
});

it('should find the active Early Access Round', async () => {
const activeRoundData = {
roundNumber: 1,
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,
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);

const activeRound = await findActiveEarlyAccessRound();

expect(activeRound).to.be.an.instanceof(EarlyAccessRound);
expect(activeRound?.roundNumber).to.equal(activeRoundData.roundNumber);
expect(activeRound?.startDate.toISOString()).to.equal(
activeRoundData.startDate.toISOString(),
);
expect(activeRound?.endDate.toISOString()).to.equal(
activeRoundData.endDate.toISOString(),
);
});

it('should return null when no active Early Access Round is found', async () => {
const activeRound = await findActiveEarlyAccessRound();
expect(activeRound).to.be.null;
});
});
32 changes: 32 additions & 0 deletions src/repositories/earlyAccessRoundRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { EarlyAccessRound } from '../entities/earlyAccessRound';
import { logger } from '../utils/logger';

export const findAllEarlyAccessRounds = async (): Promise<
EarlyAccessRound[]
> => {
try {
return EarlyAccessRound.createQueryBuilder('earlyAccessRound')
.orderBy('earlyAccessRound.startDate', 'ASC')
.getMany();
} catch (error) {
logger.error('Error fetching all Early Access rounds', { error });
throw new Error('Error fetching Early Access rounds');
}
};

// Find the currently active Early Access Round
export const findActiveEarlyAccessRound =
async (): Promise<EarlyAccessRound | null> => {
const currentDate = new Date();

try {
const query = EarlyAccessRound.createQueryBuilder('earlyAccessRound')
.where('earlyAccessRound.startDate <= :currentDate', { currentDate })
.andWhere('earlyAccessRound.endDate >= :currentDate', { currentDate });

return query.getOne();
} catch (error) {
logger.error('Error fetching active Early Access round', { error });
throw new Error('Error fetching active Early Access round');
}
};
2 changes: 1 addition & 1 deletion src/resolvers/donationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ function createDonationTestCases() {
assert.equal(donation?.referrerWallet, user2.walletAddress);
assert.isOk(donation?.referralStartTimestamp);
assert.isNotOk(donation?.qfRound);
assert.isTrue(donation?.earlyAccessRound);
// assert.isTrue(donation?.earlyAccessRound);
});
it('should create a donation in an active qfRound', async () => {
sinon.stub(qacc, 'isEarlyAccessRound').returns(false);
Expand Down
5 changes: 3 additions & 2 deletions src/resolvers/donationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
DraftDonation,
} from '../entities/draftDonation';
import qacc from '../utils/qacc';
import { findActiveEarlyAccessRound } from '../repositories/earlyAccessRoundRepository';

const draftDonationEnabled = process.env.ENABLE_DRAFT_DONATION === 'true';
@ObjectType()
Expand Down Expand Up @@ -875,7 +876,7 @@ export class DonationResolver {
logger.error('get chainvine wallet address error', e);
}
}
if (!qacc.isEarlyAccessRound()) {
if (!(await qacc.isEarlyAccessRound())) {
const activeQfRoundForProject =
await relatedActiveQfRoundForProject(projectId);
if (
Expand Down Expand Up @@ -903,7 +904,7 @@ export class DonationResolver {
}
await donation.save();
} else {
donation.earlyAccessRound = true;
donation.earlyAccessRound = await findActiveEarlyAccessRound();
await donation.save();
}
let priceChainId;
Expand Down
Loading

0 comments on commit 1d7bbe4

Please sign in to comment.