Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More API changes #21

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { VercelApiHandler, VercelRequest, VercelResponse } from "@vercel/node";
import dotenv from "dotenv";
dotenv.config();

// TODO: middlware will need to extend JWTs/ session Requests for any route not on the auth endpoint

// Cors wrapper used on each function
const allowCors =
(handler: VercelApiHandler) =>
Expand All @@ -24,6 +26,10 @@ const allowCors =
});
}

// JWT guard -> check on any incoming request coming in whether they have a valid JWT that can be parsed
// only if this is production in the future
//

return await handler(req, res);
};

Expand Down
7 changes: 2 additions & 5 deletions api/voting/createVote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import allowCors from "../middleware";

const voteHistoryController = new VotingController();

const postAttendanceChange = async (
req: VercelRequest,
res: VercelResponse
) => {
const postVote = async (req: VercelRequest, res: VercelResponse) => {
try {
const parsed = VotingHistorySchema.parse(req.body);
const vote = await voteHistoryController.createVote(parsed);
Expand All @@ -21,4 +18,4 @@ const postAttendanceChange = async (
}
};

export default allowCors(postAttendanceChange);
export default allowCors(postVote);
16 changes: 0 additions & 16 deletions api/voting/getAllQuestions.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import allowCors from "../middleware";
import { VercelRequest, VercelResponse } from "@vercel/node";
import { VotingController } from "../../src/controllers/votingController";
import { ZodError } from "zod";
import { VoteHistoryQuerySchema } from "../../src/types/types";
import allowCors from "../middleware";

const votingController = new VotingController();

const getVotingForMember = async (req: VercelRequest, res: VercelResponse) => {
const getIfMemberVoted = async (req: VercelRequest, res: VercelResponse) => {
try {
const result = VoteHistoryQuerySchema.parse(req.query);
const potentialVote = await votingController.getVotingHistory(result);
res.status(200).json(potentialVote);
const potentialQuestion = await votingController.determineQuestion(
req.query.id as string
);
res.status(200).json(potentialQuestion);
} catch (error: unknown) {
error instanceof ZodError
? res.status(400).send("Invalid Input")
: res.status(500).send("Database error");
}
};

export default allowCors(getVotingForMember);
export default allowCors(getIfMemberVoted);
21 changes: 21 additions & 0 deletions api/voting/getPastVotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { VercelRequest, VercelResponse } from "@vercel/node";
import { VotingController } from "../../src/controllers/votingController";
import { ZodError } from "zod";
import allowCors from "../middleware";

const votingController = new VotingController();

const getPastVotes = async (req: VercelRequest, res: VercelResponse) => {
try {
const pastVotes = await votingController.getMembersPastVotes(
req.query.id as string
);
res.status(200).json(pastVotes);
} catch (error: unknown) {
error instanceof ZodError
? res.status(400).send("Invalid Input")
: res.status(500).send("Database error");
}
};

export default allowCors(getPastVotes);
1 change: 1 addition & 0 deletions src/controllers/recordController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class RecordController {
return record;
}

// TODO: why don't I naturally just join these instead of having two separate calls?
async getEventsForMemberRecord(id: string) {
const [data] = await pool.query(
`SELECT uuid, event_name, start_time, end_time, sign_in_closed, description, location FROM AttendanceRecord JOIN Event ON AttendanceRecord.event_id = Event.uuid where person_id = ?`,
Expand Down
129 changes: 84 additions & 45 deletions src/controllers/votingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,79 @@ import {
VHQuery,
VotingHistory,
} from "../types/types";
import { pool } from "../utils";
import { isEmpty, pool } from "../utils";

export class VotingController {
async getAllQuestions() {
const [data] = await pool.query(`SELECT * from VoteQuestion`);

const parsedData = data as RowDataPacket[];
const quesitonList = parsedData
.map((question) => {
try {
return parseDataToVoteQuestion(question);
} catch (err) {
return null;
}
})
.filter(Boolean);

return quesitonList;
// async getMostRecentQuestion() {
// const SELECTFROM = "SELECT uuid, question, description FROM VoteQuestion ";
// const WHERE = "WHERE time_start < NOW() AND time_end > NOW()";
// const totalQuery = SELECTFROM + WHERE;

// const [result] = await pool.query(totalQuery);
// return result;
// }

async determineQuestion(member_id: string) {
const SELECTFROM =
"SELECT vq.question, vq.description FROM VoteQuestion vq";
const WHERE =
"vq.time_start < NOW() AND vq.time_end > NOW() AND NOT EXISTS(";
const innerQuery =
" SELECT 1 FROM VoteHistory vh WHERE vq.uuid = vh.vote_id AND member_id = ?";

const totalQuery = SELECTFROM + WHERE + innerQuery;
const [result] = await pool.query(totalQuery, [member_id]);
return result;
}

// if have both vote_id/member_id => we are trying to find whether they already voted for the event
// if we have just member_id => we want to find the members voting Records to be displayed
async getVotingHistory(queryParams: VHQuery) {
// async getIfMemberVoted(member_id: string) {
// // TODO: maybe check if we need an async?
// const currentQuestion = await this.getMostRecentQuestion();

// const parsedQuestion = currentQuestion as RowDataPacket[];

// // if there is not a currentQuestion Return Blank
// if (!parsedQuestion) {
// return [];
// }

// // with the currentQuestion check based on the uuid, check if we have
// const SELECT = "SELECT * FROM VoteHistory ";
// const WHERE = "WHERE vote_id = ? AND member_id = ?";
// const totalQuery = SELECT + WHERE;

// const data = [parsedQuestion[0].uuid, member_id];
// const [result] = await pool.query(totalQuery, data);

// // if we found a corresponding vote for this question, show that no vote is available;
// if (!isEmpty(result)) {
// return [];
// }

// // else return the question
// return parsedQuestion;
// }

async getVotingHistory(primaryKeys: string[]) {
const SELECTFROM = "SELECT * FROM VoteHistory";
let WHERE = "";
let data = [];

const validParmsToQuery = new Map([
["member_id", "member_id = ?"],
["vote_id", "vote_id = ?"],
]);

for (let i = 0; i < Object.keys(queryParams).length; i++) {
const currKey = Object.keys(queryParams)[i];
if (validParmsToQuery.has(currKey)) {
if (WHERE) {
WHERE += " AND " + validParmsToQuery.get(currKey);
} else {
WHERE += " WHERE " + validParmsToQuery.get(currKey);
}
data.push(Object.values(queryParams)[i]);
}
}
let WHERE = "WHERE member_id = ? AND vote_id = ?";

const totalQuery = SELECTFROM + WHERE;
const [result] = await pool.query(totalQuery, data);
const [result] = await pool.query(totalQuery, primaryKeys);

return result;
}

async getMembersPastVotes(member_id: string) {
const SELECT = "SELECT question, vote_selection FROM ";
const JOIN =
"VoteQuestion JOIN VoteHistory ON VoteQuestion.uuid = VoteHistory.vote_id ";
const WHERE = "WHERE member_id = ?";

const fullQuery = SELECT + JOIN + WHERE;
const [result] = await pool.query(fullQuery, [member_id]);

// TODO: add types for this
return result;
}

Expand All @@ -74,15 +100,28 @@ export class VotingController {
}

const totalString = initialQuery + initialValue;
// primary keys for
const [result] = await pool.query(totalString, values);
// subsequent query to get the item back
const query = {
member_id: vote.member_id,
vote_id: vote.vote_id,
};
// subsequent query to get the item back (primary keys)
const query = [vote.member_id, vote.vote_id];

const postedVote = this.getVotingHistory(query);
return postedVote;
}
}

// const validParmsToQuery = new Map([
// ["member_id", "member_id = ?"],
// ["vote_id", "vote_id = ?"],
// ]);

// for (let i = 0; i < Object.keys(queryParams).length; i++) {
// const currKey = Object.keys(queryParams)[i];
// if (validParmsToQuery.has(currKey)) {
// if (WHERE) {
// WHERE += " AND " + validParmsToQuery.get(currKey);
// } else {
// WHERE += " WHERE " + validParmsToQuery.get(currKey);
// }
// data.push(Object.values(queryParams)[i]);
// }
// }
6 changes: 4 additions & 2 deletions src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//TODO: the typing and parsing like this is extremly annoying...
//TODO p2: member should not include the nuid in its type/ what it comes back from the db, change this ( reduce fluff whereever possible)

//Types and Schemas for the database
import { z } from "zod";
import { RowDataPacket } from "mysql2";
import { castBufferToBoolean } from "../utils";
import { string } from "joi";

//Member
export const MemberSchema = z
Expand Down Expand Up @@ -186,7 +188,7 @@ export type VotingQuestion = z.infer<typeof VotingQuestionSchema>;
export const VoteHistoryQuerySchema = z
.object({
member_id: z.string(),
vote_id: z.string().optional(),
vote_id: z.string(),
})
.strict();

Expand Down