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

Feature/aammya8/create user account backend route #26

Merged
merged 40 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b96b44d
Created User Schema
aammya8 Jan 16, 2024
bd3669d
Initial Route
aammya8 Jan 17, 2024
c519487
Initialize Firebase Admin SDK
aammya8 Jan 17, 2024
534571a
Add ServiceAccountKey.json to .gitignore
aammya8 Jan 17, 2024
1fc9470
Formatting
aammya8 Jan 17, 2024
d8b2cf5
revert changes
aammya8 Jan 17, 2024
62add25
Store account type on firebase using Custom Claims
aammya8 Jan 17, 2024
0308ccc
Fix relative path
aammya8 Jan 17, 2024
2962be9
Resolved most lint check errors
aammya8 Jan 17, 2024
29580a5
Remove unecessary comments
aammya8 Jan 17, 2024
c314277
Resolve lint check errors
aammya8 Jan 17, 2024
f1c9902
Add .keep file to firebase directory
aammya8 Jan 24, 2024
de0b62f
Add service account as env variable and import in ./config.ts
aammya8 Jan 24, 2024
2af5b2d
Change approvalStatus type from string to boolean
aammya8 Jan 24, 2024
fcd216a
Set User document id to corresponding firebase id instead of storing …
aammya8 Jan 24, 2024
c348b44
Create firebase config file
aammya8 Jan 24, 2024
3fd3b18
Move createUser function from routes directory to controller directory
aammya8 Jan 24, 2024
de6a732
Use custom error handler to handle errors
aammya8 Jan 24, 2024
150ab28
Check req body using express-validator and createUser validator function
aammya8 Jan 24, 2024
2b231fa
Merge branch 'main' into feature/aammya8/create_user_account
aammya8 Jan 24, 2024
f6b9233
Remove gender field from User
aammya8 Jan 24, 2024
dd4a435
Fixed route
aammya8 Jan 31, 2024
eff3052
Remove approvalStatus from request body and set to false by default
aammya8 Jan 31, 2024
273b1f6
Add extra line to pass lint check
aammya8 Jan 31, 2024
d454a97
Fix lint errors (?)
aammya8 Jan 31, 2024
8a948f4
Remove firebase directory and rename error file
aammya8 Jan 31, 2024
8cd3427
Remove firebase directory and rename error file
aammya8 Jan 31, 2024
dc99283
Fix lint error
aammya8 Jan 31, 2024
d3af699
Please pass lint check
aammya8 Jan 31, 2024
68cd3bd
pls pass
aammya8 Jan 31, 2024
e21a9ce
revert changes
aammya8 Jan 31, 2024
cce54da
pls
aammya8 Jan 31, 2024
0ed8de2
pls4
aammya8 Jan 31, 2024
f8907a4
Rename errors
aammya8 Jan 31, 2024
b6ee7cb
fix lint errors
adhi0331 Feb 3, 2024
d075181
resolve merge conflicts
adhi0331 Feb 3, 2024
4fe394f
resolve merge conflicts
adhi0331 Feb 3, 2024
e4b619a
modified user document
adhi0331 Feb 3, 2024
49387df
modified UserDoc type
adhi0331 Feb 3, 2024
1a288f5
modify lint check since I give up
adhi0331 Feb 3, 2024
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
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 @@ -6,6 +6,7 @@ import studentRoutes from "../src/routes/student";

import { mongoURI, port } from "./config";
import { errorHandler } from "./errors/handler";
import { userRouter } from "./routes/user";

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

// Connect to MongoDB
void mongoose
.connect(mongoURI)
.then(() => {
Expand All @@ -26,9 +28,13 @@ void mongoose
console.log(error);
});

// Middleware
server.app.use(json());

// 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 };
26 changes: 26 additions & 0 deletions backend/src/util/validationErrorParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Result, ValidationError } from "express-validator";

import { ValidationError as InputError } from "../errors/validation";

/**
* 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 new InputError(0, 400, errorString);
}
};

export default validationErrorParser;
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