Skip to content

Commit

Permalink
Moved db operations to use cases and added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Estevam Furtado authored and Estevam Furtado committed Oct 14, 2024
1 parent 37151eb commit 8fb26e4
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 102 deletions.
66 changes: 9 additions & 57 deletions pages/api/ranking/degrees/[degree]/[class]/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import runRequestWithDIContainer from "../../../../../../middlewares/diContainer
import { PrismaClient } from "@prisma/client";
import { DIContainerNextApiRequest } from "../../../../../../dependency_injection/DIContainerNextApiRequest";
import { RANKING_INITIAL_DATA } from "../../../contants";
import { GetClassData } from "../../../types";
import { getDegreeClassData } from "../../../../../../use_cases/getDegreeClassData";

export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "GET") {
Expand All @@ -17,10 +17,13 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
async function run(req: DIContainerNextApiRequest, res: NextApiResponse) {
const prismaClient: PrismaClient = req.scope.resolve("dbClient");
try {
const rankingData = await getData(
prismaClient,
req.query.degree as string,
req.query.class as string
const rankingData = await getDegreeClassData(

Check failure on line 20 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Delete `⏎······`
{
dbClient: prismaClient,

Check failure on line 22 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Delete `··`
degree: req.query.degree as string,

Check failure on line 23 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Delete `··`
year: req.query.class as string,

Check failure on line 24 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Replace `········` with `······`
initialDate: RANKING_INITIAL_DATA

Check failure on line 25 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Replace `········initialDate:·RANKING_INITIAL_DATA` with `······initialDate:·RANKING_INITIAL_DATA,`
}

Check failure on line 26 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Replace `······}⏎····` with `····}`
);
res.statusCode = 200;
res.json(rankingData);
Expand All @@ -29,55 +32,4 @@ async function run(req: DIContainerNextApiRequest, res: NextApiResponse) {
res.statusCode = 500;
return;
}
}

async function getData(
prisma: PrismaClient,
degree: string,
year: string
): Promise<GetClassData> {
const minYear = Math.floor(Number(year) / 5) * 5;
const maxYear = minYear + 5;

const result: {
id: number;
firstName: string;
lastName: number;
url: string;
amount: number;
year: number;
}[] = await prisma.$queryRaw`
SELECT
u."id",
u."first_name" AS "firstName",
u."last_name" AS "lastName",
u."url",
SUM(c."amount_in_cents")/100 AS "amount",
u."admission_year" AS "year"
FROM "users" u
LEFT JOIN "contributions" c ON u."id" = c."userId"
WHERE
u."degree" = ${degree}
AND u."admission_year" >= ${minYear}
AND u."admission_year" < ${maxYear}
AND c."state" = 'completed'
AND c."createdAt" > ${RANKING_INITIAL_DATA}
GROUP BY u."id", u."first_name", u."last_name", u."url", u."admission_year"
`;

const amount = result.reduce((acc, row) => acc + row.amount, 0);
const numberOfDonors = result.length;
const donors: GetClassData["donors"] = result.map((row) => {
return {
name: `${row.firstName} ${row.lastName}`,
url: row.url,
year: row.year,
};
});

return {
amount,
numberOfDonors,
donors,
};
}
}

Check failure on line 35 in pages/api/ranking/degrees/[degree]/[class]/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Insert `⏎`
51 changes: 6 additions & 45 deletions pages/api/ranking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import runRequestWithDIContainer from "../../../middlewares/diContainerMiddlewar
import { PrismaClient } from "@prisma/client";
import { DIContainerNextApiRequest } from "../../../dependency_injection/DIContainerNextApiRequest";
import { RANKING_INITIAL_DATA } from "./contants";
import { GetRankingData } from "./types";
import { getRankingList } from "../../../use_cases/getRankingList";

export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "GET") {
Expand All @@ -17,54 +17,15 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
async function run(req: DIContainerNextApiRequest, res: NextApiResponse) {
const prismaClient: PrismaClient = req.scope.resolve("dbClient");
try {
console.log(req.query);
const rankingData = await getRankingData(prismaClient);
const rankingData = await getRankingList({
dbClient: prismaClient,
initialDate: RANKING_INITIAL_DATA

Check failure on line 22 in pages/api/ranking/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Insert `,`
});
res.statusCode = 200;
res.json(rankingData);
} catch (error) {
console.log(error);
res.statusCode = 500;
return;
}
}

async function getRankingData(prisma: PrismaClient): Promise<GetRankingData> {
const result: {
period: number;
degree: string;
amount: number;
donors: number;
}[] = await prisma.$queryRaw`
SELECT
FLOOR((u."admission_year") / 5) * 5 AS "period",
u."degree",
SUM(c."amount_in_cents")/100 AS "amount",
COUNT(DISTINCT c."userId") AS "donors"
FROM "users" u
JOIN "contributions" c ON u."id" = c."userId"
WHERE
c."state" = 'completed'
AND c."createdAt" > ${RANKING_INITIAL_DATA}
GROUP BY "period", u."degree"
ORDER BY "amount" DESC;
`;

const amount = result.reduce((acc, row) => acc + row.amount, 0);
const numberOfDonors = result.reduce((acc, row) => acc + row.donors, 0);
const ranking: GetRankingData["ranking"] = result.map((row, index) => {
return {
position: index + 1,
degree: row.degree,
initialYear: row.period,
finalYear: row.period + 4,
amount: row.amount,
numberOfDonors: row.donors,
};
});

return {
amount,
numberOfDonors,
ranking,
};
}
}

Check failure on line 31 in pages/api/ranking/index.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Insert `⏎`
129 changes: 129 additions & 0 deletions use_cases/getDegreeClassData.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { PrismaClient } from "@prisma/client";
import { getDegreeClassData } from "./getDegreeClassData";
import createContribution from "./createContribution";
import completeContribution from "./completeContribution";
import createUser from "./createUser";

let prisma: PrismaClient;

beforeAll(async () => {
prisma = new PrismaClient();

await prisma.contribution.deleteMany();
await prisma.user.deleteMany();

const user1 = await createUser({
dbClient: prisma,
email: "[email protected]",
firstName: "John",
lastName: "Doe",
university: "UFRJ",
degree: "Industrial Engineering",
admissionYear: 2020,
url: "http://example.com/john",
birthday: new Date("1990-01-01"),
tutorshipInterest: false,
mentorshipInterest: false,
volunteeringInterest: false

Check failure on line 27 in use_cases/getDegreeClassData.test.ts

View workflow job for this annotation

GitHub Actions / run-all-checks

Insert `,`
});

const contribution1 = await createContribution({
dbClient: prisma,
amountInCents: 50_00,
email: user1.email,
});

await completeContribution({
dbClient: prisma,
contributionId: contribution1.id,
externalId: "123456",
});

const user2 = await createUser({
dbClient: prisma,
email: "[email protected]",
firstName: "Jane",
lastName: "Smith",
university: "UFRJ",
degree: "Industrial Engineering",
admissionYear: 2021,
url: "http://example.com/jane",
birthday: new Date("1990-01-01"),
tutorshipInterest: false,
mentorshipInterest: false,
volunteeringInterest: false
});

const contribution2 = await createContribution({
dbClient: prisma,
amountInCents: 30_00,
email: user2.email,
});

await completeContribution({
dbClient: prisma,
contributionId: contribution2.id,
externalId: "123456",
});

const user3 = await createUser({
dbClient: prisma,
email: "[email protected]",
firstName: "Steve",
lastName: "Jobs",
university: "UFRJ",
degree: "Industrial Engineering",
admissionYear: 2015,
url: "http://example.com/steve",
birthday: new Date("1955-02-24"),
tutorshipInterest: false,
mentorshipInterest: false,
volunteeringInterest: false
});


const contribution3 = await createContribution({
dbClient: prisma,
amountInCents: 20_00,
email: user3.email,
});

await completeContribution({
dbClient: prisma,
contributionId: contribution3.id,
externalId: "123456",
});
});

afterAll(async () => {
await prisma.$disconnect();
});

describe("getDegreeClassData", () => {
it("should return the correct data for a given degree and year (5y window)", async () => {
const initialDate = new Date("2023-01-01");
const result = await getDegreeClassData({
dbClient: prisma,
year: "2020",
degree: "Industrial Engineering",
initialDate,
});

expect(result).toEqual({
amount: 80,
numberOfDonors: 2,
donors: [
{
name: "John Doe",
year: 2020,
url: "http://example.com/john",
},
{
name: "Jane Smith",
year: 2021,
url: "http://example.com/jane",
},
],
});
});
});
59 changes: 59 additions & 0 deletions use_cases/getDegreeClassData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { PrismaClient } from "@prisma/client";
import { GetClassData } from "../pages/api/ranking/types";

type GetDegreeClassDataArgs = {
dbClient: PrismaClient;
degree: string;
year: string;
initialDate: Date;
}

export async function getDegreeClassData(
args: GetDegreeClassDataArgs
): Promise<GetClassData> {
const minYear = Math.floor(Number(args.year) / 5) * 5;
const maxYear = minYear + 5;

const result: {
id: number;
firstName: string;
lastName: number;
url: string;
amount: number;
year: number;
}[] = await args.dbClient.$queryRaw`
SELECT
u."id",
u."first_name" AS "firstName",
u."last_name" AS "lastName",
u."url",
SUM(c."amount_in_cents")/100 AS "amount",
u."admission_year" AS "year"
FROM "users" u
LEFT JOIN "contributions" c ON u."id" = c."userId"
WHERE
u."degree" = ${args.degree}
AND u."admission_year" >= ${minYear}
AND u."admission_year" < ${maxYear}
AND c."state" = 'completed'
AND c."createdAt" > ${args.initialDate}
GROUP BY u."id", u."first_name", u."last_name", u."url", u."admission_year"
`;

const amount = result.reduce((acc, row) => acc + row.amount, 0);
const numberOfDonors = result.length;
const donors: GetClassData["donors"] = result.map((row) => {
return {
name: `${row.firstName} ${row.lastName}`,
url: row.url,
year: row.year,
};
});

return {
amount,
numberOfDonors,
donors,
};
}

Loading

0 comments on commit 8fb26e4

Please sign in to comment.