From 16b1674356e04b732f5e7224137dc38b2518bf84 Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Fri, 18 Jul 2025 17:45:48 +0700 Subject: [PATCH] :triangular_flag_on_post: complete get user method adding find user by emial, username, and ID --- .../controller/getUserByEmail.controller.ts | 4 +-- .../user/controller/getUserById.controller.ts | 19 ++++++++++ .../getUserByUsername.controller.ts | 22 ++++++++++++ src/modules/user/index.ts | 6 +++- .../read/findUserByEmail.repository.ts | 35 +------------------ .../read/findUserById.repository.ts | 9 +++++ .../read/findUserByUsername.repository.ts | 9 +++++ .../user/schemas/getUserByEmail.schema.ts | 5 +++ .../user/schemas/getUserById.schema.ts | 2 +- .../user/schemas/getUserByUsername.schema.ts | 5 +++ .../user/schemas/getUserOptions.schema.ts | 10 +++--- .../services/http/getUserByEmail.service.ts | 8 +++-- .../user/services/http/getUserById.service.ts | 18 ++++++++++ .../http/getUserByUsername.service.ts | 18 ++++++++++ .../services/internal/findUser.service.ts | 23 ++++++++++++ src/modules/user/user.types.ts | 4 +-- 16 files changed, 149 insertions(+), 48 deletions(-) create mode 100644 src/modules/user/controller/getUserById.controller.ts create mode 100644 src/modules/user/controller/getUserByUsername.controller.ts create mode 100644 src/modules/user/schemas/getUserByEmail.schema.ts create mode 100644 src/modules/user/schemas/getUserByUsername.schema.ts create mode 100644 src/modules/user/services/http/getUserById.service.ts create mode 100644 src/modules/user/services/http/getUserByUsername.service.ts diff --git a/src/modules/user/controller/getUserByEmail.controller.ts b/src/modules/user/controller/getUserByEmail.controller.ts index 19f22bd..9b9caf5 100644 --- a/src/modules/user/controller/getUserByEmail.controller.ts +++ b/src/modules/user/controller/getUserByEmail.controller.ts @@ -1,14 +1,14 @@ import { Context } from "elysia"; -import { getUserByIdSchema } from "../schemas/getUserById.schema"; import { mainErrorHandler } from "../../../helpers/error/handler"; import { getUserByEmailService } from "../services/http/getUserByEmail.service"; import { returnReadResponse } from "../../../helpers/callback/httpResponse"; import { getUserOptionsSchema } from "../schemas/getUserOptions.schema"; import { getUserDataOptions } from "../user.types"; +import { getUserByEmailSchema } from "../schemas/getUserByEmail.schema"; export const getUserByEmailController = async (ctx: Context) => { try { - const params = getUserByIdSchema.parse(ctx.params); + const params = getUserByEmailSchema.parse(ctx.params); const options = getUserOptionsSchema.parse(ctx.query) as getUserDataOptions; const getUserByEmail = await getUserByEmailService(params.email, options); diff --git a/src/modules/user/controller/getUserById.controller.ts b/src/modules/user/controller/getUserById.controller.ts new file mode 100644 index 0000000..766ac21 --- /dev/null +++ b/src/modules/user/controller/getUserById.controller.ts @@ -0,0 +1,19 @@ +import { Context } from "elysia"; +import { getUserByIdSchema } from "../schemas/getUserById.schema"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { returnReadResponse } from "../../../helpers/callback/httpResponse"; +import { getUserOptionsSchema } from "../schemas/getUserOptions.schema"; +import { getUserDataOptions } from "../user.types"; +import { getUserByIdService } from "../services/http/getUserById.service"; + +export const getUserByIdController = async (ctx: Context) => { + try { + const params = getUserByIdSchema.parse(ctx.params); + const options = getUserOptionsSchema.parse(ctx.query) as getUserDataOptions; + const getUserByEmail = await getUserByIdService(params.id, options); + + return returnReadResponse(ctx.set, 200, "User data found", getUserByEmail); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/user/controller/getUserByUsername.controller.ts b/src/modules/user/controller/getUserByUsername.controller.ts new file mode 100644 index 0000000..1a72827 --- /dev/null +++ b/src/modules/user/controller/getUserByUsername.controller.ts @@ -0,0 +1,22 @@ +import { Context } from "elysia"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { returnReadResponse } from "../../../helpers/callback/httpResponse"; +import { getUserOptionsSchema } from "../schemas/getUserOptions.schema"; +import { getUserDataOptions } from "../user.types"; +import { getUserByUsernameSchema } from "../schemas/getUserByUsername.schema"; +import { getUserByUsernameService } from "../services/http/getUserByUsername.service"; + +export const getUserByUsernameController = async (ctx: Context) => { + try { + const params = getUserByUsernameSchema.parse(ctx.params); + const options = getUserOptionsSchema.parse(ctx.query) as getUserDataOptions; + const getUserByEmail = await getUserByUsernameService( + params.username, + options + ); + + return returnReadResponse(ctx.set, 200, "User data found", getUserByEmail); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/user/index.ts b/src/modules/user/index.ts index 56170b6..5e8f23c 100644 --- a/src/modules/user/index.ts +++ b/src/modules/user/index.ts @@ -1,7 +1,11 @@ import Elysia from "elysia"; import { createUserViaRegisterController } from "./controller/createUserViaRegister.controller"; import { getUserByEmailController } from "./controller/getUserByEmail.controller"; +import { getUserByUsernameController } from "./controller/getUserByUsername.controller"; +import { getUserByIdController } from "./controller/getUserById.controller"; export const userModule = new Elysia({ prefix: "/users" }) .post("/", createUserViaRegisterController) - .get("/e/:email", getUserByEmailController); + .get("/e/:email", getUserByEmailController) + .get("/u/:username", getUserByUsernameController) + .get("/id/:id", getUserByIdController); diff --git a/src/modules/user/repositories/read/findUserByEmail.repository.ts b/src/modules/user/repositories/read/findUserByEmail.repository.ts index 684a5ad..e9dd52b 100644 --- a/src/modules/user/repositories/read/findUserByEmail.repository.ts +++ b/src/modules/user/repositories/read/findUserByEmail.repository.ts @@ -1,42 +1,9 @@ import { userModel } from "../../user.model"; -import { getUserDataOptions } from "../../user.types"; -export const findUserByEmailRepository = async ( - email: string, - options: getUserDataOptions -) => { +export const findUserByEmailRepository = async (email: string) => { return await userModel.findUnique({ where: { email, }, - select: { - id: options.verbosity?.includes("full"), - name: ["full", "basic"].some((level) => - options.verbosity?.includes(level) - ), - username: ["full", "basic"].some((level) => - options.verbosity?.includes(level) - ), - email: options.verbosity?.includes("full"), - password: options.verbosity?.includes("full"), - birthDate: options.verbosity?.includes("full"), - gender: options.verbosity?.includes("full"), - phoneCC: options.verbosity?.includes("full"), - phoneNumber: options.verbosity?.includes("full"), - bioProfile: ["full", "basic"].some((level) => - options.verbosity?.includes(level) - ), - avatar: ["full", "basic"].some((level) => - options.verbosity?.includes(level) - ), - commentBackground: ["full", "basic"].some((level) => - options.verbosity?.includes(level) - ), - preferenceId: options.verbosity?.includes("full"), - verifiedAt: options.verbosity?.includes("full"), - disabledAt: options.verbosity?.includes("full"), - createdAt: options.verbosity?.includes("full"), - updatedAt: options.verbosity?.includes("full"), - }, }); }; diff --git a/src/modules/user/repositories/read/findUserById.repository.ts b/src/modules/user/repositories/read/findUserById.repository.ts index e69de29..220acb2 100644 --- a/src/modules/user/repositories/read/findUserById.repository.ts +++ b/src/modules/user/repositories/read/findUserById.repository.ts @@ -0,0 +1,9 @@ +import { userModel } from "../../user.model"; + +export const findUserByIdRepository = async (id: string) => { + return await userModel.findUnique({ + where: { + id, + }, + }); +}; diff --git a/src/modules/user/repositories/read/findUserByUsername.repository.ts b/src/modules/user/repositories/read/findUserByUsername.repository.ts index e69de29..22825b5 100644 --- a/src/modules/user/repositories/read/findUserByUsername.repository.ts +++ b/src/modules/user/repositories/read/findUserByUsername.repository.ts @@ -0,0 +1,9 @@ +import { userModel } from "../../user.model"; + +export const findUserByUsernameRepository = async (username: string) => { + return await userModel.findUnique({ + where: { + username, + }, + }); +}; diff --git a/src/modules/user/schemas/getUserByEmail.schema.ts b/src/modules/user/schemas/getUserByEmail.schema.ts new file mode 100644 index 0000000..1f80668 --- /dev/null +++ b/src/modules/user/schemas/getUserByEmail.schema.ts @@ -0,0 +1,5 @@ +import z from "zod"; + +export const getUserByEmailSchema = z.object({ + email: z.email(), +}); diff --git a/src/modules/user/schemas/getUserById.schema.ts b/src/modules/user/schemas/getUserById.schema.ts index 17a18a6..4992bcc 100644 --- a/src/modules/user/schemas/getUserById.schema.ts +++ b/src/modules/user/schemas/getUserById.schema.ts @@ -1,5 +1,5 @@ import z from "zod"; export const getUserByIdSchema = z.object({ - email: z.email(), + id: z.string(), }); diff --git a/src/modules/user/schemas/getUserByUsername.schema.ts b/src/modules/user/schemas/getUserByUsername.schema.ts new file mode 100644 index 0000000..f0a06ab --- /dev/null +++ b/src/modules/user/schemas/getUserByUsername.schema.ts @@ -0,0 +1,5 @@ +import z from "zod"; + +export const getUserByUsernameSchema = z.object({ + username: z.string().min(4), +}); diff --git a/src/modules/user/schemas/getUserOptions.schema.ts b/src/modules/user/schemas/getUserOptions.schema.ts index 30b460e..490f29c 100644 --- a/src/modules/user/schemas/getUserOptions.schema.ts +++ b/src/modules/user/schemas/getUserOptions.schema.ts @@ -3,12 +3,10 @@ import z from "zod"; const includeOptions = ["preference", "roles"] as const; export const getUserOptionsSchema = z.object({ - verbosity: z - .enum( - ["exists", "basic", "full"], - "option: verbosity value didn't match with enum types" - ) - .optional(), + verbosity: z.enum( + ["exists", "basic", "full"], + "option: verbosity value must match with enum types" + ), include: z .string() .optional() diff --git a/src/modules/user/services/http/getUserByEmail.service.ts b/src/modules/user/services/http/getUserByEmail.service.ts index 634129a..7997d80 100644 --- a/src/modules/user/services/http/getUserByEmail.service.ts +++ b/src/modules/user/services/http/getUserByEmail.service.ts @@ -1,13 +1,17 @@ import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; -import { findUserByEmailRepository } from "../../repositories/read/findUserByEmail.repository"; import { getUserDataOptions } from "../../user.types"; +import { findUserService } from "../internal/findUser.service"; export const getUserByEmailService = async ( email: string, options: getUserDataOptions ) => { try { - return await findUserByEmailRepository(email, options); + return await findUserService({ + identifier: email, + queryTarget: "email", + options, + }); } catch (error) { ErrorForwarder(error); } diff --git a/src/modules/user/services/http/getUserById.service.ts b/src/modules/user/services/http/getUserById.service.ts new file mode 100644 index 0000000..27c15ea --- /dev/null +++ b/src/modules/user/services/http/getUserById.service.ts @@ -0,0 +1,18 @@ +import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; +import { getUserDataOptions } from "../../user.types"; +import { findUserService } from "../internal/findUser.service"; + +export const getUserByIdService = async ( + id: string, + options: getUserDataOptions +) => { + try { + return await findUserService({ + identifier: id, + queryTarget: "id", + options, + }); + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/user/services/http/getUserByUsername.service.ts b/src/modules/user/services/http/getUserByUsername.service.ts new file mode 100644 index 0000000..4f45ef0 --- /dev/null +++ b/src/modules/user/services/http/getUserByUsername.service.ts @@ -0,0 +1,18 @@ +import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; +import { getUserDataOptions } from "../../user.types"; +import { findUserService } from "../internal/findUser.service"; + +export const getUserByUsernameService = async ( + username: string, + options: getUserDataOptions +) => { + try { + return await findUserService({ + identifier: username, + queryTarget: "username", + options, + }); + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/user/services/internal/findUser.service.ts b/src/modules/user/services/internal/findUser.service.ts index e69de29..09995b0 100644 --- a/src/modules/user/services/internal/findUser.service.ts +++ b/src/modules/user/services/internal/findUser.service.ts @@ -0,0 +1,23 @@ +import { User } from "@prisma/client"; +import { findUserByEmailRepository } from "../../repositories/read/findUserByEmail.repository"; +import { getUserDataService } from "../../user.types"; +import { AppError } from "../../../../helpers/error/instances/app"; +import { findUserByIdRepository } from "../../repositories/read/findUserById.repository"; +import { findUserByUsernameRepository } from "../../repositories/read/findUserByUsername.repository"; + +export const findUserService = async (payload: getUserDataService) => { + let userData: User | null = null; + if (payload.queryTarget === "email") { + userData = await findUserByEmailRepository(payload.identifier); + } + if (payload.queryTarget === "id") { + userData = await findUserByIdRepository(payload.identifier); + } + if (payload.queryTarget === "username") { + userData = await findUserByUsernameRepository(payload.identifier); + } + + if (userData === null) throw new AppError(404, "User not found"); + + return userData; +}; diff --git a/src/modules/user/user.types.ts b/src/modules/user/user.types.ts index b0fa55d..f559eea 100644 --- a/src/modules/user/user.types.ts +++ b/src/modules/user/user.types.ts @@ -1,10 +1,10 @@ export interface getUserDataService { identifier: string; queryTarget: "id" | "email" | "username" | "email_username"; - options?: getUserDataOptions; + options: getUserDataOptions; } export interface getUserDataOptions { - verbosity?: "exists" | "basic" | "full"; + verbosity: "exists" | "basic" | "full"; include?: ("preference" | "roles")[]; }