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

Create Articles and Event Enrollments CRUD Routes #36

Merged
merged 9 commits into from
Jan 10, 2025
148 changes: 148 additions & 0 deletions server/routes/articles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import express from "express";

import { keysToCamel } from "../common/utils";
import { db } from "../db/db-pgp";

const articlesRouter = express.Router();
articlesRouter.use(express.json());

interface Article {
id: number;
s3_url: string;
description: string;
media_url: string;
}

interface ArticleRequest {
s3_url?: string;
description?: string;
media_url?: string;
}

// GET /articles/:id
articlesRouter.get("/:id", async (req, res) => {
try {
// Select rows where the id matches the id in the request parameters
const rows = await db.query("SELECT * FROM articles WHERE id = $1", [
req.params.id,
]);
// If no rows are returned, send a 404 response
if (rows.length === 0) {
return res.status(404).json({ error: "Article not found" });
}
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(rows[0] as Article));
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// GET /articles
articlesRouter.get("/", async (req, res) => {
try {
// Select all rows from the articles table
const rows = await db.query("SELECT * FROM articles");
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(rows) as Article[]);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// POST /articles
articlesRouter.post("/", async (req, res) => {
try {
// Destructure the request body
const { s3_url, description, media_url } = req.body as ArticleRequest;
// Since its required in the schema send an error
if (!s3_url || !description || !media_url) {
return res.status(400).json({ error: "Missing required parameters" });
}
// Insert the new article into the database
// Returning * will return the newly inserted row in the response
const rows = await db.query(
"INSERT INTO articles (s3_url, description, media_url) VALUES ($1, $2, $3) RETURNING *",
[s3_url, description, media_url]
);
// Convert the snake_case keys to camelCase and send the response with status 201 (Created)
res.status(201).json(keysToCamel(rows[0] as Article));
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// PUT /articles/:id
articlesRouter.put("/:id", async (req, res) => {
try {
// Destructure the request body
const { id } = req.params;
const { s3_url, description, media_url } = req.body as ArticleRequest;

const to_update = []; // Parameters that needed to be updated
const values = []; // Values that need to be assosciated with the specific parameter
let paramCount = 1; // the id of the value matching the parameter

if (s3_url) {
to_update.push(`s3_url = $${paramCount}`);
values.push(s3_url);
paramCount++;
}
if (description) {
to_update.push(`description = $${paramCount}`);
values.push(description);
paramCount++;
}
if (media_url) {
to_update.push(`media_url = $${paramCount}`);
values.push(media_url);
paramCount++;
}

values.push(id);

const query = `
UPDATE articles
SET ${to_update.join(", ")}
WHERE id = $${paramCount}
RETURNING *
`;

// Update the article with the matching id
const rows = await db.query(query, values);

// If no rows are returned, send a 404 response
if (rows.length === 0) {
// Could not find the article with the given id
return res.status(404).json({ error: "Article not found" });
}
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(rows[0]) as Article);
} catch (error) {
// Send a 500 response with the error message
res.status(500).json({ error: error.message });
}
});

// DELETE /articles/:id
articlesRouter.delete("/:id", async (req, res) => {
try {
// Delete the article with the matching id
const rows = await db.query(
"DELETE FROM articles WHERE id = $1 RETURNING *",
[req.params.id]
);
// If no rows are returned, send a 404 response
if (rows.length === 0) {
// Could not find the article with the given id
return res.status(404).json({ error: "Article not found" });
}
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(rows[0] as Article));
} catch (error) {
// Send a 500 response with the error message
res.status(500).json({ error: error.message });
}
});

// Export the router made to handle these new routes
export default articlesRouter;
128 changes: 128 additions & 0 deletions server/routes/event_enrollments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import express from "express";

import { keysToCamel } from "../common/utils";
import { db } from "../db/db-pgp";

const eventEnrollmentRouter = express.Router();
eventEnrollmentRouter.use(express.json());

interface EventEnrollment {
id: number;
student_id: number;
event_id: number;
attendance: boolean;
}

interface EventEnrollmentRequest {
student_id?: number;
event_id?: number;
attendance?: boolean;
}

// GET /
eventEnrollmentRouter.get("/", async (req, res) => {
try {
const events = await db.query("SELECT * FROM event_enrollments");
res.status(200).json(keysToCamel(events) as EventEnrollment[]);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// GET /:id
eventEnrollmentRouter.get("/:id", async (req, res) => {
try {
const events = await db.query(
"SELECT * FROM event_enrollments WHERE id = $1",
[req.params.id]
);
// If no rows are returned, send a 404 response
if (events.length === 0) {
return res.status(404).json({ error: "Event not found" });
}
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(events[0] as EventEnrollment));
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// POST /event-enrollments
eventEnrollmentRouter.post("/", async (req, res) => {
try {
// Destructure the request body
const { student_id, event_id } = req.body as EventEnrollmentRequest;
// mathing sql schema
if (!student_id || !event_id) {
return res.status(400).json({ error: "Missing required parameters" });
}

// Insert the new article into the database
// Returning * will return the newly inserted row in the response

// By default the attendance will be false as mentioned in the table
const rows = await db.query(
"INSERT INTO event_enrollments (student_id, event_id, attendance) VALUES ($1, $2, false) RETURNING *",
[student_id, event_id]
);
// Convert the snake_case keys to camelCase and send the response with status 201 (Created)
res.status(201).json(keysToCamel(rows[0] as EventEnrollment));
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// PUT /:id
eventEnrollmentRouter.put("/:id", async (req, res) => {
try {
// Destructure the request body
const { id } = req.params;
const { student_id, event_id, attendance } =
req.body as EventEnrollmentRequest;

const to_update = [];
const values = [];
let paramCount = 1;

if (student_id) {
to_update.push(`student_id = $${paramCount}`);
values.push(student_id);
paramCount++;
}
if (event_id) {
to_update.push(`event_id = $${paramCount}`);
values.push(event_id);
paramCount++;
}
if (attendance) {
to_update.push(`attendance = $${paramCount}`);
values.push(attendance);
paramCount++;
}

values.push(id);

const query = `
UPDATE event_enrollments
SET ${to_update.join(", ")}
WHERE id = $${paramCount}
RETURNING *
`;

// Update the article with the matching id
const events = await db.query(query, values);

// If no rows are returned, send a 404 response
if (events.length === 0) {
// Could not find the event-enrollment with the given id
return res.status(404).json({ error: "Event not found" });
}
// Convert the snake_case keys to camelCase and send the response
res.status(200).json(keysToCamel(events[0]) as EventEnrollment);
} catch (error) {
// Send a 500 response with the error message
res.status(500).json({ error: error.message });
}
});

export default eventEnrollmentRouter;
34 changes: 0 additions & 34 deletions server/routes/sample.ts

This file was deleted.

9 changes: 7 additions & 2 deletions server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import dotenv from "dotenv";
import express from "express";
import schedule from "node-schedule"; // TODO: Keep only if scheduling cronjobs

import { sampleRouter } from "../routes/sample"; // TODO: delete sample router
import articlesRouter from "../routes/articles";
import eventEnrollmentRouter from "../routes/event_enrollments";
// import { sampleRouter } from "../routes/sample"; // TODO: delete sample router
import { usersRouter } from "../routes/users";
import { verifyToken } from "./middleware";

Expand Down Expand Up @@ -36,8 +38,11 @@ if (process.env.NODE_ENV === "production") {
app.use(verifyToken);
}

app.use("/", sampleRouter); // TODO: delete sample endpoint
// app.use("/", sampleRouter); // TODO: delete sample endpoint
app.use("/users", usersRouter);
// connecting made router with the app
app.use("/articles", articlesRouter);
app.use("/event-enrollments", eventEnrollmentRouter);

app.listen(SERVER_PORT, () => {
console.info(`Server listening on ${SERVER_PORT}`);
Expand Down