Skip to content

Commit

Permalink
Optimize user data fetching and api logic (#11)
Browse files Browse the repository at this point in the history
* Install vitest and test date utilities and validation functions

- Installed **Vitest** for `unit testing` framework.
- Created test cases for the `formatDate` utility function to ensure correct formatting of valid date strings.
- Added validation tests for `username`, `email`, `password`, and `confirm password` fields to verify proper validation logic.
- Updated the testing configuration to support the new tests and ensure they run successfully.

* Add unit test for hooks

- Implemented tests for the `useResize` hook to verify behavior across different screen sizes.
- Added tests for the `useTheme` hook to check the default theme and verify that it updates to light or dark when changed.

* Add unit test for providers

- Added a test to ensure the `ThemeProvider` correctly applies the selected `theme`
  to the document's `root` element.
- Mocked `localStorage` to return a predefined `theme` value for testing.
- Verified that the appropriate `class` is applied to `document.documentElement`
  based on the `theme`.

* Add unit test for zustand stores

- Implemented tests for `userStore` to verify user state
  and persistence in session storage.
- Created tests for `authStore` to ensure authentication
  logic and state management function correctly.

* Add tests for authService methods

Implemented tests for the following **AuthService** methods:

- `signIn`: Verified successful sign-in and error handling.
- `signUp`: Tested user registration process and error responses.
- `signOut`: Checked the sign-out functionality.
- `verifyEmail`: Confirmed email verification process.
- `resendVerify`: Ensured resend verification logic works as expected.
- `verifyToken`: Tested token verification and error handling.
- `forgotPassword`: Validated forgot password request handling.
- `resetPassword`: Checked password reset process.
- `changePassword`: Tested password change functionality.

* Add tests for guard components

- Created unit tests for `AuthGuard` and `PrivateGuard`
  components to ensure proper functionality.
- Verified that guards correctly redirect unauthorized users
  and allow access for authorized users.

* Optimize user data fetching and api logic

- Added `Select` to the user retrieval query
  to minimize the data fetched from the database,
  improving performance and reducing memory usage.
- Updated the user object with the latest information
  fetched from the verifyToken function, ensuring that
  only the necessary fields are loaded and modified.
- This change enhances the efficiency of user operations
  by focusing on relevant fields during database interactions.
  • Loading branch information
Fingertips18 authored Sep 29, 2024
1 parent b1f7b26 commit 0b42037
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 21 deletions.
7 changes: 5 additions & 2 deletions client/src/guards/private-guard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@ import { useEffect } from "react";

import { AuthService } from "@/lib/services/auth-service";
import { useAuthStore } from "@/lib/stores/auth-store";
import { useUserStore } from "@/lib/stores/user-store";
import { VERIFYTOKENKEY } from "@/constants/keys";
import { AppRoutes } from "@/constants/routes";
import { Loading } from "@/components/loading";

const PrivateGuard = () => {
const { authorized, setAuthorized } = useAuthStore();
const { setUser } = useUserStore();

const { isLoading, isError, isSuccess } = useQuery({
const { isLoading, isError, isSuccess, data } = useQuery({
queryKey: [VERIFYTOKENKEY],
queryFn: AuthService.verifyToken,
});

useEffect(() => {
if (isSuccess) {
setAuthorized(true);
setUser(data.user);
}
if (isError) {
setAuthorized(false);
}
}, [isSuccess, isError, setAuthorized]);
}, [isSuccess, isError, setAuthorized, data?.user, setUser]);

if (!authorized) {
return <Navigate to={AppRoutes.SignIn} replace />;
Expand Down
56 changes: 37 additions & 19 deletions controllers/auth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func SignUp(c fiber.Ctx) error {
exp := time.Now().Add(time.Hour * 24)
user.VerificationTokenExpiration = &exp

res := database.Instance.Create(&user)
res := database.Instance.Select("").Create(&user)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to create user"})
}
Expand All @@ -65,7 +65,7 @@ func SignIn(c fiber.Ctx) error {
}

var user models.User
res := database.Instance.Where("email_address = ?", data.Email).First(&user)
res := database.Instance.Select("id", "username", "email_address", "password", "is_verified", "created_at").Where("email_address = ?", data.Email).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand All @@ -85,9 +85,7 @@ func SignIn(c fiber.Ctx) error {

utils.SetCookieToken(c, token)

user.LastSignedIn = time.Now()

res = database.Instance.Save(&user)
res = database.Instance.Model(&user).Update("last_signed_in", time.Now())
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save sign in credentials"})
}
Expand Down Expand Up @@ -117,7 +115,7 @@ func VerifyEmail(c fiber.Ctx) error {

var user models.User

res := database.Instance.Where("verification_token = ?", data.Token).Where("verification_token_exp > ?", time.Now()).First(&user)
res := database.Instance.Select("id", "email_address", "username").Where("verification_token = ?", data.Token).Where("verification_token_exp > ?", time.Now()).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "Invalid or expired verification token"})
Expand All @@ -129,7 +127,13 @@ func VerifyEmail(c fiber.Ctx) error {
user.VerificationToken = nil
user.VerificationTokenExpiration = nil

res = database.Instance.Save(&user)
updates := map[string]interface{}{
"is_verified": user.IsVerified,
"verification_token": user.VerificationToken,
"verification_token_exp": user.VerificationTokenExpiration,
}

res = database.Instance.Model(&user).Updates(updates)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save email verification credentials"})
}
Expand All @@ -152,7 +156,7 @@ func ResendVerify(c fiber.Ctx) error {

var user models.User

res := database.Instance.Where("email_address = ?", data.Email).First(&user)
res := database.Instance.Select("id", "email_address", "username").Where("email_address = ?", data.Email).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand All @@ -168,7 +172,12 @@ func ResendVerify(c fiber.Ctx) error {
exp := time.Now().Add(time.Hour * 24)
user.VerificationTokenExpiration = &exp

res = database.Instance.Save(&user)
updates := map[string]interface{}{
"verification_token": user.VerificationToken,
"verification_token_exp": user.VerificationTokenExpiration,
}

res = database.Instance.Model(&user).Updates(updates)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save email verification credentials"})
}
Expand All @@ -190,7 +199,7 @@ func VerifyToken(c fiber.Ctx) error {

var user models.User

res := database.Instance.Where("id = ?", id).First(&user)
res := database.Instance.Select("id", "username", "email_address", "password", "is_verified", "created_at").Where("id = ?", id).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{
Expand All @@ -217,7 +226,7 @@ func ForgotPassword(c fiber.Ctx) error {
}

var user models.User
res := database.Instance.Where("email_address = ?", data.Email).First(&user)
res := database.Instance.Select("id", "email_address").Where("email_address = ?", data.Email).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand All @@ -234,12 +243,17 @@ func ForgotPassword(c fiber.Ctx) error {
user.ResetPasswordToken = resetPasswordToken
user.ResetPasswordTokenExpiration = &resetPasswordTokenExp

res = database.Instance.Save(&user)
updates := map[string]interface{}{
"reset_password_token": user.ResetPasswordToken,
"reset_password_token_exp": user.ResetPasswordTokenExpiration,
}

res = database.Instance.Model(&user).Updates(updates)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save forgot password credentials"})
}

if err := utils.SendEmailRequestResetPassword(user.Email, *resetPasswordToken); err != nil {
if err := utils.SendEmailRequestResetPassword(user.Email, *user.ResetPasswordToken); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: err.Error()})
}

Expand All @@ -262,7 +276,7 @@ func ResetPassword(c fiber.Ctx) error {

var user models.User

res := database.Instance.Where("reset_password_token = ?", token).First(&user)
res := database.Instance.Select("id", "username", "email_address", "password").Where("reset_password_token = ?", token).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand All @@ -283,7 +297,13 @@ func ResetPassword(c fiber.Ctx) error {
user.ResetPasswordToken = nil
user.ResetPasswordTokenExpiration = nil

res = database.Instance.Save(&user)
updates := map[string]interface{}{
"password": user.Password,
"reset_password_token": user.ResetPasswordToken,
"reset_password_token_exp": user.ResetPasswordTokenExpiration,
}

res = database.Instance.Model(&user).Updates(updates)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save reset password credentials"})
}
Expand All @@ -306,7 +326,7 @@ func ChangePassword(c fiber.Ctx) error {
}

var user models.User
res := database.Instance.Where("email_address = ?", data.Email).First(&user)
res := database.Instance.Select("id", "password").Where("email_address = ?", data.Email).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand All @@ -323,9 +343,7 @@ func ChangePassword(c fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(dto.ErrorDTO{Error: err.Error()})
}

user.Password = password

res = database.Instance.Save(&user)
res = database.Instance.Model(&user).Update("password", password)
if res.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(dto.ErrorDTO{Error: "Unable to save change password credentials"})
}
Expand Down

0 comments on commit 0b42037

Please sign in to comment.