From ca447499b71ef75c3f5bce84e272f8bdbb07f0a7 Mon Sep 17 00:00:00 2001 From: Kang-Quan <105635942+Kang-Quan@users.noreply.github.com> Date: Fri, 13 Oct 2023 12:54:51 +0800 Subject: [PATCH] Update db schema (#41) * Update schema to allow for null values since it is empty in the start * Changed contrains for User schema email does not need to be unique * Update schema username does not have to be unique either * Update new db schema to link with other database tested with postman Add input validation * Add more data validation * Remove line that cause error in frontend index.tsx --- front-end/peer-prep/src/index.tsx | 117 +++++++++--------- .../src/controller/user-controller.ts | 102 +++++++++------ services/profile-service/src/entity/User.ts | 13 +- .../profile-service/src/router/user-routes.ts | 6 +- 4 files changed, 134 insertions(+), 104 deletions(-) diff --git a/front-end/peer-prep/src/index.tsx b/front-end/peer-prep/src/index.tsx index 16962cf9..94b1dcab 100644 --- a/front-end/peer-prep/src/index.tsx +++ b/front-end/peer-prep/src/index.tsx @@ -1,84 +1,85 @@ -import ReactDOM from 'react-dom/client'; -import reportWebVitals from './reportWebVitals'; +import ReactDOM from "react-dom/client"; +import reportWebVitals from "./reportWebVitals"; import { auth } from "./components/Auth/Firebase"; import { useAuthState } from "react-firebase-hooks/auth"; // import Redux components here -import {Provider} from "react-redux"; -import store from "./components/redux/store/store" +import { Provider } from "react-redux"; +import store from "./components/redux/store/store"; // import React Routing components here import { - BrowserRouter, + BrowserRouter, Routes, Route, - Outlet, - Navigate, + Outlet, + Navigate, } from "react-router-dom"; // import app components here -import QuestionsPage from './components/QuestionsPage'; -import UserPage from './components/UserPage'; +import QuestionsPage from "./components/QuestionsPage"; +import UserPage from "./components/UserPage"; // import styles -import { appStyle } from './styles'; -import ErrorPage from './components/ErrorPage'; -import SignInPage from './components/Auth/SignInPage'; -import Navbar from './components/Navbar'; -import GoodbyePage from './components/Auth/GoodbyePage'; +import { appStyle } from "./styles"; +import ErrorPage from "./components/ErrorPage"; +import SignInPage from "./components/Auth/SignInPage"; +import Navbar from "./components/Navbar"; +import GoodbyePage from "./components/Auth/GoodbyePage"; const ProtectedRoute = () => { - const [user, loading, error] = useAuthState(auth); - debugger; + const [user, loading, error] = useAuthState(auth); + debugger; if (loading) { - // the user object will be null if firebase is loading - // handle loading next time - return <> - } - if (error) { - return ; - } - if (!user) { - debugger; - // User is not authenticated, navigate to SignIn page - return ; - } - return + // the user object will be null if firebase is loading + // handle loading next time + return <>; + } + if (error) { + return ; + } + if (!user) { + debugger; + // User is not authenticated, navigate to SignIn page + return ; + } + return ; }; - const RootApp = () => { - return ( - -
- - - {/* All protected routes are written here */} - - - - - }> - }/> - }/> - - {/* All non-protected routes are written here */} - } /> - } /> - }/> - }/> - - -
-
- ) -} + return ( + +
+ + + {/* All protected routes are written here */} + + + + + } + > + } /> + } /> + + {/* All non-protected routes are written here */} + } /> + } /> + } /> + } /> + + +
+
+ ); +}; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById("root") as HTMLElement ); -root.render(); +root.render(); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) diff --git a/services/profile-service/src/controller/user-controller.ts b/services/profile-service/src/controller/user-controller.ts index 9c916285..0b5177f2 100644 --- a/services/profile-service/src/controller/user-controller.ts +++ b/services/profile-service/src/controller/user-controller.ts @@ -3,64 +3,96 @@ import { ResponseUtil } from "../utils/Response"; import { User } from "../entity/User"; import { AppDataSource } from "../data-source"; +interface iUserData { + uid: string; + username: string; + email: string; + firstName: string; + lastName: string; + age: number; +} + +const ERR_MSG_NO_USER = "User not found"; +const ERR_MSG_BAD_REQUEST = + "Bad Request - Request body is empty or fields missing"; +const ERR_MSG_AGE_NEGATIVE = "Invalid age: Age cannot be negative."; +const ERR_MSG_DUPLICATE = + "Update a resource that already exists or has conflicting information"; + +//helper function to validate data +async function validateData(userData: iUserData): Promise { + // Array of required fields + const requiredFields = [ + "uid", + "username", + "email", + "firstName", + "lastName", + "age", + ]; + + for (const field of requiredFields) { + if (!userData[field]) { + return ERR_MSG_BAD_REQUEST; + } + } + if (userData.age < 0) { + return ERR_MSG_AGE_NEGATIVE; + } + return null; +} + export class UserController { async getUser(req: Request, res: Response): Promise { - const { username } = req.params; + const { uid } = req.params; const user = await AppDataSource.getRepository(User).findOneBy({ - username: username, + uid: uid, }); if (!user) { - return ResponseUtil.sendError(res, "User not found", 404); + return ResponseUtil.sendError(res, ERR_MSG_NO_USER, 404); } return ResponseUtil.sendResponse(res, user, 200); } async createUser(req: Request, res: Response, next): Promise { const userData = req.body; - const repo = AppDataSource.getRepository(User); - - //check for duplicate username - const checkUserName = await AppDataSource.getRepository(User).findOneBy( - { - username: userData.username, - } - ); - if (checkUserName) { - return ResponseUtil.sendError(res, "Username already used", 409); + const validationError = await validateData(userData); + if (validationError) { + return ResponseUtil.sendError(res, validationError, 400); } - //check for duplicate email - const checkEmail = await AppDataSource.getRepository(User).findOneBy({ - email: userData.email, + const repo = AppDataSource.getRepository(User); + const userInDB = await AppDataSource.getRepository(User).findOneBy({ + uid: userData.uid, }); - - if (checkEmail) { - return ResponseUtil.sendError(res, "Email already used", 409); + if (userInDB) { + return ResponseUtil.sendError(res, ERR_MSG_DUPLICATE, 409); } - const user = repo.create(userData); await repo.save(user); return ResponseUtil.sendResponse(res, user, 201); } async updateUser(req: Request, res: Response): Promise { - const { username } = req.params; + const { uid } = req.params; const userData = req.body; + + const validationError = await validateData(userData); + if (validationError) { + return ResponseUtil.sendError(res, validationError, 400); + } + + if (uid !== userData.uid) { + return ResponseUtil.sendError(res, ERR_MSG_DUPLICATE, 409); + } + const repo = AppDataSource.getRepository(User); const user = await repo.findOneBy({ - username: username, + uid: uid, }); if (!user) { - return ResponseUtil.sendError(res, "user not found", 404); - } - - //check for duplicate email - const checkEmail = await AppDataSource.getRepository(User).findOneBy({ - email: userData.email, - }); - if (checkEmail) { - return ResponseUtil.sendError(res, "Email already used", 409); + return ResponseUtil.sendError(res, ERR_MSG_NO_USER, 404); } repo.merge(user, userData); @@ -69,15 +101,15 @@ export class UserController { } async deleteUser(req: Request, res: Response): Promise { - const { username } = req.params; + const { uid } = req.params; const repo = AppDataSource.getRepository(User); const user = await repo.findOneBy({ - username: username, + uid: uid, }); if (!user) { - return ResponseUtil.sendError(res, "user not found", 404); + return ResponseUtil.sendError(res, ERR_MSG_NO_USER, 404); } await repo.remove(user); - return ResponseUtil.sendResponse(res, null); + return ResponseUtil.sendResponse(res, null, 204); } } diff --git a/services/profile-service/src/entity/User.ts b/services/profile-service/src/entity/User.ts index 0132d838..b743715e 100644 --- a/services/profile-service/src/entity/User.ts +++ b/services/profile-service/src/entity/User.ts @@ -1,18 +1,15 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, Column, PrimaryColumn } from "typeorm"; @Entity() export class User { - @PrimaryGeneratedColumn() - user_id: number; + @PrimaryColumn() + uid: string; - @Column({ unique: true }) + @Column() username: string; - @Column({ unique: true }) - email: string; - @Column() - password: string; + email: string; @Column() firstName: string; diff --git a/services/profile-service/src/router/user-routes.ts b/services/profile-service/src/router/user-routes.ts index 1d7f84bf..a2256c53 100644 --- a/services/profile-service/src/router/user-routes.ts +++ b/services/profile-service/src/router/user-routes.ts @@ -5,9 +5,9 @@ const userController = new UserController(); const router = express.Router(); -router.get("/:username", userController.getUser); +router.get("/:uid", userController.getUser); router.post("/", userController.createUser); -router.put("/:username", userController.updateUser); -router.delete("/:username", userController.deleteUser); +router.put("/:uid", userController.updateUser); +router.delete("/:uid", userController.deleteUser); export default router;