Skip to content

Commit

Permalink
apply review changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ae2079 committed Nov 26, 2024
1 parent 3470966 commit 1c38188
Show file tree
Hide file tree
Showing 10 changed files with 37 additions and 287 deletions.
4 changes: 2 additions & 2 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,6 @@ MONGO_DB_REPORT_DB_NAME=
# Gitcoin score
GITCOIN_PASSPORT_MIN_VALID_SCORE=
# 1 day
GITCOINT_PASSPORT_EXPIRATION_PERIOD_MS=86400000
MAX_CONTRIBUTION_WITH_GITCOING_PASSPORT_ONLY=
GITCOIN_PASSPORT_EXPIRATION_PERIOD_MS=86400000
MAX_CONTRIBUTION_WITH_GITCOIN_PASSPORT_ONLY=
ACTIVATE_GITCOIN_SCORE_CHECK=
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddScoreTimestampToUser1732495872789
export class AddScoreTimestampToUser1732582914845
implements MigrationInterface
{
name = 'AddScoreTimestampToUser1732495872789';
name = 'AddScoreTimestampToUser1732582914845';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user" ADD "passportScoreUpdateTimestamp" character varying`,
`ALTER TABLE "user" ADD "passportScoreUpdateTimestamp" TIMESTAMP WITH TIME ZONE`,
);
}

Expand Down
7 changes: 7 additions & 0 deletions src/constants/qacc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ export const QACC_DONATION_TOKEN_COINGECKO_ID =
(config.get('QACC_DONATION_TOKEN_COINGECKO_ID') as string) || 'matic-network';
export const QACC_PRICE_FETCH_LEAD_TIME_IN_SECONDS =
(+config.get('QACC_PRICE_FETCH_LEAD_TIME_IN_SECONDS') as number) || 300; // 5 minutes
export const GITCOIN_PASSPORT_EXPIRATION_PERIOD_MS =
(+config.get('GITCOIN_PASSPORT_EXPIRATION_PERIOD_MS') as number) || 86400000; // 1 day
export const GITCOIN_PASSPORT_MIN_VALID_SCORE =
(+config.get('GITCOIN_PASSPORT_MIN_VALID_SCORE') as number) || 50;
export const MAX_CONTRIBUTION_WITH_GITCOIN_PASSPORT_ONLY =
(+config.get('MAX_CONTRIBUTION_WITH_GITCOIN_PASSPORT_ONLY') as number) ||
1000;
16 changes: 13 additions & 3 deletions src/entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ProjectVerificationForm } from './projectVerificationForm';
import { ReferredEvent } from './referredEvent';
import { NOTIFICATIONS_EVENT_NAMES } from '../analytics/analytics';
import { PrivadoAdapter } from '../adapters/privado/privadoAdapter';
import config from '../config';

export const publicSelectionFields = [
'user.id',
Expand Down Expand Up @@ -117,9 +118,9 @@ export class User extends BaseEntity {
@Column({ type: 'real', nullable: true, default: null })
passportScore?: number;

@Field(_type => String, { nullable: true })
@Column({ nullable: true })
passportScoreUpdateTimestamp?: string;
@Field(_type => Date, { nullable: true })
@Column({ type: 'timestamptz', nullable: true })
passportScoreUpdateTimestamp?: Date;

@Field(_type => Number, { nullable: true })
@Column({ nullable: true, default: null })
Expand Down Expand Up @@ -232,6 +233,15 @@ export class User extends BaseEntity {
);
}

@Field(_type => Boolean, { nullable: true })
get hasEnoughPassportScore(): boolean {
return !!(
this.passportScore &&
this.passportScore >=
Number(config.get('GITCOIN_PASSPORT_MIN_VALID_SCORE'))
);
}

@Field(_type => Int, { nullable: true })
async donationsCount() {
return await Donation.createQueryBuilder('donation')
Expand Down
188 changes: 0 additions & 188 deletions src/resolvers/donationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import {
fetchNewDonorsCount,
fetchNewDonorsDonationTotalUsd,
fetchDonationMetricsQuery,
validateDonationQuery,
} from '../../test/graphqlQueries';
import { NETWORK_IDS, QACC_NETWORK_ID } from '../provider';
import { User } from '../entities/user';
Expand Down Expand Up @@ -77,7 +76,6 @@ describe('donationsByProjectId() test cases', donationsByProjectIdTestCases);
describe('donationByUserId() test cases', donationsByUserIdTestCases);
describe('donationsByDonor() test cases', donationsByDonorTestCases);
describe('createDonation() test cases', createDonationTestCases);
describe('validateDonation() test cases', validateDonationTestCases);
// describe('updateDonationStatus() test cases', updateDonationStatusTestCases);
describe('donationsToWallets() test cases', donationsToWalletsTestCases);
describe('donationsFromWallets() test cases', donationsFromWalletsTestCases);
Expand Down Expand Up @@ -2723,192 +2721,6 @@ function createDonationTestCases() {
});
}

