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;