Skip to content

Commit

Permalink
Merge pull request #15 from TritonSE/feature/arnav-jacob/page-footer
Browse files Browse the repository at this point in the history
Feature/arnav jacob/page footer
  • Loading branch information
jackavh authored Feb 13, 2024
2 parents 2982966 + 6603351 commit f5e0fc3
Show file tree
Hide file tree
Showing 26 changed files with 951 additions and 484 deletions.
23 changes: 0 additions & 23 deletions .husky/lint-config.sh

This file was deleted.

216 changes: 0 additions & 216 deletions .husky/pre-commit

This file was deleted.

3 changes: 0 additions & 3 deletions .husky/pre-push

This file was deleted.

3 changes: 3 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "dotenv/config";
import cors from "cors";
import express, { NextFunction, Request, Response } from "express";
import { isHttpError } from "http-errors";
import subscriberRoutes from "src/routes/subscriber";
import memberRoutes from "src/routes/members";

const app = express();
Expand All @@ -25,7 +26,9 @@ app.use(
);

// Routes ( e.g. app.use("/api/task", taskRoutes); )
app.use("/api/subscribers", subscriberRoutes);
app.use("/api/member", memberRoutes);

/**
* Error handler; all errors thrown by server are handled here.
* Explicit typings required here because TypeScript cannot infer the argument types.
Expand Down
32 changes: 32 additions & 0 deletions backend/src/controllers/subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Controller for the newsletter subscriber route, /api/subscribers.
* passes error handling off to /src/util/validationErrorParser.ts
*/

import { RequestHandler } from "express";
import { validationResult } from "express-validator";
import Subscriber from "src/models/subscriber";
import validationErrorParser from "src/util/validationErrorParser";

export const createSubscriber: RequestHandler = async (req, res, next) => {
const errors = validationResult(req);
const { email } = req.body;

try {
// validationErrorParser is a helper that throws 400 if there are errors
validationErrorParser(errors);
const subscriber = await Subscriber.create({
email: email,
});

/*
* TODO: Handle adding the newsletter subscriber
* to a mailing list or however this will be handled.
*/

// successfully created subscriber in db
res.status(201).json(subscriber);
} catch (error) {
next(error);
}
};
13 changes: 13 additions & 0 deletions backend/src/models/subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Defines the schema for a newsletter subscriber
*/

import { InferSchemaType, Schema, model } from "mongoose";

const subscriberSchema = new Schema({
email: { type: String, required: true },
});

type Subscriber = InferSchemaType<typeof subscriberSchema>;

export default model<Subscriber>("Subscriber", subscriberSchema);
13 changes: 13 additions & 0 deletions backend/src/routes/subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Newsletter subscription route requests.
*/

import express from "express";
import * as SubscriberController from "src/controllers/subscriber";
import * as SubscriberValidator from "src/validators/subscriber";

const router = express.Router();

router.post("/", SubscriberValidator.createSubscriber, SubscriberController.createSubscriber);

export default router;
25 changes: 25 additions & 0 deletions backend/src/util/validationErrorParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Result, ValidationError } from "express-validator";
import createHttpError from "http-errors";

/**
* Parses through errors thrown by validator (if any exist). Error messages are
* added to a string and that string is used as the error message for the HTTP
* error.
*
* @param errors the validation result provided by express validator middleware
*/
const validationErrorParser = (errors: Result<ValidationError>) => {
if (!errors.isEmpty()) {
let errorString = "";

// parse through errors returned by the validator and append them to the error string
for (const error of errors.array()) {
errorString += error.msg + " ";
}

// trim removes the trailing space created in the for loop
throw createHttpError(400, errorString.trim());
}
};

export default validationErrorParser;
34 changes: 34 additions & 0 deletions backend/src/validators/subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Normalize and validate emails for newsletter subscribers
* in the route handler.
*/

import { body } from "express-validator";
import Subscriber from "src/models/subscriber";

/*
* 1. Trim whitespace then
* 2. check if empty then
* 3. check if valid email then
* 4. normalize email then
* 5. check if email already exists in db
*/
const makeEmailValidator = () =>
body("email")
.trim()
.exists()
.withMessage("email is required")
.bail()
.isEmail()
.withMessage("email must be a valid email address")
.bail()
.normalizeEmail()
.custom(async (value) => {
// check if email already exists in db
const subscriber = await Subscriber.findOne({ email: value }).exec();
if (subscriber !== null) {
return Promise.reject(`email is already subscribed`);
}
});

export const createSubscriber = [makeEmailValidator()];
3 changes: 2 additions & 1 deletion frontend/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"printWidth": 100
"printWidth": 100,
"trailingComma": "all"
}
Loading

0 comments on commit f5e0fc3

Please sign in to comment.