function validateDonationTestCases() {
let project;
let user;

before(async () => {
// Set up a project and user before each test case
project = await saveProjectDirectlyToDb(createProjectData());
user = await User.create({
walletAddress: generateRandomEtheriumAddress(),
loginType: 'wallet',
firstName: 'Test User',
}).save();
});

beforeEach(async () => {
sinon.stub(qAccService, 'getQAccDonationCap').resolves(10000);
});

afterEach(async () => {
sinon.restore();
});

it('should return true if donation is valid', async () => {
// Mocking valid conditions for donation
const amount = 100;
const token = QACC_DONATION_TOKEN_SYMBOL;
const transactionNetworkId = QACC_NETWORK_ID;
const projectId = project.id;

const accessToken = await generateTestAccessToken(user.id);

const validateDonationResponse = await axios.post(
graphqlUrl,
{
query: validateDonationQuery,
variables: {
amount,
token,
transactionNetworkId,
projectId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

assert.isTrue(validateDonationResponse.data.data.validateDonation);
});

it('should return false if donation amount exceeds project cap', async () => {
// Test case for exceeding donation cap
const amount = 1000000; // Large donation amount
const token = QACC_DONATION_TOKEN_SYMBOL;
const transactionNetworkId = QACC_NETWORK_ID;
const projectId = project.id;

const accessToken = await generateTestAccessToken(user.id);

const validateDonationResponse = await axios.post(
graphqlUrl,
{
query: validateDonationQuery,
variables: {
amount,
token,
transactionNetworkId,
projectId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

assert.isFalse(validateDonationResponse.data.data.validateDonation);
});

it('should return false if user is unauthorized', async () => {
// Test case for unauthorized user
const amount = 100;
const token = QACC_DONATION_TOKEN_SYMBOL;
const transactionNetworkId = QACC_NETWORK_ID;
const projectId = project.id;

const accessToken = 'InvalidAccessToken'; // Invalid token

try {
await axios.post(
graphqlUrl,
{
query: validateDonationQuery,
variables: {
amount,
token,
transactionNetworkId,
projectId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
} catch (e) {
assert.include(e.response.data.errors[0].message, 'Unauthorized');
}
});

it('should throw error if donation is invalid due to network mismatch', async () => {
// Test case for invalid network ID
const amount = 100;
const token = QACC_DONATION_TOKEN_SYMBOL;
const transactionNetworkId = 999; // Invalid network ID
const projectId = project.id;

const accessToken = await generateTestAccessToken(user.id);

const validateDonationResponse = await axios.post(
graphqlUrl,
{
query: validateDonationQuery,
variables: {
amount,
token,
transactionNetworkId,
projectId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

assert.isTrue(
(validateDonationResponse.data.errors[0].message as string).startsWith(
'Invalid tokenAddress',
),
);
});

it('should throw error if project is not active', async () => {
// Test case for project not being active
const amount = 100;
const token = QACC_DONATION_TOKEN_SYMBOL;
const transactionNetworkId = QACC_NETWORK_ID;
const projectId = project.id;

project.status.id = ProjStatus.deactive; // Setting project status to deactive
await project.save();

const accessToken = await generateTestAccessToken(user.id);

const validateDonationResponse = await axios.post(
graphqlUrl,
{
query: validateDonationQuery,
variables: {
amount,
token,
transactionNetworkId,
projectId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);

assert.isTrue(
(validateDonationResponse.data.errors[0].message as string).startsWith(
'Just active projects accept donation',
),
);
});
}

function donationsFromWalletsTestCases() {
it('should find donations with special source successfully', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
Expand Down
67 changes: 0 additions & 67 deletions src/resolvers/donationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,73 +678,6 @@ export class DonationResolver {
};
}

@Query(_returns => Boolean)
async validateDonation(
@Arg('amount') amount: number,
@Arg('token') token: string,
@Arg('transactionNetworkId') transactionNetworkId: number,
@Arg('projectId') projectId: number,
@Ctx() ctx: ApolloContext,
): Promise<boolean> {
const logData = {
amount,
transactionNetworkId,
token,
projectId,
userId: ctx?.req?.user?.userId,
};
logger.debug(
'validateDonation() resolver has been called with this data',
logData,
);
try {
const userId = ctx?.req?.user?.userId;
if (!userId) {
throw new Error(i18n.__(translationErrorMessagesKeys.UN_AUTHORIZED));
}
// Fetch user data
const donorUser = await findUserById(userId);
if (!donorUser) {
throw new Error(i18n.__(translationErrorMessagesKeys.UN_AUTHORIZED));
}
const chainType = detectAddressChainType(donorUser.walletAddress!);
const networkId = getAppropriateNetworkId({
networkId: transactionNetworkId,
chainType,
});
const project = await findProjectById(projectId);

if (!project)
throw new Error(
i18n.__(translationErrorMessagesKeys.PROJECT_NOT_FOUND),
);
if (project.status.id !== ProjStatus.active) {
throw new Error(
i18n.__(
translationErrorMessagesKeys.JUST_ACTIVE_PROJECTS_ACCEPT_DONATION,
),
);
}
const donateTime = new Date();

return await qacc.validateDonation({
projectId,
networkId,
tokenSymbol: token,
userAddress: donorUser.walletAddress!,
amount,
donateTime,
});
} catch (e) {
SentryLogger.captureException(e);
logger.error('validateDonation() error', {
error: e,
inputData: logData,
});
throw e;
}
}

@Mutation(_returns => Number)
async createDonation(
@Arg('amount') amount: number,
Expand Down
2 changes: 1 addition & 1 deletion src/resolvers/userResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class UserResolver {
if (passportScore && passportScore?.score) {
const score = Number(passportScore.score);
foundUser.passportScore = score;
foundUser.passportScoreUpdateTimestamp = Date.now().toString();
foundUser.passportScoreUpdateTimestamp = new Date();
}
if (passportStamps)
foundUser.passportStamps = passportStamps.items.length;
Expand Down
Loading

0 comments on commit 1c38188

Please sign in to comment.