Skip to content

Commit

Permalink
feat(server): added caching to the profile routes
Browse files Browse the repository at this point in the history
  • Loading branch information
AdwaithAthman committed Dec 27, 2023
1 parent 05f598f commit b2962bf
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 47 deletions.
5 changes: 0 additions & 5 deletions server/src/adapters/authController/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { UserDbInterface } from "../../application/repositories/userDbRepository
import { UserRepositoryMongoDB } from "../../frameworks/database/mongoDB/repositories/userRepositoryMongoDB";
import { OtpDbInterface } from "../../application/repositories/otpDbRepository";
import { OtpRepositoryMongoDB } from "../../frameworks/database/mongoDB/repositories/otpRepositoryMongoDB";
import redisRepository from "../../frameworks/database/redis/redisRepository";
import { redisClient } from "../../app";
import { MailSenderService } from "../../frameworks/services/mailSenderService";
import { MailSenderServiceInterface } from "../../application/services/mailSenderServiceInterface";

Expand Down Expand Up @@ -42,9 +40,6 @@ const authController = (
const authService = authServiceInterface(authServiceImpl());
const mailSenderService = mailSenderServiceInterface(mailSenderServiceImpl());

const redisRepositoryImpl = redisRepository(redisClient);
// const { setAsync, sIsMemberAsync, sAddAsync, getAsync } = redisRepositoryImpl;

const registerUser = asyncHandler(async (req: Request, res: Response) => {
const user: UserInterface = req.body;
await userRegister(user, dbUserRepository, authService);
Expand Down
25 changes: 16 additions & 9 deletions server/src/adapters/profileController/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { UserRepositoryMongoDB } from "../../frameworks/database/mongoDB/reposit
import { AuthService } from "../../frameworks/services/authService";
import { AuthServiceInterface } from "../../application/services/authServiceInterface";
import { ProfileInterface } from "../../types/profileInterface";
import { RedisRepository } from "../../frameworks/database/redis/redisRepository";

//use-cases import
import {
Expand All @@ -22,18 +23,22 @@ import {
handleSearchUsers,
handleAddUsername,
} from "../../application/use-cases/profile/userProfile";
import { RedisDbInterface } from "../../application/repositories/redisDbRepository";

const profileController = (
cloudinaryServiceImpl: CloudinaryService,
cloudinaryServiceInterface: CloudinaryServiceInterface,
userDbRepositoryImpl: UserRepositoryMongoDB,
userDbRepositoryInterface: UserDbInterface,
authServiceImpl: AuthService,
authServiceInterface: AuthServiceInterface
authServiceInterface: AuthServiceInterface,
redisRepositoryImpl: RedisRepository,
redisRepositoryInterface: RedisDbInterface,
) => {
const dbUserRepository = userDbRepositoryInterface(userDbRepositoryImpl());
const cloudinaryService = cloudinaryServiceInterface(cloudinaryServiceImpl());
const authService = authServiceInterface(authServiceImpl());
const redisRepository = redisRepositoryInterface(redisRepositoryImpl());

const uploadCoverPhoto = asyncHandler(async (req: Request, res: Response) => {
const { userId }: { userId: string } = req.body;
Expand All @@ -49,7 +54,8 @@ const profileController = (
buffer,
mimetype,
cloudinaryService,
dbUserRepository
dbUserRepository,
redisRepository
);
res.json({
status: "success",
Expand All @@ -73,7 +79,8 @@ const profileController = (
buffer,
mimetype,
cloudinaryService,
dbUserRepository
dbUserRepository,
redisRepository
);
res.json({
status: "success",
Expand All @@ -85,7 +92,7 @@ const profileController = (

const getUserInfo = asyncHandler(async (req: Request, res: Response) => {
const { userId }: { userId: string } = req.body;
const user = await handleUserInfo(userId, dbUserRepository);
const user = await handleUserInfo(userId, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "user info fetched",
Expand All @@ -95,7 +102,7 @@ const profileController = (

const getOtherUserInfo = asyncHandler(async (req: Request, res: Response) => {
const id: string = req.params.id;
const otherUser = await handleUserInfo(id, dbUserRepository);
const otherUser = await handleUserInfo(id, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "user info fetched",
Expand All @@ -105,7 +112,7 @@ const profileController = (

const deleteCoverPhoto = asyncHandler(async (req: Request, res: Response) => {
const { userId }: { userId: string } = req.body;
await handleCoverPhotoDeletion(userId, dbUserRepository);
await handleCoverPhotoDeletion(userId, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "cover photo deleted",
Expand All @@ -114,7 +121,7 @@ const profileController = (

const deleteProfilePhoto = asyncHandler(async (req: Request, res: Response) => {
const { userId }: { userId: string } = req.body;
await handleProfilePhotoDeletion(userId, dbUserRepository);
await handleProfilePhotoDeletion(userId, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "Profile photo deleted",
Expand Down Expand Up @@ -142,7 +149,7 @@ const profileController = (

const editProfile = asyncHandler(async (req: Request, res: Response) => {
const profileInfo: ProfileInterface = req.body;
const user = await handleUpdateProfile(profileInfo, dbUserRepository);
const user = await handleUpdateProfile(profileInfo, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "user info fetched",
Expand All @@ -162,7 +169,7 @@ const profileController = (

const addUsername = asyncHandler(async (req: Request, res: Response) => {
const { userId, username }: { userId: string; username: string } = req.body;
const user = await handleAddUsername(userId, username, dbUserRepository);
const user = await handleAddUsername(userId, username, dbUserRepository, redisRepository);
res.json({
status: "success",
message: "Username added successfully",
Expand Down
82 changes: 53 additions & 29 deletions server/src/application/use-cases/profile/userProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import AppError from "../../../utils/appError";
import { CloudinaryServiceInterface } from "../../services/cloudinaryServiceInterface";
import { UserDbInterface } from "../../repositories/userDbRepository";
import { AuthServiceInterface } from "../../services/authServiceInterface";
import { RedisDbInterface } from "../../repositories/redisDbRepository";

//importing from types
import { HttpStatus } from "../../../types/httpStatus";
import { ProfileInterface } from "../../../types/profileInterface";
import { redisRepository } from "../../../frameworks/database/redis/redisRepository";

export const handleUploadCoverPhoto = async (
userId: string,
buffer: WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>,
mimetype: string,
cloudinaryService: ReturnType<CloudinaryServiceInterface>,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
const b64 = Buffer.from(buffer).toString("base64");
Expand All @@ -23,6 +26,7 @@ export const handleUploadCoverPhoto = async (
cldRes.secure_url
);
const coverPhoto = user?.coverPhoto;
await redisRepository.deleteCache(`userInfo:${userId}`);
return { cldRes, coverPhoto };
} catch (err) {
console.log("err= ", err);
Expand All @@ -38,14 +42,16 @@ export const handleUploadProfilePhoto = async (
buffer: WithImplicitCoercion<ArrayBuffer | SharedArrayBuffer>,
mimetype: string,
cloudinaryService: ReturnType<CloudinaryServiceInterface>,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
const b64 = Buffer.from(buffer).toString("base64");
let dataURI = "data:" + mimetype + ";base64," + b64;
const cldRes = await cloudinaryService.handleUpload(dataURI);
const user = await dbUserRepository.updateDp(userId, cldRes.secure_url);
const dp = user?.dp;
await redisRepository.deleteCache(`userInfo:${userId}`);
return { cldRes, dp };
} catch (err) {
console.log("err= ", err);
Expand All @@ -58,30 +64,37 @@ export const handleUploadProfilePhoto = async (

export const handleUserInfo = async (
userId: string,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
const userData = await dbUserRepository.getUserById(userId);
if (!userData) {
throw new Error("User not found!")
}
const user = {
_id: userData?._id.toString(),
name: userData?.name,
username: userData?.username,
email: userData?.email,
phoneNumber: userData?.phoneNumber,
dp: userData?.dp,
coverPhoto: userData?.coverPhoto,
bio: userData?.bio,
gender: userData?.gender,
city: userData?.city,
followers: userData?.followers,
following: userData?.following,
isAccountVerified: userData?.isAccountVerified,
notifications: userData?.notifications,
};
return user;
const userInfo = await redisRepository.hashCache(
`userInfo:${userId}`,
async () => {
const userData = await dbUserRepository.getUserById(userId);
if (!userData) {
throw new Error("User not found!");
}
const user = {
_id: userData?._id.toString(),
name: userData?.name,
username: userData?.username,
email: userData?.email,
phoneNumber: userData?.phoneNumber,
dp: userData?.dp,
coverPhoto: userData?.coverPhoto,
bio: userData?.bio,
gender: userData?.gender,
city: userData?.city,
followers: userData?.followers,
following: userData?.following,
isAccountVerified: userData?.isAccountVerified,
notifications: userData?.notifications,
};
return user;
}
);
return userInfo;
} catch (err) {
console.log("err= ", err);
throw new AppError("User not found!", HttpStatus.INTERNAL_SERVER_ERROR);
Expand All @@ -90,10 +103,12 @@ export const handleUserInfo = async (

export const handleCoverPhotoDeletion = async (
userId: string,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
await dbUserRepository.deleteCoverPhoto(userId);
await redisRepository.deleteCache(`userInfo:${userId}`);
} catch (err) {
throw new AppError(
"Error deleting cover photo!",
Expand All @@ -104,10 +119,12 @@ export const handleCoverPhotoDeletion = async (

export const handleProfilePhotoDeletion = async (
userId: string,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
await dbUserRepository.deleteProfilePhoto(userId);
await redisRepository.deleteCache(`userInfo:${userId}`);
} catch (err) {
throw new AppError(
"Error deleting cover photo!",
Expand Down Expand Up @@ -147,7 +164,8 @@ export const handleChangePassword = async (

export const handleUpdateProfile = async (
profileInfo: ProfileInterface,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
if (profileInfo.email) {
Expand All @@ -160,7 +178,10 @@ export const handleUpdateProfile = async (
HttpStatus.INTERNAL_SERVER_ERROR
);
} else {
profileInfo.userId && await dbUserRepository.changeIsAccountUnverified(profileInfo.userId);
profileInfo.userId &&
(await dbUserRepository.changeIsAccountUnverified(
profileInfo.userId
));
}
}
if (profileInfo?.username) {
Expand All @@ -186,6 +207,7 @@ export const handleUpdateProfile = async (
}
}
const user = await dbUserRepository.updateProfile(profileInfo);
await redisRepository.deleteCache(`userInfo:${profileInfo.userId}`);
return user;
} catch (error) {
throw error;
Expand All @@ -210,7 +232,8 @@ export const handleSearchUsers = async (
export const handleAddUsername = async (
userId: string,
username: string,
dbUserRepository: ReturnType<UserDbInterface>
dbUserRepository: ReturnType<UserDbInterface>,
redisRepository: ReturnType<RedisDbInterface>
) => {
try {
const existingUsername = await dbUserRepository.getUserByUsername(username);
Expand All @@ -221,6 +244,7 @@ export const handleAddUsername = async (
);
}
const user = await dbUserRepository.addUsername(userId, username);
await redisRepository.deleteCache(`userInfo:${userId}`);
return user;
} catch (error) {
throw error;
Expand Down
7 changes: 5 additions & 2 deletions server/src/frameworks/database/redis/redisRepository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { RedisClient } from "../../../app";
import { redisClient } from "../../../app";

const DEFAULT_EXPIRATION = 1800;

const redisRepository = (redisClient: RedisClient) => {
export const redisRepository = () => {

const stringCache = async (key: string, cb: Function) => {
const cachedData = await redisClient.get(key);
if (cachedData != null) return JSON.parse(cachedData);
Expand All @@ -25,12 +26,14 @@ const redisRepository = (redisClient: RedisClient) => {
const hashCache = async (key: string, cb: Function) => {
const cachedData = await redisClient.hGetAll(key);
if (Object.keys(cachedData).length > 0) {
console.log("from cache")
Object.keys(cachedData).forEach((field) => {
cachedData[field] = JSON.parse(cachedData[field]);
});
return cachedData;
}
const freshData = await cb();
console.log('no cache')
for (const field of Object.keys(freshData)) {
redisClient.hSet(key, field, JSON.stringify(freshData[field]));
}
Expand Down
7 changes: 5 additions & 2 deletions server/src/frameworks/webserver/routes/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { cloudinaryService } from "../../services/cloudinaryService";
import { cloudinaryServiceInterface } from "../../../application/services/cloudinaryServiceInterface";
import { userRepositoryMongoDB } from "../../database/mongoDB/repositories/userRepositoryMongoDB";
import { userDbRepository } from "../../../application/repositories/userDbRepository";
import checkUsernameAvailabilityMiddleware from "../middlewares/redisCheckUsernameAvailability";
import profileController from "../../../adapters/profileController";
import { authService } from "../../services/authService";
import { authServiceInterface } from "../../../application/services/authServiceInterface";
import { redisRepository } from "../../database/redis/redisRepository";
import { redisDbRepository } from "../../../application/repositories/redisDbRepository";

//Middleware
import authMiddleware from "../middlewares/authMiddleware";
Expand All @@ -21,7 +22,9 @@ const profileRouter = () => {
userRepositoryMongoDB,
userDbRepository,
authService,
authServiceInterface
authServiceInterface,
redisRepository,
redisDbRepository
)

//routes
Expand Down

0 comments on commit b2962bf

Please sign in to comment.