Skip to content

Commit

Permalink
Merge branch 'main' into feature/lisasiliu/add-programs-form-backend-…
Browse files Browse the repository at this point in the history
…route
  • Loading branch information
lisasiliu authored Feb 5, 2024
2 parents c7461c0 + 6908a29 commit 795f83c
Show file tree
Hide file tree
Showing 18 changed files with 312 additions and 19 deletions.
Binary file added .DS_Store
Binary file not shown.
Binary file added backend/.DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions backend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
// "@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-misused-promises": "warn",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-call": "warn",
"@typescript-eslint/no-unsafe-member-access": "warn",

// Avoid bugs.
"@typescript-eslint/no-shadow": ["error", { "ignoreTypeValueShadow": true }],
Expand Down
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Firebase Service Account Key
src/firebase/ServiceAccountKey.json

.env
node_modules/
.eslintcache
Expand Down
6 changes: 3 additions & 3 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import studentRoutes from "../src/routes/student";
import { mongoURI, port } from "./config";
import { errorHandler } from "./errors/handler";
import program from "./routes/program";
import { userRouter } from "./routes/user";

/**
* Express server application class
Expand All @@ -18,6 +19,7 @@ class Server {
// initialize server app
const server = new Server();

// Connect to MongoDB
void mongoose
.connect(mongoURI)
.then(() => {
Expand All @@ -27,11 +29,15 @@ void mongoose
console.log(error);
});

// Middleware
server.app.use(json());
server.app.use("/program", program);
server.app.use(errorHandler);

// Routes
server.app.use("/user", userRouter);
server.app.use("/student", studentRoutes);
// Error Handler
server.app.use(errorHandler);

// make server listen on some port
Expand Down
15 changes: 4 additions & 11 deletions backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import { RequestHandler } from "express";
import { validationResult } from "express-validator";

import { ValidationError } from "../errors/validation";
import StudentModel from "../models/student";
import validationErrorParser from "../util/validationErrorParser";

export type contact = {
lastName: string;
Expand All @@ -32,17 +32,10 @@ export type typedModel = {
};

export const createStudent: RequestHandler = async (req, res, next) => {
const errors = validationResult(req);

try {
if (!errors.isEmpty()) {
let errorString = "";

for (const error of errors.array()) {
errorString += error.msg + " ";
}
throw new ValidationError(errorString);
}
const errors = validationResult(req);

validationErrorParser(errors);

const newStudent = await StudentModel.create(req.body as typedModel);

Expand Down
53 changes: 53 additions & 0 deletions backend/src/controllers/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NextFunction, Request, Response } from "express";
import { validationResult } from "express-validator";
import admin from "firebase-admin";

import UserModel from "../models/user";
import { firebaseAuth } from "../util/firebase";
import validationErrorParser from "../util/validationErrorParser";

// Define the type for req.body
type CreateUserRequestBody = {
name: string;
accountType: "admin" | "team";
email: string;
password: string;
};

export const createUser = async (
req: Request<Record<string, never>, Record<string, never>, CreateUserRequestBody>,
res: Response,
nxt: NextFunction,
) => {
try {
// Check for validation errors
const errors = validationResult(req);

validationErrorParser(errors);

const { name, accountType, email, password } = req.body;

// Create user in Firebase
const userRecord = await firebaseAuth.createUser({
email,
password,
} as admin.auth.CreateRequest); // Type assertion

// Set custom claim for accountType (“admin” | “team”)
await firebaseAuth.setCustomUserClaims(userRecord.uid, { accountType });

const newUser = await UserModel.create({

Check warning on line 39 in backend/src/controllers/user.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

Check warning on line 39 in backend/src/controllers/user.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe call of an `any` typed value

Check warning on line 39 in backend/src/controllers/user.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe member access .create on an `any` value
_id: userRecord.uid, // Set document id to firebaseUID (Linkage between Firebase and MongoDB)
name,
accountType,
// approvalStatus default false in User constructor
});

res.status(201).json(newUser);
} catch (error) {
console.error(error);
nxt(error);
}

return;
};
1 change: 1 addition & 0 deletions backend/src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./errors";
export * from "./internal";
export * from "./validation";
1 change: 1 addition & 0 deletions backend/src/errors/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export class InternalError extends CustomError {
static NO_APP_PORT = new InternalError(0, 500, NO_APP_PORT);

static NO_MONGO_URI = new InternalError(1, 500, NO_MONGO_URI);

static NO_SERVICE_ACCOUNT_KEY = new InternalError(5, 500, NO_SERVICE_ACCOUNT_KEY);
}
9 changes: 6 additions & 3 deletions backend/src/errors/validation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CustomError } from "../errors";
import { CustomError } from "./errors";

const USER_CREATION_UNSUCCESSFUL = "User not created successfully";

export class ValidationError extends CustomError {
constructor(message: string) {
super(0, 400, "VALIDATION ERROR: " + message);
constructor(code: number, status: number, message: string) {
super(code, status, "VALIDATION ERROR: " + message);
}
static USER_CREATION_UNSUCCESSFUL = new ValidationError(1, 400, USER_CREATION_UNSUCCESSFUL);
}
Empty file removed backend/src/models/.keep
Empty file.
18 changes: 18 additions & 0 deletions backend/src/models/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import mongoose, { InferSchemaType } from "mongoose";

// export type UserDocument = {
// name: string;
// accountType: "admin" | "team"; // NOTE Also stored on Firebase using Custom Claims
// approvalStatus: boolean;
// };

const userSchema = new mongoose.Schema({
_id: { type: String, required: true }, // Set _id to firebaseUid; Linkage between firebase account and user document on MongoDb
name: { type: String, required: true },
accountType: { type: String, enum: ["admin", "team"], required: true },
approvalStatus: { type: Boolean, default: false }, // default false
});

type User = InferSchemaType<typeof userSchema>;

export default mongoose.model<User>("User", userSchema);
12 changes: 12 additions & 0 deletions backend/src/routes/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import express from "express";

import * as UserController from "../controllers/user";
import * as UserValidator from "../validators/user";

const router = express.Router();

router.use(express.json());

router.post("/", UserValidator.createUser, UserController.createUser);

Check warning on line 10 in backend/src/routes/user.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise returned in function argument where a void return was expected

export { router as userRouter };
2 changes: 1 addition & 1 deletion backend/src/util/validationErrorParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const validationErrorParser = (errors: Result<ValidationError>) => {
}

// trim removes the trailing space created in the for loop
throw new InputError(errorString);
throw new InputError(0, 400, errorString);
}
};

Expand Down
8 changes: 8 additions & 0 deletions backend/src/validators/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ValidationChain, body } from "express-validator";

export const createUser: ValidationChain[] = [
body("name").notEmpty().isString(),
body("accountType").notEmpty().isIn(["admin", "team"]),
body("email").notEmpty().isEmail(),
body("password").notEmpty().isString().isLength({ min: 6 }),
];
2 changes: 1 addition & 1 deletion backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
"resolveJsonModule": true /* Enable importing .json files. */,
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

Expand Down
Loading

0 comments on commit 795f83c

Please sign in to comment.